HAWQ-1691. Add universal plan used by ORC format
diff --git a/depends/build-all.sh b/depends/build-all.sh
index 17d96b8..627c777 100755
--- a/depends/build-all.sh
+++ b/depends/build-all.sh
@@ -117,10 +117,13 @@
 }
 echo "Delete headers in ${PREFIX}/include ..."
 rm -rf $PREFIX/include/dbcommon
+rm -rf $PREFIX/include/univplan
 echo "Done."
 
 echo "Delete libs in ${PREFIX}/lib ..."
 rm -rf $PREFIX/lib/libdbcommon*
+rm -rf $PREFIX/lib/libunivplan*
 echo "Done."
 
 build dbcommon
+build univplan
diff --git a/depends/univplan/.gitignore b/depends/univplan/.gitignore
new file mode 100644
index 0000000..a9b913a
--- /dev/null
+++ b/depends/univplan/.gitignore
@@ -0,0 +1,8 @@
+.DS_Store
+.cproject
+.project
+.settings
+.pydevproject
+*.pyc
+build/
+CodeCoverageReport/
diff --git a/depends/univplan/CMake/CMakeTestCompileInt64tType.cc b/depends/univplan/CMake/CMakeTestCompileInt64tType.cc
new file mode 100644
index 0000000..ad2fc8e
--- /dev/null
+++ b/depends/univplan/CMake/CMakeTestCompileInt64tType.cc
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <cstdint>
+
+// test if int64_t is typedef to be long long
+
+void test(long long *i) {}  // NOLINT
+
+int main() {
+  int64_t i = 0;
+  test(&i);
+  return 0;
+}
diff --git a/depends/univplan/CMake/FindCogapp.cmake b/depends/univplan/CMake/FindCogapp.cmake
new file mode 100644
index 0000000..198c23b
--- /dev/null
+++ b/depends/univplan/CMake/FindCogapp.cmake
@@ -0,0 +1,50 @@
+# locate cogapp and generate source code from template
+# 
+# find_package(Cogapp REQUIRED)
+#
+# COGAPP_GENERATE (public function)
+#   GENERATED_CODE = Variable to define with generated source files.
+#   TEMPLATE  = Template used to generate source files.
+#
+# NOTE: The COGAPP_GENERATE macro & add_executable() or add_library()
+#       calls only work properly within the same directory.
+#
+
+find_package(PythonInterp REQUIRED)
+
+function(COGAPP_GENERATE GENERATED_CODE)
+  if(NOT ARGN)
+    message(SEND_ERROR "Error: COGAPP_GENERATE() called without any template files")
+    return()
+  endif()
+
+  set(${GENERATED_CODE})
+  foreach(FIL ${ARGN})
+    get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
+    file(RELATIVE_PATH FIL_REL ${CMAKE_SOURCE_DIR} ${ABS_FIL})
+    
+    get_filename_component(FIL_DIR ${CMAKE_BINARY_DIR}/codegen/${FIL_REL} DIRECTORY)
+    file(MAKE_DIRECTORY ${FIL_DIR})
+    
+    get_filename_component(FIL_WE "${CMAKE_BINARY_DIR}/codegen/${FIL_REL}" NAME_WE)
+    get_filename_component(FIL_EXT "${CMAKE_BINARY_DIR}/codegen/${FIL_REL}" EXT)
+    
+    set(FIL_OUT "${FIL_DIR}/${FIL_WE}.cg${FIL_EXT}")
+    list(APPEND ${GENERATED_CODE} ${FIL_OUT})
+    
+    if(NOT EXISTS ${ABS_FIL})
+        MESSAGE(FATAL_ERROR "file ${ABS_FIL} does not exist")
+    endif()
+
+    add_custom_command(
+      OUTPUT ${FIL_OUT}
+      COMMAND  ${PYTHON_EXECUTABLE}
+      ARGS -m cogapp -d -o ${FIL_OUT} ${ABS_FIL}
+      DEPENDS ${ABS_FIL}
+      COMMENT "Running cog on ${FIL}"
+      VERBATIM )
+  endforeach()
+
+  set_source_files_properties(${${GENERATED_CODE}} PROPERTIES GENERATED TRUE)
+  set(${GENERATED_CODE} ${${GENERATED_CODE}} PARENT_SCOPE)
+endfunction()
\ No newline at end of file
diff --git a/depends/univplan/CMake/FindGFlags.cmake b/depends/univplan/CMake/FindGFlags.cmake
new file mode 100644
index 0000000..f93c571
--- /dev/null
+++ b/depends/univplan/CMake/FindGFlags.cmake
@@ -0,0 +1,48 @@
+# - Try to find GFLAGS
+#
+# The following variables are optionally searched for defaults
+#  GFLAGS_ROOT_DIR:            Base directory where all GFLAGS components are found
+#
+# The following are set after configuration is done:
+#  GFLAGS_FOUND
+#  GFLAGS_INCLUDE_DIRS
+#  GFLAGS_LIBRARIES
+#  GFLAGS_LIBRARYRARY_DIRS
+
+include(FindPackageHandleStandardArgs)
+
+set(GFLAGS_ROOT_DIR "" CACHE PATH "Folder contains Gflags")
+
+# We are testing only a couple of files in the include directories
+if(WIN32)
+    find_path(GFLAGS_INCLUDE_DIR gflags/gflags.h
+        PATHS ${GFLAGS_ROOT_DIR}/src/windows)
+else()
+    find_path(GFLAGS_INCLUDE_DIR gflags/gflags.h
+        PATHS ${GFLAGS_ROOT_DIR})
+endif()
+
+if(MSVC)
+    find_library(GFLAGS_LIBRARY_RELEASE
+        NAMES libgflags
+        PATHS ${GFLAGS_ROOT_DIR}
+        PATH_SUFFIXES Release)
+
+    find_library(GFLAGS_LIBRARY_DEBUG
+        NAMES libgflags-debug
+        PATHS ${GFLAGS_ROOT_DIR}
+        PATH_SUFFIXES Debug)
+
+    set(GFLAGS_LIBRARY optimized ${GFLAGS_LIBRARY_RELEASE} debug ${GFLAGS_LIBRARY_DEBUG})
+else()
+    find_library(GFLAGS_LIBRARY gflags)
+endif()
+
+find_package_handle_standard_args(GFLAGS DEFAULT_MSG
+    GFLAGS_INCLUDE_DIR GFLAGS_LIBRARY)
+
+
+if(GFLAGS_FOUND)
+    set(GFLAGS_INCLUDE_DIRS ${GFLAGS_INCLUDE_DIR})
+    set(GFLAGS_LIBRARIES ${GFLAGS_LIBRARY})
+endif()
diff --git a/depends/univplan/CMake/FindGlog.cmake b/depends/univplan/CMake/FindGlog.cmake
new file mode 100644
index 0000000..d9f0ee0
--- /dev/null
+++ b/depends/univplan/CMake/FindGlog.cmake
@@ -0,0 +1,49 @@
+
+# - Try to find Glog
+#
+# The following variables are optionally searched for defaults
+#  GLOG_ROOT_DIR:            Base directory where all GLOG components are found
+#
+# The following are set after configuration is done: 
+#  GLOG_FOUND
+#  GLOG_INCLUDE_DIRS
+#  GLOG_LIBRARIES
+#  GLOG_LIBRARYRARY_DIRS
+
+include(FindPackageHandleStandardArgs)
+
+set(GLOG_ROOT_DIR "" CACHE PATH "Folder contains Google glog")
+
+if(WIN32)
+    find_path(GLOG_INCLUDE_DIR glog/logging.h
+        PATHS ${GLOG_ROOT_DIR}/src/windows)
+else()
+    find_path(GLOG_INCLUDE_DIR glog/logging.h
+        PATHS ${GLOG_ROOT_DIR})
+endif()
+
+if(MSVC)
+    find_library(GLOG_LIBRARY_RELEASE libglog_static
+        PATHS ${GLOG_ROOT_DIR}
+        PATH_SUFFIXES Release)
+
+    find_library(GLOG_LIBRARY_DEBUG libglog_static
+        PATHS ${GLOG_ROOT_DIR}
+        PATH_SUFFIXES Debug)
+
+    set(GLOG_LIBRARY optimized ${GLOG_LIBRARY_RELEASE} debug ${GLOG_LIBRARY_DEBUG})
+else()
+    find_library(GLOG_LIBRARY glog
+        PATHS ${GLOG_ROOT_DIR}
+        PATH_SUFFIXES
+            lib
+            lib64)
+endif()
+
+find_package_handle_standard_args(GLOG DEFAULT_MSG
+    GLOG_INCLUDE_DIR GLOG_LIBRARY)
+
+if(GLOG_FOUND)
+    set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR})
+    set(GLOG_LIBRARIES ${GLOG_LIBRARY})
+endif()
\ No newline at end of file
diff --git a/depends/univplan/CMake/FindJSON.cmake b/depends/univplan/CMake/FindJSON.cmake
new file mode 100644
index 0000000..a334948
--- /dev/null
+++ b/depends/univplan/CMake/FindJSON.cmake
@@ -0,0 +1,38 @@
+# - Find json
+# Find the native JSON headers and libraries.
+#
+#  JSON_INCLUDE_DIRS   - where to find json/json.h, etc.
+#  JSON_LIBRARIES      - List of libraries when using json.
+#  JSON_FOUND          - True if json found.
+
+#=============================================================================
+# Copyright 2006-2009 Kitware, Inc.
+# Copyright 2012 Rolf Eike Beer <eike@sf-mail.de>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+# (To distribute this file outside of CMake, substitute the full
+#  License text for the above reference.)
+
+# Look for the header file.
+find_path(JSON_INCLUDE_DIR NAMES json/json.h)
+mark_as_advanced(JSON_INCLUDE_DIR)
+
+# Look for the library (sorted from most current/relevant entry to least).
+find_library(JSON_LIBRARY NAMES jsoncpp
+)
+mark_as_advanced(JSON_LIBRARY)
+
+# handle the QUIETLY and REQUIRED arguments and set JSON_FOUND to TRUE if
+# all listed variables are TRUE
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(JSON DEFAULT_MSG JSON_INCLUDE_DIR JSON_LIBRARY)
+
+if(JSON_FOUND)
+  set(JSON_LIBRARIES ${JSON_LIBRARY})
+  set(JSON_INCLUDE_DIRS ${JSON_INCLUDE_DIR})
+endif()
diff --git a/depends/univplan/CMake/FindSnappy.cmake b/depends/univplan/CMake/FindSnappy.cmake
new file mode 100644
index 0000000..623d2d7
--- /dev/null
+++ b/depends/univplan/CMake/FindSnappy.cmake
@@ -0,0 +1,30 @@
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+#  SNAPPY_ROOT_DIR  Set this variable to the root installation of
+#                    Snappy if the module has problems finding
+#                    the proper installation path.
+#
+# Variables defined by this module:
+#
+#  SNAPPY_FOUND              System has Snappy libs/headers
+#  SNAPPY_LIBRARIES          The Snappy libraries
+#  SNAPPY_INCLUDE_DIR        The location of Snappy headers
+
+find_path(SNAPPY_INCLUDE_DIR
+    NAMES snappy.h
+    HINTS ${SNAPPY_ROOT_DIR}/include)
+
+find_library(SNAPPY_LIBRARIES
+    NAMES snappy
+    HINTS ${SNAPPY_ROOT_DIR}/lib)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Snappy DEFAULT_MSG
+    SNAPPY_LIBRARIES
+    SNAPPY_INCLUDE_DIR)
+
+mark_as_advanced(
+    SNAPPY_ROOT_DIR
+    SNAPPY_LIBRARIES
+    SNAPPY_INCLUDE_DIR)
diff --git a/depends/univplan/CMake/Functions.cmake b/depends/univplan/CMake/Functions.cmake
new file mode 100644
index 0000000..a771b60
--- /dev/null
+++ b/depends/univplan/CMake/Functions.cmake
@@ -0,0 +1,46 @@
+FUNCTION(AUTO_SOURCES RETURN_VALUE PATTERN SOURCE_SUBDIRS)
+
+	IF ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
+		SET(PATH ".")
+		IF (${ARGC} EQUAL 4)
+			LIST(GET ARGV 3 PATH)
+		ENDIF ()
+	ENDIF()
+
+	IF ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
+		UNSET(${RETURN_VALUE})
+		FILE(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
+		LIST(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
+
+		FILE(GLOB SUBDIRS RELATIVE ${PATH} ${PATH}/*)
+
+		FOREACH(DIR ${SUBDIRS})
+			IF (IS_DIRECTORY ${PATH}/${DIR})
+				IF (NOT "${DIR}" STREQUAL "CMAKEFILES")
+					FILE(GLOB_RECURSE SUBDIR_FILES "${PATH}/${DIR}/${PATTERN}")
+					LIST(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
+				ENDIF()
+			ENDIF()
+		ENDFOREACH()
+	ELSE ()
+		FILE(GLOB ${RETURN_VALUE} "${PATTERN}")
+
+		FOREACH (PATH ${SOURCE_SUBDIRS})
+			FILE(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
+			LIST(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
+		ENDFOREACH(PATH ${SOURCE_SUBDIRS})
+	ENDIF ()
+
+	IF (${FILTER_OUT})
+		LIST(REMOVE_ITEM ${RETURN_VALUE} ${FILTER_OUT})
+	ENDIF()
+
+	SET(${RETURN_VALUE} ${${RETURN_VALUE}} PARENT_SCOPE)
+ENDFUNCTION(AUTO_SOURCES)
+
+FUNCTION(CONTAINS_STRING FILE SEARCH RETURN_VALUE)
+	FILE(STRINGS ${FILE} FILE_CONTENTS REGEX ".*${SEARCH}.*")
+	IF (FILE_CONTENTS)
+		SET(${RETURN_VALUE} TRUE PARENT_SCOPE)
+	ENDIF()
+ENDFUNCTION(CONTAINS_STRING)
diff --git a/depends/univplan/CMake/Options.cmake b/depends/univplan/CMake/Options.cmake
new file mode 100644
index 0000000..36841ea
--- /dev/null
+++ b/depends/univplan/CMake/Options.cmake
@@ -0,0 +1,71 @@
+##############################################################################
+# In this file we handle all env and customer's settings
+##############################################################################
+
+##############################################################################
+# Setup build and dependencies information 
+##############################################################################
+SET(DEPENDENCY_INSTALL_PREFIX "/opt/dependency")
+IF($ENV{DEPENDENCY_INSTALL_PREFIX})
+	SET(DEPENDENCY_INSTALL_PREFIX $ENV{DEPENDENCY_INSTALL_PREFIX})
+ENDIF()
+
+SET(DEPENDENCY_DIST_PACKAGE_NAME "dependency-dist-package.tar.gz")
+IF($ENV{DEPENDENCY_DIST_PACKAGE_NAME})
+	SET(DEPENDENCY_DIST_PACKAGE_NAME $ENV{DEPENDENCY_DIST_PACKAGE_NAME})
+ENDIF()
+
+SET(CMAKE_PREFIX_PATH "${DEPENDENCY_INSTALL_PREFIX}" ${CMAKE_PREFIX_PATH})
+SET(CMAKE_PREFIX_PATH "${DEPENDENCY_INSTALL_PREFIX}/package" ${CMAKE_PREFIX_PATH})
+SET(CMAKE_PREFIX_PATH "${DEPENDENCY_INSTALL_PREFIX}/tools" ${CMAKE_PREFIX_PATH})
+
+SET(DEPENDENCY_LIBRARY_PATH "${DEPENDENCY_INSTALL_PREFIX}/package/lib:${DEPENDENCY_LIBRARY_PATH}")
+SET(DEPENDENCY_LIBRARY_PATH "${DEPENDENCY_INSTALL_PREFIX}/package/lib64:${DEPENDENCY_LIBRARY_PATH}")
+
+##############################################################################
+# Setup build flags
+##############################################################################
+OPTION(ENABLE_COVERAGE "enable code coverage." OFF)
+
+IF(NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE)
+ENDIF(NOT CMAKE_BUILD_TYPE)
+
+IF(ENABLE_COVERAGE STREQUAL ON)
+    INCLUDE(CodeCoverage)
+ENDIF(ENABLE_COVERAGE STREQUAL ON)
+
+IF(CMAKE_BUILD_TYPE MATCHES Debug)
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")    
+ENDIF()
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fno-omit-frame-pointer -fno-strict-aliasing")
+
+IF(ENABLE_AVX STREQUAL ON)
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx -mno-avx2")
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DAVX_OPT")
+ELSE()
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-avx -mno-avx2")
+ENDIF()
+
+#c++11 is needed to provide thread saft singleton implementation. 
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-deprecated-register")
+#-Rpass-missed=loop-vectorize  -Wall -Wconversion
+
+IF(CMAKE_COMPILER_IS_CLANG)
+    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-limit-debug-info -stdlib=libc++ -DUSE_CLANG")
+    IF(OS_LINUX)
+        SET(CLANG_LDFLAGS "-lc++abi -lc++" ${CLANG_LDFLAGS})
+    ENDIF(OS_LINUX)
+ENDIF(CMAKE_COMPILER_IS_CLANG)
+
+TRY_COMPILE(INT64T_EQUAL_LONGLONG
+    ${CMAKE_BINARY_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/CMake/CMakeTestCompileInt64tType.cc
+    OUTPUT_VARIABLE OUTPUT)
+
+IF(INT64T_EQUAL_LONGLONG)
+    MESSAGE(STATUS "Checking whether int64_t is typedef to long long -- yes")
+ELSE(INT64T_EQUAL_LONGLONG)
+    MESSAGE(STATUS "Checking whether int64_t is typedef to long long -- no")
+ENDIF(INT64T_EQUAL_LONGLONG)
diff --git a/depends/univplan/CMake/Platform.cmake b/depends/univplan/CMake/Platform.cmake
new file mode 100644
index 0000000..1ee0238
--- /dev/null
+++ b/depends/univplan/CMake/Platform.cmake
@@ -0,0 +1,47 @@
+IF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    SET(OS_LINUX true CACHE INTERNAL "Linux operating system")
+ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+    SET(OS_MACOSX true CACHE INTERNAL "Mac Darwin operating system")
+ELSE(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    MESSAGE(FATAL_ERROR "Unsupported OS: \"${CMAKE_SYSTEM_NAME}\"")
+ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+
+IF(CMAKE_COMPILER_IS_GNUCXX)
+    EXECUTE_PROCESS(COMMAND ${CMAKE_CXX_COMPILER} --version  OUTPUT_VARIABLE COMPILER_OUTPUT)
+    
+    STRING(REGEX MATCH "[0-9]\\.[0-9]\\.[0-9]" GCC_COMPILER_VERSION ${COMPILER_OUTPUT})
+    STRING(REGEX MATCHALL "[0-9]" GCC_COMPILER_VERSION ${GCC_COMPILER_VERSION})
+    
+    LIST(LENGTH GCC_COMPILER_VERSION GCC_COMPILER_VERSION_LEN)
+    IF (NOT 3 EQUAL ${GCC_COMPILER_VERSION_LEN})
+        MESSAGE(FATAL_ERROR "Cannot get gcc version from \"${COMPILER_OUTPUT}\"")
+    ENDIF(NOT 3 EQUAL ${GCC_COMPILER_VERSION_LEN})
+    
+    LIST(GET GCC_COMPILER_VERSION 0 GCC_COMPILER_VERSION_MAJOR)
+    LIST(GET GCC_COMPILER_VERSION 1 GCC_COMPILER_VERSION_MINOR)
+    LIST(GET GCC_COMPILER_VERSION 2 GCC_COMPILER_VERSION_PATCH)
+    
+    SET(GCC_COMPILER_VERSION_MAJOR ${GCC_COMPILER_VERSION_MAJOR} CACHE INTERNAL "gcc major version")
+    SET(GCC_COMPILER_VERSION_MINOR ${GCC_COMPILER_VERSION_MINOR} CACHE INTERNAL "gcc minor version")
+    SET(GCC_COMPILER_VERSION_PATCH ${GCC_COMPILER_VERSION_PATCH} CACHE INTERNAL "gcc patch version")
+    
+    MESSAGE(STATUS "checking compiler: GCC (${GCC_COMPILER_VERSION_MAJOR}.${GCC_COMPILER_VERSION_MINOR}.${GCC_COMPILER_VERSION_PATCH})")
+ELSE(CMAKE_COMPILER_IS_GNUCXX)
+    EXECUTE_PROCESS(COMMAND ${CMAKE_CXX_COMPILER} --version  OUTPUT_VARIABLE COMPILER_OUTPUT)
+    IF(COMPILER_OUTPUT MATCHES "clang")
+        SET(CMAKE_COMPILER_IS_CLANG true CACHE INTERNAL "using clang as compiler")
+        MESSAGE(STATUS "checking compiler: CLANG")
+    ELSE(COMPILER_OUTPUT MATCHES "clang")
+        MESSAGE(FATAL_ERROR "Unsupported compiler: \"${CMAKE_CXX_COMPILER}\"")
+    ENDIF(COMPILER_OUTPUT MATCHES "clang")
+ENDIF(CMAKE_COMPILER_IS_GNUCXX)
+
+INCLUDE (TestBigEndian)
+TEST_BIG_ENDIAN(IS_BIG_ENDIAN)
+if(IS_BIG_ENDIAN)
+ message(STATUS "BIG_ENDIAN")
+ ADD_DEFINITIONS(-DIS_BIG_ENDIAN)
+else()
+ message(STATUS "LITTLE_ENDIAN")
+ ADD_DEFINITIONS(-DIS_LITTLE_ENDIAN)
+endif()
diff --git a/depends/univplan/CMakeLists.txt b/depends/univplan/CMakeLists.txt
new file mode 100644
index 0000000..f25ad2c
--- /dev/null
+++ b/depends/univplan/CMakeLists.txt
@@ -0,0 +1,28 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+PROJECT(univplan)
+
+##############################################################################
+# General CMake initialization
+##############################################################################
+SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH})
+SET(CMAKE_VERBOSE_MAKEFILE OFF CACHE STRING "Verbose build." FORCE)
+
+IF(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
+    MESSAGE(FATAL_ERROR "cannot build the project in the source directory! Out-of-source build is enforced!")
+ENDIF()
+
+##############################################################################
+# Import env, customer settings and utilities
+##############################################################################
+INCLUDE(Functions)
+INCLUDE(Platform)
+INCLUDE(Options)
+
+ADD_SUBDIRECTORY(src)
+ADD_SUBDIRECTORY(test)
+
+ADD_CUSTOM_TARGET(coverage
+    COMMAND make resetcoverage
+    COMMAND make -j8 unittest
+    COMMAND make ucoverage
+    COMMENT "Run all unit tests and get coverage...")
diff --git a/depends/univplan/README b/depends/univplan/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/depends/univplan/README
diff --git a/depends/univplan/bootstrap b/depends/univplan/bootstrap
new file mode 100755
index 0000000..36fd66c
--- /dev/null
+++ b/depends/univplan/bootstrap
@@ -0,0 +1,109 @@
+#!/bin/sh
+# 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.
+#
+
+die() {
+    echo "$@" 1>&2 ; exit 1
+}
+
+arg ()
+{
+    echo "$1" | sed "s/^${2-[^=]*=}//"
+}
+
+# Detect directory information.
+source_dir=`cd "\`dirname \"$0\"\`";pwd`
+binary_dir=`pwd`
+
+# Choose the default install prefix.
+default_prefix="/opt/dependency/package"
+
+# Display bootstrap usage
+usage() {
+echo '
+Usage: '"$0"' [<options>]
+Options: [defaults in brackets after descriptions]
+Configuration:
+    --help                          print this message
+    --prefix=PREFIX                 install files in tree rooted at PREFIX
+                                    ['"${default_prefix}"']
+    --enable-coverage               enable code coverage, must be used together with --enable-debug
+    --enable-debug                  enable debug build
+    --enable-avx                    enable avx for vector instruction optimization
+'
+    exit 10
+}
+
+# Parse arguments
+prefix_dir="${default_prefix}"
+build_type="Release"
+enable_coverage="OFF"
+enable_avx="ON"
+while test $# != 0; do
+    case "$1" in
+    --prefix=*) dir=`arg "$1"`
+                prefix_dir="$dir";;
+	--enable-coverage) enable_coverage="ON"
+                       build_type="Debug";;
+    --enable-debug) build_type="Debug";;
+    --enable-avx=*) avx=`arg "$1"`
+                    enable_avx="$avx";;
+    --help) usage ;;
+    *) die "Unknown option: $1" ;;
+    esac
+    shift
+done
+
+if [ ${source_dir} = ${binary_dir} ]; then
+	die "cannot build the project in the source directory! Out-of-source build is enforced!"
+fi
+
+enable_avx_upper=`echo "${enable_avx}" | tr [a-z] [A-Z]`
+if [ ${enable_avx_upper} != "ON" ] && [ ${enable_avx_upper} != "OFF" ]; then
+    die "unknown value for option enable-avx: ${enable_avx}, valid options are: on and off"
+fi
+
+# Check clang compiler
+if [[ x"${CC}" = x"" ]]; then
+    CC=gcc
+fi
+
+if [[ x"${CXX}" = x"" ]]; then
+    CXX=g++
+fi
+
+c_compiler=`which ${CC}`
+cxx_compiler=`which ${CXX}`
+cmake=`which cmake`
+
+if [ ! -x ${c_compiler} ]; then
+    die "cannot found c compiler"
+fi
+
+if [ ! -x ${cxx_compiler} ]; then
+    die "cannot found c++ compiler"
+fi
+
+if [ ! -x ${cmake} ]; then
+    die "cannot found cmake"
+fi
+
+# Configure 
+${cmake} -DCMAKE_BUILD_TYPE=${build_type} -DCMAKE_INSTALL_PREFIX=${prefix_dir} -DCMAKE_C_COMPILER=${c_compiler} -DCMAKE_CXX_COMPILER=${cxx_compiler} -DENABLE_COVERAGE=${enable_coverage} -DENABLE_AVX=${enable_avx_upper} ${source_dir} || die "failed to configure the project"
+
+echo 'bootstrap success. Run "make" to build.'
diff --git a/depends/univplan/src/CMakeLists.txt b/depends/univplan/src/CMakeLists.txt
new file mode 100644
index 0000000..f53e3a7
--- /dev/null
+++ b/depends/univplan/src/CMakeLists.txt
@@ -0,0 +1,77 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+PROJECT(univplan)
+
+FIND_PACKAGE(Protobuf REQUIRED)
+
+SET(univplan_VERSION_MAJOR 0)
+SET(univplan_VERSION_MINOR 1)
+SET(univplan_VERSION_PATCH 0)
+SET(univplan_VERSION_API 1)
+set(CMAKE_MACOSX_RPATH 1)
+
+SET(univplan_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+SET(univplan_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/univplan)
+SET(univplan_proto_DIR ${univplan_SRC_DIR}/proto)
+
+file(GLOB proto_files "${univplan_SRC_DIR}/proto/*.proto")
+set(proto_SRC_DIR ${CMAKE_BINARY_DIR}/src/univplan/proto)
+set(UNIVPLAN_PROTO_HDRS
+       ${proto_SRC_DIR}/universal-plan.pb.h
+       ${proto_SRC_DIR}/universal-plan-catalog.pb.h
+       ${proto_SRC_DIR}/universal-plan-expr.pb.h
+)
+set(UNIVPLAN_PROTO_SRCS
+       ${proto_SRC_DIR}/universal-plan.pb.cc
+       ${proto_SRC_DIR}/universal-plan-catalog.pb.cc
+       ${proto_SRC_DIR}/universal-plan-expr.pb.cc
+)
+file(MAKE_DIRECTORY ${proto_SRC_DIR})
+add_custom_command(
+       OUTPUT ${UNIVPLAN_PROTO_SRCS} ${UNIVPLAN_PROTO_HDRS}
+       COMMAND ${Protobuf_PROTOC_EXECUTABLE}
+       ARGS --cpp_out ${CMAKE_BINARY_DIR}/src -I ${CMAKE_CURRENT_SOURCE_DIR} ${proto_files}
+       DEPENDS "${proto_files}"
+       )
+
+AUTO_SOURCES(univplan_files "*.cc" "RECURSE" "${univplan_SRC_DIR}")
+LIST(APPEND univplan_SOURCES ${univplan_files})
+
+AUTO_SOURCES(univplanbuilder_HEADER "*.h" "${univplan_SRC_DIR}/univplanbuilder")
+AUTO_SOURCES(common_HEADER "*.h" "${univplan_SRC_DIR}/common")
+AUTO_SOURCES(cwrapper_HEADER "*.h" "${univplan_SRC_DIR}/cwrapper")
+AUTO_SOURCES(minmax_HEADER "*.h" "${univplan_SRC_DIR}/minmax")
+AUTO_SOURCES(testutil_HEADER "*.h" "${univplan_SRC_DIR}/testutil")
+
+INCLUDE_DIRECTORIES(${univplan_ROOT_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/src)
+INCLUDE_DIRECTORIES(${DEPENDENCY_INSTALL_PREFIX}/package/include)
+
+LINK_DIRECTORIES(${DEPENDENCY_INSTALL_PREFIX}/package/lib)
+
+
+ADD_LIBRARY(univplan-shared SHARED
+            ${UNIVPLAN_PROTO_HDRS}
+            ${UNIVPLAN_PROTO_SRCS}
+            ${univplan_SOURCES}
+            )
+# ADD_LIBRARY(univplan-static STATIC ${univplan_SOURCES} ${UNIVPLAN_PROTO_SRCS} ${UNIVPLAN_PROTO_HDRS})
+
+SET_TARGET_PROPERTIES(univplan-shared PROPERTIES OUTPUT_NAME "univplan")
+# SET_TARGET_PROPERTIES(univplan-static PROPERTIES OUTPUT_NAME "univplan")
+
+target_link_libraries(univplan-shared ${CLANG_LDFLAGS} dbcommon glog protobuf pthread)
+# target_link_libraries(univplan-static ${CLANG_LDFLAGS} dbcommon glog protobuf pthread)
+
+INSTALL(TARGETS univplan-shared
+     RUNTIME DESTINATION bin
+     LIBRARY DESTINATION lib
+     ARCHIVE DESTINATION lib)
+
+INSTALL(FILES ${univplanbuilder_HEADER} DESTINATION include/univplan/univplanbuilder)
+INSTALL(FILES ${common_HEADER} DESTINATION include/univplan/common)
+INSTALL(FILES ${cwrapper_HEADER} DESTINATION include/univplan/cwrapper)
+INSTALL(FILES ${minmax_HEADER} DESTINATION include/univplan/minmax)
+INSTALL(FILES ${testutil_HEADER} DESTINATION include/univplan/testutil)
+INSTALL(FILES ${UNIVPLAN_PROTO_HDRS} DESTINATION include/univplan/proto)
+
+SET(univplan_ROOT_DIR ${univplan_ROOT_DIR} PARENT_SCOPE)
diff --git a/depends/univplan/src/univplan/common/expression.cc b/depends/univplan/src/univplan/common/expression.cc
new file mode 100644
index 0000000..974bf39
--- /dev/null
+++ b/depends/univplan/src/univplan/common/expression.cc
@@ -0,0 +1,742 @@
+/*
+ * 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.
+ */
+
+#include "univplan/common/expression.h"
+
+#include <algorithm>
+
+#include "dbcommon/common/tuple-batch.h"
+#include "dbcommon/common/vector/fixed-length-vector.h"
+#include "dbcommon/common/vector/list-vector.h"
+#include "dbcommon/common/vector/struct-vector.h"
+#include "dbcommon/common/vector/variable-length-vector.h"
+#include "dbcommon/function/func-kind.cg.h"
+#include "dbcommon/log/logger.h"
+#include "dbcommon/type/array.h"
+#include "dbcommon/type/type-util.h"
+
+namespace univplan {
+
+dbcommon::SelectList *ExprState::convertSelectList(const dbcommon::Datum &d,
+                                                   ExprContext *context) {
+  dbcommon::Object *obj = dbcommon::DatumGetValue<dbcommon::Object *>(d);
+  dbcommon::SelectList *sel = nullptr;
+  dbcommon::BooleanVector *vec = nullptr;
+  dbcommon::Scalar *val = nullptr;
+
+  if ((sel = dynamic_cast<dbcommon::SelectList *>(obj)) == nullptr) {
+    if (convertedSelectlist == nullptr)
+      convertedSelectlist.reset(new dbcommon::SelectList);
+    sel = convertedSelectlist.get();
+    if ((vec = dynamic_cast<dbcommon::BooleanVector *>(obj)) != nullptr) {
+      sel->fromVector(vec);
+    } else if ((val = dynamic_cast<dbcommon::Scalar *>(obj)) != nullptr) {
+      sel->clear();
+      if (val->getBoolValue()) {
+        auto size = context->getNumOfRows();
+        sel->resize(size);
+        for (auto i = 0; i < size; i++) (*sel)[i] = i;
+      }
+    } else {
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "Supposed to be SelectList or BooleanVector or Const");
+    }
+  }
+  return sel;
+}
+
+ListExprState::ListExprState(const univplan::UnivPlanExprPolyList *exprs) {
+  for (int i = 0; i < exprs->size(); ++i) {
+    args.push_back(InitExpr(&exprs->Get(i)));
+  }
+}
+
+dbcommon::Datum ListExprState::calc(ExprContext *context) {
+  dbcommon::SelectList *result = nullptr;
+
+  for (ExprState::uptr &e : args) {
+    dbcommon::Datum d = e->calc(context);
+    dbcommon::SelectList *sel = convertSelectList(d, context);
+
+    if (result) {
+      retval.resize(result->size() + sel->size());
+      auto end =
+          std::set_intersection(result->begin(), result->end(), sel->begin(),
+                                sel->end(), retval.begin());
+      retval.resize(end - retval.begin());
+      result->swap(retval);
+    } else {
+      result = sel;
+    }
+  }
+
+  return dbcommon::CreateDatum(result);
+}
+
+dbcommon::Datum VarExprState::calc(ExprContext *context) {
+  int32_t columnIndex = var->varattno() - 1;
+
+  dbcommon::TupleBatch *batch = nullptr;
+  if (var->varno() == INNER_VAR) {
+    batch = context->innerBatch;
+  } else if (var->varno() == OUTER_VAR) {
+    batch = context->outerBatch;
+  } else {
+    batch = context->scanBatch;
+  }
+
+  if (context->tupleIterValid() && !batch->isScalarTupleBatch()) {
+    batch->getColumn(columnIndex)->readPlainScalar(context->tupleIdx, &scalar);
+    return dbcommon::CreateDatum<dbcommon::Scalar *>(&scalar);
+  }
+
+  dbcommon::Vector *ret;
+
+  if (columnIndex >= 0) {
+    // Case 1. Relation attribute
+    ret = batch->getColumn(columnIndex);
+  } else {
+    // Case 2. System attribute
+    LOG_ERROR(
+        ERRCODE_FEATURE_NOT_SUPPORTED,
+        "System attribute is not supported in univplan::VarExprState::calc");
+  }
+
+  return CreateDatum(ret);
+}
+
+ConstExprState::ConstExprState(const univplan::UnivPlanConst *val)
+    : scalar(dbcommon::Datum()) {
+  retType = univplan::PlanNodeUtil::typeKindMapping(val->type());
+  retTypeMod = val->typemod();
+  if (val->isnull()) {
+    scalar.isnull = true;
+  } else if (univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::TIMESTAMPID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::TIMESTAMPTZID) {
+    scalar.value = dbcommon::CreateDatum(
+        val->value().c_str(), &ts,
+        univplan::PlanNodeUtil::typeKindMapping(val->type()));
+  } else if (univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::DECIMALID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::DECIMALNEWID) {
+    dbcommon::DecimalType t;
+    decimalReg = t.fromString(val->value().c_str());
+    scalar.value = dbcommon::CreateDatum(&decimalReg);
+  } else if (univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+             dbcommon::TypeKind::INTERVALID) {
+    intervalReg = dbcommon::IntervalType::fromString(val->value().c_str());
+    scalar.value = dbcommon::CreateDatum(&intervalReg);
+  } else if (univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::BINARYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::IOBASETYPEID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::STRUCTEXID) {
+    auto srcStr = val->value().c_str();
+    auto srcLen = val->value().size();
+    uint32_t dstLen = 0;
+    binary.resize(srcLen + sizeof(dstLen));
+    for (auto i = 0; i < srcLen;) {
+      if (srcStr[i] != '\\') {
+        binary[dstLen++] = srcStr[i];
+        i += 1;
+      } else if (srcStr[i + 1] == '\\') {
+        binary[dstLen++] = '\\';
+        i += 2;
+      } else {
+        binary[dstLen++] = (srcStr[i + 1] - '0') * 64 +
+                           (srcStr[i + 2] - '0') * 8 + (srcStr[i + 3] - '0');
+        i += 4;
+      }
+    }
+    scalar.value = dbcommon::CreateDatum<char *>(&binary[0]);
+    scalar.length = dstLen;
+  } else if (univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::SMALLINTARRAYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::INTARRAYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::BIGINTARRAYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::FLOATARRAYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::DOUBLEARRAYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::STRINGARRAYID ||
+             univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+                 dbcommon::TypeKind::BPCHARARRAYID) {
+    auto typeEnt = reinterpret_cast<dbcommon::ArrayType *>(
+        dbcommon::TypeUtil::instance()
+            ->getTypeEntryById(
+                univplan::PlanNodeUtil::typeKindMapping(val->type()))
+            ->type.get());
+    array = std::move(typeEnt->getScalarFromString(val->value()));
+    scalar.value = dbcommon::CreateDatum(array.get());
+  } else {
+    scalar.value = dbcommon::CreateDatum(
+        val->value().c_str(),
+        univplan::PlanNodeUtil::typeKindMapping(val->type()));
+  }
+
+  if (univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+          dbcommon::TypeKind::STRINGID ||
+      univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+          dbcommon::TypeKind::VARCHARID ||
+      univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+          dbcommon::TypeKind::CHARID ||
+      univplan::PlanNodeUtil::typeKindMapping(val->type()) ==
+          dbcommon::TypeKind::DECIMALNEWID) {
+    scalar.length = val->value().length();
+  }
+}
+
+dbcommon::Datum ConstExprState::calc(ExprContext *context) {
+  return dbcommon::CreateDatum(&scalar);
+}
+
+OpExprState::OpExprState(const univplan::UnivPlanOpExpr *op)
+    : invoker(univplan::PlanNodeUtil::funcKindMapping(op->funcid())) {
+  const dbcommon::FuncEntry *entry =
+      dbcommon::Func::instance()->getFuncEntryById(
+          univplan::PlanNodeUtil::funcKindMapping(op->funcid()));
+  retType = entry->retType;
+}
+
+OpExprState::OpExprState(dbcommon::FuncKind funcId) : invoker(funcId) {
+  const dbcommon::FuncEntry *entry =
+      dbcommon::Func::instance()->getFuncEntryById(
+          univplan::PlanNodeUtil::funcKindMapping(funcId));
+  retType = entry->retType;
+}
+
+dbcommon::Datum OpExprState::calc(ExprContext *context) {
+  invoker.resetPrarmeter();
+  invoker.addParam(dbcommon::CreateDatum(0));
+
+  assert(args.size() == 2 || args.size() == 1);
+  for (ExprState::uptr &arg : args) {
+    invoker.addParam(arg->calc(context));
+  }
+  if (retval) {
+    retval->clear();
+  } else {
+    bool isRetvalVector = true;
+    if (args.size() == 2) {
+      dbcommon::Object *paraL =
+          dbcommon::DatumGetValue<dbcommon::Object *>(invoker.getParam(1));
+      dbcommon::Vector *vecL = dynamic_cast<dbcommon::Vector *>(paraL);
+      dbcommon::Object *paraR =
+          dbcommon::DatumGetValue<dbcommon::Object *>(invoker.getParam(2));
+      dbcommon::Vector *vecR = dynamic_cast<dbcommon::Vector *>(paraR);
+      isRetvalVector = vecL || vecR;
+    } else if (args.size() == 1) {
+      dbcommon::Object *para =
+          dbcommon::DatumGetValue<dbcommon::Object *>(invoker.getParam(1));
+      isRetvalVector = dynamic_cast<dbcommon::Vector *>(para);
+    }
+
+    if (isRetvalVector) {
+      if (retType == dbcommon::TypeKind::BOOLEANID)
+        retval = std::unique_ptr<dbcommon::Object>(new dbcommon::SelectList);
+      else
+        retval = dbcommon::Vector::BuildVector(retType, true, retTypeMod);
+    } else {
+      retval = std::unique_ptr<dbcommon::Object>(new dbcommon::Scalar);
+    }
+  }
+  invoker.setParam(0, dbcommon::CreateDatum<dbcommon::Object *>(retval.get()));
+
+  return invoker.invoke();
+}
+
+dbcommon::Datum FuncExprState::calc(ExprContext *context) {
+  invoker.resetPrarmeter();
+  invoker.addParam(dbcommon::CreateDatum(0));
+
+  for (ExprState::uptr &arg : args) {
+    invoker.addParam(arg->calc(context));
+  }
+
+  if (retval) {
+    retval->clear();
+  } else {
+    bool isVecFunc = false;
+    for (size_t i = 1; i <= args.size(); i++) {
+      dbcommon::Object *para =
+          dbcommon::DatumGetValue<dbcommon::Object *>(invoker.getParam(i));
+      dbcommon::Vector *vec = dynamic_cast<dbcommon::Vector *>(para);
+      if (vec) {
+        isVecFunc = true;
+        break;
+      }
+    }
+    if (func->funcid() == dbcommon::FuncKind::RANDOMF) isVecFunc = true;
+    if (!isVecFunc) {
+      retval = std::unique_ptr<dbcommon::Object>(new dbcommon::Scalar);
+    } else {
+      dbcommon::TypeKind rettype =
+          univplan::PlanNodeUtil::typeKindMapping(func->rettype());
+      if (rettype == dbcommon::TypeKind::BOOLEANID)
+        retval = std::unique_ptr<dbcommon::Object>(new dbcommon::SelectList);
+      else
+        retval = dbcommon::Vector::BuildVector(rettype, true);
+    }
+  }
+  invoker.setParam(0, dbcommon::CreateDatum<dbcommon::Object *>(retval.get()));
+  if (func->funcid() == dbcommon::FuncKind::RANDOMF) {
+    invoker.addParam(dbcommon::CreateDatum(context->outerBatch != nullptr
+                                               ? context->outerBatch
+                                               : context->scanBatch));
+  }
+
+  return invoker.invoke();
+}
+
+dbcommon::Datum NullTestState::calc(ExprContext *context) {
+  assert(args.size() == 1);
+
+  auto obj =
+      dbcommon::DatumGetValue<dbcommon::Object *>(args[0]->calc(context));
+
+  if (auto scalar = dynamic_cast<dbcommon::Scalar *>(obj)) {
+    scalarResult_.value = dbcommon::CreateDatum<bool>(scalar->isnull == isNull);
+    return dbcommon::CreateDatum(&scalarResult_);
+  } else {
+    auto vec = reinterpret_cast<dbcommon::Vector *>(obj);
+    size_t size = vec->getNumOfRows();
+    result.resize(0);
+
+    for (size_t i = 0; i < size; ++i) {
+      bool null = vec->isNull(i);
+
+      if (null == isNull) {
+        result.push_back(vec->getPlainIndex(i));
+      }
+    }
+  }
+
+  return dbcommon::CreateDatum(&result);
+}
+
+dbcommon::Datum BooleanTestState::calc(ExprContext *context) {
+  assert(args.size() == 1);
+  for (ExprState::uptr &arg : args) {
+    dbcommon::Vector *vec =
+        dbcommon::DatumGetValue<dbcommon::Vector *>(arg->calc(context));
+
+    size_t size = vec->getNumOfRows();
+    result.reset(new dbcommon::SelectList);
+    result->reserve(size);
+
+    const bool *val = reinterpret_cast<const bool *>(vec->getValue());
+    for (size_t i = 0; i < size; ++i) {
+      uint64_t index = vec->getPlainIndex(i);
+      bool null = vec->isNullPlain(index);
+      if (op->type() == univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_TRUE) {
+        if (!null && val[index]) result->push_back(index);
+      } else if (op->type() ==
+                 univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_NOT_TRUE) {
+        if (null || !val[index]) result->push_back(index);
+      } else if (op->type() == univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_FALSE) {
+        if (!null && !val[index]) result->push_back(index);
+      } else if (op->type() ==
+                 univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_NOT_FALSE) {
+        if (null || val[index]) result->push_back(index);
+      } else if (op->type() ==
+                 univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_UNKNOWN) {
+        if (null) result->push_back(index);
+      } else {
+        assert(op->type() ==
+               univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_NOT_UNKNOWN);
+        if (!null) result->push_back(index);
+      }
+    }
+  }
+
+  return dbcommon::CreateDatum(result.get());
+}
+
+dbcommon::Datum BoolExprState::calc(ExprContext *context) {
+  if (op == univplan::BOOLEXPRTYPE::BOOLEXPRTYPE_OR_EXPR) {
+    return calcOr(context);
+  } else if (op == univplan::BOOLEXPRTYPE::BOOLEXPRTYPE_NOT_EXPR) {
+    return calcNot(context);
+  } else {
+    assert(op == univplan::BOOLEXPRTYPE::BOOLEXPRTYPE_AND_EXPR);
+    return calcAnd(context);
+  }
+}
+
+dbcommon::Datum BoolExprState::calcAnd(ExprContext *context) {
+  assert(args.size() >= 2);
+  backupSel_ = context->releaseSelected();
+  dbcommon::SelectList *backupSel = context->getSelectList();
+
+  dbcommon::Object *para0 = args[0]->calc(context);
+  if (dynamic_cast<dbcommon::Scalar *>(para0))
+    LOG_ERROR(ERRCODE_FEATURE_NOT_SUPPORTED, "Feature not supported");
+
+  if (dynamic_cast<dbcommon::SelectList *>(para0)) {
+    if (retval == nullptr)
+      retval =
+          dbcommon::Vector::BuildVector(dbcommon::TypeKind::BOOLEANID, true);
+    reinterpret_cast<dbcommon::SelectList *>(para0)->toVector(
+        reinterpret_cast<dbcommon::Vector *>(retval.get()));
+  } else {
+    retval =
+        reinterpret_cast<dbcommon::Vector *>(para0)->cloneSelected(nullptr);
+  }
+
+  dbcommon::Vector *retVector =
+      reinterpret_cast<dbcommon::Vector *>(retval.get());
+
+  std::unique_ptr<dbcommon::Vector> tmpVectorBackup;
+
+  for (auto i = 1; i < args.size(); i++) {
+    auto &it = args[i];
+    dbcommon::Object *para = it->calc(context);
+
+    auto lhsVector = retVector->cloneSelected(nullptr);
+    dbcommon::Vector *rhsVector = reinterpret_cast<dbcommon::Vector *>(para);
+
+    if (dynamic_cast<dbcommon::Scalar *>(para))
+      LOG_ERROR(ERRCODE_FEATURE_NOT_SUPPORTED, "Feature not supported");
+
+    if (auto sel = dynamic_cast<dbcommon::SelectList *>(para)) {
+      if (tmpVectorBackup == nullptr)
+        tmpVectorBackup =
+            dbcommon::Vector::BuildVector(dbcommon::TypeKind::BOOLEANID, true);
+      rhsVector = tmpVectorBackup.get();
+      sel->toVector(rhsVector);
+    }
+
+    dbcommon::FixedSizeTypeVectorRawData<bool> lhs(lhsVector.get());
+    dbcommon::FixedSizeTypeVectorRawData<bool> rhs(rhsVector);
+    retVector->resize(context->getNumOfRowsPlain(), nullptr, lhs.nulls,
+                      rhs.nulls);
+    dbcommon::FixedSizeTypeVectorRawData<bool> ret(retVector);
+    auto setBoolean = [&](uint64_t plainIdx) {
+      ret.values[plainIdx] = lhs.values[plainIdx] & rhs.values[plainIdx];
+    };
+    dbcommon::transformVector(ret.plainSize, nullptr, nullptr, setBoolean);
+
+    if (ret.nulls) {
+      auto setNullFromLhs = [&](uint64_t plainIdx) {
+        if (lhs.values[plainIdx] == false)
+          const_cast<bool *>(ret.nulls)[plainIdx] = false;
+      };
+      dbcommon::transformVector(ret.plainSize, nullptr, lhs.nulls,
+                                setNullFromLhs);
+
+      auto setNullFromRhs = [&](uint64_t plainIdx) {
+        if (rhs.values[plainIdx] == false)
+          const_cast<bool *>(ret.nulls)[plainIdx] = false;
+      };
+      dbcommon::transformVector(ret.plainSize, nullptr, rhs.nulls,
+                                setNullFromRhs);
+    }
+  }
+
+  context->setSelected(backupSel);
+  auto retsel = convertSelectList(dbcommon::CreateDatum(retVector), context);
+  return dbcommon::CreateDatum(retsel);
+}
+
+dbcommon::Datum BoolExprState::calcOr(ExprContext *context) {
+  assert(args.size() >= 2);
+  backupSel_ = context->releaseSelected();
+  dbcommon::SelectList *backupSel = context->getSelectList();
+  if (retval == nullptr) retval.reset(new dbcommon::SelectList);
+  retval->clear();
+  dbcommon::SelectList *retsel =
+      static_cast<dbcommon::SelectList *>(retval.get());
+  retsel->setPlainSize(0);
+
+  dbcommon::Datum d = args[0]->calc(context);
+  dbcommon::SelectList *sel = convertSelectList(d, context);
+  *retsel = *sel;
+
+  for (auto i = 1; i < args.size(); i++) {
+    auto &it = args[i];
+    dbcommon::Datum d = it->calc(context);
+    dbcommon::SelectList *sel = convertSelectList(d, context);
+
+    dbcommon::SelectList tmp = *retsel;
+
+    retsel->resize(tmp.size() + sel->size());
+    auto end = std::set_union(tmp.begin(), tmp.end(), sel->begin(), sel->end(),
+                              retsel->begin());
+    retsel->resize(end - retsel->begin());
+    retsel->setNulls(context->getNumOfRowsPlain(), nullptr, tmp.getNulls(),
+                     sel->getNulls());
+  }
+  retsel->setNulls(context->getNumOfRowsPlain(), retsel, false);
+
+  context->setSelected(backupSel);
+  return dbcommon::CreateDatum(retval.get());
+}
+
+dbcommon::Datum BoolExprState::calcNot(ExprContext *context) {
+  assert(args.size() == 1);
+  backupSel_ = context->releaseSelected();
+  dbcommon::SelectList *backupSel = context->getSelectList();
+  dbcommon::Datum d = args.front()->calc(context);
+
+  if (auto sel = dynamic_cast<dbcommon::SelectList *>(
+          dbcommon::DatumGetValue<dbcommon::Object *>(d))) {
+    if (retval == nullptr) retval.reset(new dbcommon::SelectList);
+    auto retsel = reinterpret_cast<dbcommon::SelectList *>(retval.get());
+    *retsel = sel->getComplement(context->getNumOfRowsPlain());
+    retsel->setNulls(context->getNumOfRowsPlain(), nullptr, sel->getNulls());
+
+    if (backupSel) {
+      auto end = std::set_intersection(retsel->begin(), retsel->end(),
+                                       backupSel->begin(), backupSel->end(),
+                                       retsel->begin());
+      retsel->resize(end - retsel->begin());
+    }
+  } else if (dbcommon::BooleanVector *vec =
+                 dynamic_cast<dbcommon::BooleanVector *>(
+                     dbcommon::DatumGetValue<dbcommon::Object *>(d))) {
+    if (retval == nullptr) retval.reset(new dbcommon::SelectList);
+    auto retsel = reinterpret_cast<dbcommon::SelectList *>(retval.get());
+    dbcommon::SelectList tmp;
+    tmp.fromVector(vec);
+    *retsel = tmp.getComplement(context->getNumOfRowsPlain());
+    retsel->setNulls(context->getNumOfRowsPlain(), nullptr, tmp.getNulls());
+
+    if (backupSel) {
+      auto end = std::set_intersection(retsel->begin(), retsel->end(),
+                                       backupSel->begin(), backupSel->end(),
+                                       retsel->begin());
+      retsel->resize(end - retsel->begin());
+    }
+  } else {
+    dbcommon::Scalar *scalar = static_cast<dbcommon::Scalar *>(
+        dbcommon::DatumGetValue<dbcommon::Object *>(d));
+    if (scalar->value.value.i8)
+      scalar->value.value.i8 = 0;
+    else
+      scalar->value.value.i8 = 1;
+    retval.reset(new dbcommon::Scalar(*scalar));
+  }
+
+  context->setSelected(backupSel);
+  return dbcommon::CreateDatum(retval.get());
+}
+
+ExprState::uptr InitExpr(const univplan::UnivPlanExprPolyList *exprs) {
+  return ExprState::uptr(new ListExprState(exprs));
+}
+
+ExprState::uptr InitExpr(const univplan::UnivPlanExprPoly *expr) {
+  switch (expr->type()) {
+    case univplan::UNIVPLAN_EXPR_VAR:
+      return ExprState::uptr(new VarExprState(&expr->var()));
+    case univplan::UNIVPLAN_EXPR_CONST:
+      return ExprState::uptr(new ConstExprState(&expr->val()));
+    case univplan::UNIVPLAN_EXPR_TARGETENTRY:
+      return InitExpr(&expr->targetentry().expression());
+    case univplan::UNIVPLAN_EXPR_OPEXPR: {
+      const univplan::UnivPlanOpExpr &op = expr->opexpr();
+      bool predictable =
+          dbcommon::Func::instance()
+              ->getFuncEntryById(static_cast<dbcommon::FuncKind>(op.funcid()))
+              ->predictable;
+      if (!predictable)
+        LOG_ERROR(ERRCODE_INTERNAL_ERROR, "FuncExpr %d is not predictable",
+                  op.funcid());
+      OpExprState::uptr retval(new OpExprState(&op));
+      for (int i = 0; i < op.args_size(); ++i)
+        retval->addArg(InitExpr(&op.args(i)));
+      return std::move(retval);
+    }
+    case univplan::UNIVPLAN_EXPR_FUNCEXPR: {
+      const univplan::UnivPlanFuncExpr &func = expr->funcexpr();
+      bool predictable =
+          dbcommon::Func::instance()
+              ->getFuncEntryById(static_cast<dbcommon::FuncKind>(func.funcid()))
+              ->predictable;
+      if (!predictable)
+        LOG_ERROR(ERRCODE_INTERNAL_ERROR, "FuncExpr %d is not predictable",
+                  func.funcid());
+      FuncExprState::uptr retval(new FuncExprState(&func));
+      for (int i = 0; i < func.args_size(); ++i)
+        retval->addArg(InitExpr(&func.args(i)));
+      return std::move(retval);
+    }
+    case univplan::UNIVPLAN_EXPR_NULLTEST: {
+      const univplan::UnivPlanNullTest &op = expr->nulltest();
+      NullTestState::uptr retval(new NullTestState(&op));
+      retval->addArg(InitExpr(&op.arg()));
+      return std::move(retval);
+    }
+    case univplan::UNIVPLAN_EXPR_BOOLEANTEST: {
+      const univplan::UnivPlanBooleanTest &op = expr->booltest();
+      BooleanTestState::uptr retval(new BooleanTestState(&op));
+      retval->addArg(InitExpr(&op.arg()));
+      return std::move(retval);
+    }
+    case univplan::UNIVPLAN_EXPR_BOOLEXPR: {
+      const univplan::UnivPlanBoolExpr &op = expr->boolexpr();
+      BoolExprState::uptr retval(new BoolExprState(&op));
+      for (int i = 0; i < op.args_size(); ++i)
+        retval->addArg(InitExpr(&op.args(i)));
+      return std::move(retval);
+    }
+    default:
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR, "expr type %d is not predictable",
+                expr->type());
+  }
+  return nullptr;
+}
+
+bool do_opexpr2(int col, const univplan::UnivPlanOpExpr &op,
+                std::vector<std::unique_ptr<IndexExpr> > &allExpr,    // NOLINT
+                std::vector<std::unique_ptr<IndexExpr> > &equalExpr,  // NOLINT
+                int *equal) {
+  std::unique_ptr<IndexExpr> idx(new IndexExpr());
+  const univplan::UnivPlanVar &var = op.args(0).var();
+  int att = var.varattno();
+  int typeId = var.typeid_();
+  if (typeId == dbcommon::TypeKind::DECIMALID ||
+      typeId == dbcommon::TypeKind::BOOLEANID) {
+    return false;
+  }
+  int modify = var.typemod();
+  std::string funcName =
+      dbcommon::Func::instance()
+          ->getFuncEntryById(static_cast<dbcommon::FuncKind>(op.funcid()))
+          ->funcName;
+  if (att - 1 == col &&
+      (!(dbcommon::StringUtil::countReplicates(funcName, "not_equal") == 1))) {
+    const univplan::UnivPlanConst &con = op.args(1).val();
+    idx->colidx = att;
+    idx->typeId = typeId;
+    idx->oper = funcName;
+    idx->constVal = con.value();
+    idx->typeModify = modify;
+    if (!(dbcommon::StringUtil::countReplicates(funcName, "equal") == 1)) {
+      allExpr.push_back(std::move(idx));
+      *equal = 0;
+    } else {
+      equalExpr.push_back(std::move(idx));
+      *equal = 1;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool do_boolexpr2(
+    int pk, const univplan::UnivPlanBoolExpr &expr,
+    std::vector<std::unique_ptr<IndexExpr> > &allExpr,    // NOLINT
+    std::vector<std::unique_ptr<IndexExpr> > &equalExpr,  // NOLINT
+    int *equal) {
+  int args_size = expr.args_size();
+  bool hasExpr = false;
+  for (int i = 0; i < args_size; ++i) {
+    const univplan::UnivPlanExprPoly &op = expr.args(i);
+    switch (op.type()) {
+      case univplan::UNIVPLAN_EXPR_OPEXPR: {
+        const univplan::UnivPlanOpExpr &opex = op.opexpr();
+        bool has = do_opexpr2(pk, opex, allExpr, equalExpr, equal);
+        if (*equal == 1) return true;
+        if (has) hasExpr = true;
+        break;
+      }
+      case univplan::UNIVPLAN_EXPR_BOOLEXPR: {
+        const univplan::UnivPlanBoolExpr &opex = op.boolexpr();
+        bool has = do_boolexpr2(pk, opex, allExpr, equalExpr, equal);
+        if (*equal == 1) return true;
+        if (has) hasExpr = true;
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  return hasExpr;
+}
+
+bool loopContinue(
+    int pkCol, const univplan::UnivPlanExprPolyList *filterExprs,
+    std::vector<std::unique_ptr<IndexExpr> > &allExpr,      // NOLINT
+    std::vector<std::unique_ptr<IndexExpr> > &equalExpr) {  // NOLINT
+  int filterSize = filterExprs->size();
+  assert(filterSize > 0);
+  bool hasExpr = false;
+  for (int i = 0; i < filterSize; ++i) {
+    const univplan::UnivPlanExprPoly &ep = filterExprs->Get(i);
+    switch (ep.type()) {
+      case univplan::UNIVPLAN_EXPR_OPEXPR: {
+        const univplan::UnivPlanOpExpr &op = ep.opexpr();
+        int equal = 0;
+        bool has = do_opexpr2(pkCol, op, allExpr, equalExpr, &equal);
+        if (equal == 1) return true;
+        if (has) hasExpr = true;
+        break;
+      }
+      case univplan::UNIVPLAN_EXPR_BOOLEXPR: {
+        const univplan::UnivPlanBoolExpr &op = ep.boolexpr();
+        if (op.type() == univplan::BOOLEXPRTYPE::BOOLEXPRTYPE_AND_EXPR) {
+          int equal = 0;
+          bool has = do_boolexpr2(pkCol, op, allExpr, equalExpr, &equal);
+          if (equal == 1) return true;
+          if (has) hasExpr = true;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  return hasExpr;
+}
+
+std::vector<std::vector<std::unique_ptr<IndexExpr> > > getIndexExpr2(
+    const std::vector<int> &index,
+    const univplan::UnivPlanExprPolyList *filterExprs) {
+  std::vector<std::vector<std::unique_ptr<IndexExpr> > > indexExprVec;
+  int indexSize = index.size();
+  assert(indexSize > 0);
+  for (int i = 0; i < indexSize; ++i) {
+    int col = index[i];
+    std::vector<std::unique_ptr<IndexExpr> > allExpr;
+    std::vector<std::unique_ptr<IndexExpr> > equalExpr;
+    bool loop = loopContinue(col, filterExprs, allExpr, equalExpr);
+    if (!loop) return indexExprVec;
+    int equalSize = equalExpr.size();
+    int allSize = allExpr.size();
+    if (equalSize > 0) {
+      indexExprVec.push_back(std::move(equalExpr));
+      continue;
+    } else {
+      if (allSize > 0) {
+        indexExprVec.push_back(std::move(allExpr));
+        return indexExprVec;
+      }
+    }
+  }
+  return indexExprVec;
+}
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/common/expression.h b/depends/univplan/src/univplan/common/expression.h
new file mode 100644
index 0000000..1f87c3a
--- /dev/null
+++ b/depends/univplan/src/univplan/common/expression.h
@@ -0,0 +1,350 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_EXPRESSION_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_EXPRESSION_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "dbcommon/function/invoker.h"
+#include "dbcommon/type/decimal.h"
+#include "dbcommon/type/interval.h"
+#include "dbcommon/utils/string-util.h"
+
+#include "univplan/common/plannode-util.h"
+
+namespace dbcommon {
+class TupleBatch;
+}
+
+namespace univplan {
+
+class ExprContext {
+ public:
+  ExprContext()
+      : innerBatch(nullptr), outerBatch(nullptr), scanBatch(nullptr) {}
+  ExprContext(dbcommon::TupleBatch *outerBatch,
+              dbcommon::TupleBatch *innerBatch, dbcommon::TupleBatch *scanBatch)
+      : outerBatch(outerBatch), innerBatch(innerBatch), scanBatch(scanBatch) {
+    assert((scanBatch && !innerBatch && !outerBatch) ||
+           (outerBatch && !innerBatch && !scanBatch) ||
+           (innerBatch && outerBatch && !scanBatch));
+  }
+
+  dbcommon::TupleBatch *innerBatch;
+  dbcommon::TupleBatch *outerBatch;
+  dbcommon::TupleBatch *scanBatch;
+
+  // Iterator of plain index for processing input tuple by tuple rather than TB
+  // by TB
+  uint64_t tupleIdx = -1;
+  bool tupleIterValid() { return tupleIdx != -1; }
+  void setTupleIter(uint64_t tupleIdx) { this->tupleIdx = tupleIdx; }
+  void disableTupleIter() { tupleIdx = -1; }
+
+ public:
+  void setSelected(dbcommon::SelectList *selected) {
+    if (innerBatch) innerBatch->setSelected(selected);
+    if (outerBatch) outerBatch->setSelected(selected);
+    if (scanBatch) scanBatch->setSelected(selected);
+  }
+
+  std::unique_ptr<dbcommon::SelectList> releaseSelected() {
+    if (innerBatch) return innerBatch->releaseSelected();
+    if (outerBatch) return outerBatch->releaseSelected();
+    if (scanBatch) return scanBatch->releaseSelected();
+    return nullptr;
+  }
+
+  size_t getNumOfRows() {
+    if (innerBatch) return innerBatch->getNumOfRows();
+    if (outerBatch) return outerBatch->getNumOfRows();
+    if (scanBatch) return scanBatch->getNumOfRows();
+
+    // tupleCount=1 special for constant select
+    return 1;
+  }
+
+  size_t getNumOfRowsPlain() {
+    if (innerBatch) return innerBatch->getNumOfRowsPlain();
+    if (outerBatch) return outerBatch->getNumOfRowsPlain();
+    if (scanBatch) return scanBatch->getNumOfRowsPlain();
+
+    // tupleCount=1 special for constant select
+    return 1;
+  }
+
+  dbcommon::SelectList *getSelectList() {
+    if (innerBatch && innerBatch->getSelected())
+      return innerBatch->getSelected();
+    if (outerBatch && outerBatch->getSelected())
+      return outerBatch->getSelected();
+    if (scanBatch && scanBatch->getSelected()) return scanBatch->getSelected();
+    return nullptr;
+  }
+};
+
+class ExprState {
+ public:
+  ExprState() {}
+  virtual ~ExprState() {}
+
+  typedef std::unique_ptr<ExprState> uptr;
+
+  virtual dbcommon::Datum calc(ExprContext *context) = 0;
+
+  dbcommon::SelectList *convertSelectList(const dbcommon::Datum &d,
+                                          ExprContext *context);
+
+  dbcommon::TypeKind getRetType() { return retType; }
+
+  int64_t getRetTypeModifier() { return retTypeMod; }
+
+  void addArg(ExprState::uptr arg) { args.push_back(std::move(arg)); }
+
+  ExprState *getArg(size_t idx) { return args[idx].get(); }
+
+ protected:
+  dbcommon::TypeKind retType = dbcommon::TypeKind::UNKNOWNID;
+  int64_t retTypeMod = -1;
+  std::vector<ExprState::uptr> args;
+
+  // When relying on current ExprContext's SelectList content, ExprState should
+  // backup the SelectList.
+  std::unique_ptr<dbcommon::SelectList> backupSel_;
+
+ private:
+  std::unique_ptr<dbcommon::SelectList> convertedSelectlist;
+};
+
+class ListExprState : public ExprState {
+ public:
+  explicit ListExprState(const univplan::UnivPlanExprPolyList *exprs);
+  ~ListExprState() {}
+
+  typedef std::unique_ptr<ListExprState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+ private:
+  dbcommon::SelectList retval;
+};
+
+class VarExprState : public ExprState {
+ public:
+  explicit VarExprState(const univplan::UnivPlanVar *var) : var(var) {
+    retType = univplan::PlanNodeUtil::typeKindMapping(var->typeid_());
+    retTypeMod = var->typemod();
+  }
+  ~VarExprState() {}
+
+  typedef std::unique_ptr<VarExprState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+  int32_t getAttributeNumber() const { return var->varattno(); }
+
+  uint32_t getVarNo() const { return var->varno(); }
+
+ private:
+  const univplan::UnivPlanVar *var;
+  // iterator for processing input tuple by tuple
+  dbcommon::Scalar scalar;
+};
+
+class ConstExprState : public ExprState {
+ public:
+  explicit ConstExprState(const univplan::UnivPlanConst *val);
+  ~ConstExprState() {}
+
+  typedef std::unique_ptr<ConstExprState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+ private:
+  dbcommon::Scalar scalar;
+  dbcommon::Timestamp ts;
+  dbcommon::DecimalVar decimalReg;
+  dbcommon::IntervalVar intervalReg;
+  std::vector<char> binary;
+  dbcommon::Vector::uptr array;
+};
+
+class OpExprState : public ExprState {
+ public:
+  explicit OpExprState(const univplan::UnivPlanOpExpr *op);
+  explicit OpExprState(dbcommon::FuncKind funcId);
+  ~OpExprState() {}
+
+  typedef std::unique_ptr<OpExprState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+ private:
+  dbcommon::Invoker invoker;
+  std::unique_ptr<dbcommon::Object> retval;
+};
+
+class FuncExprState : public ExprState {
+ public:
+  explicit FuncExprState(const univplan::UnivPlanFuncExpr *func)
+      : func(func),
+        invoker(univplan::PlanNodeUtil::funcKindMapping(func->funcid())) {}
+  ~FuncExprState() {}
+
+  typedef std::unique_ptr<FuncExprState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+ private:
+  const univplan::UnivPlanFuncExpr *func;
+  dbcommon::Invoker invoker;
+  std::unique_ptr<dbcommon::Object> retval;
+};
+
+class NullTestState : public ExprState {
+ public:
+  explicit NullTestState(const univplan::UnivPlanNullTest *op)
+      : op(op),
+        isNull(op->type() == univplan::NULLTESTTYPE::NULLTESTTYPE_IS_NULL) {}
+
+  explicit NullTestState(bool isNull) : op(nullptr), isNull(isNull) {}
+
+  ~NullTestState() {}
+
+  typedef std::unique_ptr<NullTestState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+ private:
+  const univplan::UnivPlanNullTest *op;
+  bool isNull;
+  dbcommon::SelectList result;
+  dbcommon::Scalar scalarResult_;
+};
+
+class BooleanTestState : public ExprState {
+ public:
+  explicit BooleanTestState(const univplan::UnivPlanBooleanTest *op) : op(op) {}
+  ~BooleanTestState() {}
+
+  typedef std::unique_ptr<BooleanTestState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+
+ private:
+  const univplan::UnivPlanBooleanTest *op;
+  std::unique_ptr<dbcommon::SelectList> result;
+};
+
+class BoolExprState : public ExprState {
+ public:
+  explicit BoolExprState(const univplan::UnivPlanBoolExpr *expr)
+      : op(expr->type()) {}
+  ~BoolExprState() {}
+
+  typedef std::unique_ptr<BoolExprState> uptr;
+
+  dbcommon::Datum calc(ExprContext *context) override;
+  dbcommon::Datum calcAnd(ExprContext *context);
+  dbcommon::Datum calcOr(ExprContext *context);
+  dbcommon::Datum calcNot(ExprContext *context);
+
+ private:
+  const univplan::BOOLEXPRTYPE op;
+  std::unique_ptr<dbcommon::Object> retval;
+};
+
+ExprState::uptr InitExpr(const univplan::UnivPlanExprPoly *expr);
+ExprState::uptr InitExpr(const univplan::UnivPlanExprPolyList *exprs);
+
+/* those interface only for magma index */
+struct IndexExpr {
+  IndexExpr() : colidx(-1), typeId(-1), typeModify(-1) {}
+
+  IndexExpr(const IndexExpr &idx) {
+    colidx = idx.colidx;
+    typeId = idx.typeId;
+    oper = idx.oper;
+    constVal = idx.constVal;
+    typeModify = idx.typeModify;
+  }
+
+  IndexExpr &operator=(const IndexExpr &idx) {
+    colidx = idx.colidx;
+    typeId = idx.typeId;
+    oper = idx.oper;
+    constVal = idx.constVal;
+    typeModify = idx.typeModify;
+    return *this;
+  }
+
+  bool isEqual() {
+    return dbcommon::StringUtil::countReplicates(oper, "equal");
+  }
+
+  bool isGreater() {
+    return dbcommon::StringUtil::countReplicates(oper, "greater");
+  }
+
+  bool isLessthan() {
+    return dbcommon::StringUtil::countReplicates(oper, "less");
+  }
+
+  ~IndexExpr() {}
+  int colidx;
+  int typeId;
+  int typeModify;
+  std::string oper;
+  std::string constVal;
+};
+
+bool do_opexpr(const univplan::UnivPlanOpExpr &op, IndexExpr *index, int pk,
+               int *equal);
+
+bool do_boolexpr(const univplan::UnivPlanBoolExpr &expr, IndexExpr *index,
+                 int pk, int *equal);
+
+std::vector<std::unique_ptr<IndexExpr> > getIndexExpr(
+    const std::vector<int> &index,
+    const univplan::UnivPlanExprPolyList *filterExprs);
+
+bool do_opexpr2(int pk, const univplan::UnivPlanOpExpr &op,
+                std::vector<std::unique_ptr<IndexExpr> > &allExpr,    // NOLINT
+                std::vector<std::unique_ptr<IndexExpr> > &equalExpr,  // NOLINT
+                int *equal);
+bool do_boolexpr2(
+    int pk, const univplan::UnivPlanBoolExpr &expr,
+    std::vector<std::unique_ptr<IndexExpr> > &allExpr,    // NOLINT
+    std::vector<std::unique_ptr<IndexExpr> > &equalExpr,  // NOLINT
+    int *equal);
+bool loopContinue(
+    int pkCol, const univplan::UnivPlanExprPolyList *filterExprs,
+    std::vector<std::unique_ptr<IndexExpr> > &allExpr,     // NOLINT
+    std::vector<std::unique_ptr<IndexExpr> > &equalExpr);  // NOLINT
+
+std::vector<std::vector<std::unique_ptr<IndexExpr> > > getIndexExpr2(
+    const std::vector<int> &index,
+    const univplan::UnivPlanExprPolyList *filterExprs);
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_EXPRESSION_H_
diff --git a/depends/univplan/src/univplan/common/plannode-util.h b/depends/univplan/src/univplan/common/plannode-util.h
new file mode 100644
index 0000000..33079bb
--- /dev/null
+++ b/depends/univplan/src/univplan/common/plannode-util.h
@@ -0,0 +1,336 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_PLANNODE_UTIL_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_PLANNODE_UTIL_H_
+
+#include <algorithm>
+#include <utility>
+
+#include "dbcommon/log/logger.h"
+#include "dbcommon/type/type-kind.h"
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-agg.h"
+#include "univplan/univplanbuilder/univplanbuilder-append.h"
+#include "univplan/univplanbuilder/univplanbuilder-connector.h"
+#include "univplan/univplanbuilder/univplanbuilder-ext-gs-scan.h"
+#include "univplan/univplanbuilder/univplanbuilder-hash.h"
+#include "univplan/univplanbuilder/univplanbuilder-hashjoin.h"
+#include "univplan/univplanbuilder/univplanbuilder-insert.h"
+#include "univplan/univplanbuilder/univplanbuilder-limit.h"
+#include "univplan/univplanbuilder/univplanbuilder-material.h"
+#include "univplan/univplanbuilder/univplanbuilder-mergejoin.h"
+#include "univplan/univplanbuilder/univplanbuilder-nestloop.h"
+#include "univplan/univplanbuilder/univplanbuilder-result.h"
+#include "univplan/univplanbuilder/univplanbuilder-scan-seq.h"
+#include "univplan/univplanbuilder/univplanbuilder-shareinput-scan.h"
+#include "univplan/univplanbuilder/univplanbuilder-sink.h"
+#include "univplan/univplanbuilder/univplanbuilder-sort.h"
+#include "univplan/univplanbuilder/univplanbuilder-subquery-scan.h"
+#include "univplan/univplanbuilder/univplanbuilder-unique.h"
+
+namespace dbcommon {
+enum FuncKind : uint32_t;
+};
+
+namespace univplan {
+
+class PlanNodeUtil {
+ public:
+  PlanNodeUtil() {}
+  ~PlanNodeUtil() {}
+
+  static UnivPlanBuilderNode::uptr createPlanBuilderNode(
+      UNIVPLANNODETYPE ntype) {
+    UnivPlanBuilderNode::uptr res;
+    switch (ntype) {
+      case UNIVPLAN_AGG:
+        res.reset(new UnivPlanBuilderAgg());
+        break;
+      case UNIVPLAN_SCAN_SEQ:
+        res.reset(new UnivPlanBuilderScanSeq());
+        break;
+      case UNIVPLAN_CONNECTOR:
+        res.reset(new UnivPlanBuilderConnector());
+        break;
+      case UNIVPLAN_SORT:
+        res.reset(new UnivPlanBuilderSort());
+        break;
+      case UNIVPLAN_EXT_GS_SCAN:
+        res.reset(new UnivPlanBuilderExtGSScan());
+        break;
+      case UNIVPLAN_LIMIT:
+        res.reset(new UnivPlanBuilderLimit());
+        break;
+      case UNIVPLAN_APPEND:
+        res.reset(new UnivPlanBuilderAppend());
+        break;
+      case UNIVPLAN_NESTLOOP:
+        res.reset(new UnivPlanBuilderNestLoop());
+        break;
+      case UNIVPLAN_HASHJOIN:
+        res.reset(new UnivPlanBuilderHashJoin());
+        break;
+      case UNIVPLAN_MERGEJOIN:
+        res.reset(new UnivPlanBuilderMergeJoin());
+        break;
+      case UNIVPLAN_MATERIAL:
+        res.reset(new UnivPlanBuilderMaterial());
+        break;
+      case UNIVPLAN_RESULT:
+        res.reset(new UnivPlanBuilderResult());
+        break;
+      case UNIVPLAN_HASH:
+        res.reset(new UnivPlanBuilderHash());
+        break;
+      case UNIVPLAN_SUBQUERYSCAN:
+        res.reset(new UnivPlanBuilderSubqueryScan());
+        break;
+      case UNIVPLAN_UNIQUE:
+        res.reset(new UnivPlanBuilderUnique());
+        break;
+      case UNIVPLAN_INSERT:
+        res.reset(new UnivPlanBuilderInsert());
+        break;
+      case UNIVPLAN_SHAREINPUTSCAN:
+        res.reset(new UnivPlanBuilderShareInputScan());
+        break;
+      default:
+        LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                  "PlanNodeUtil::createPlanBuilderNode can't handle "
+                  "UNIVPLANNODETYPE %d",
+                  ntype);
+    }
+    return std::move(res);
+  }
+
+  static const UnivPlanPlanNode &getPlanNode(const UnivPlanPlanNodePoly &node) {
+    if (node.has_connector()) {
+      return node.connector().super();
+    } else if (node.has_scanseq()) {
+      return node.scanseq().super();
+    } else if (node.has_converge()) {
+      return node.converge().super();
+    } else if (node.has_shuffle()) {
+      return node.shuffle().super();
+    } else if (node.has_broadcast()) {
+      return node.broadcast().super();
+    } else if (node.has_sink()) {
+      return node.sink().super();
+    } else if (node.has_agg()) {
+      return node.agg().super();
+    } else if (node.has_sort()) {
+      return node.sort().super();
+    } else if (node.has_extgsscan()) {
+      return node.extgsscan().super();
+    } else if (node.has_extgsfilter()) {
+      return node.extgsfilter().super();
+    } else if (node.has_extgsproject()) {
+      return node.extgsproject().super();
+    } else if (node.has_limit()) {
+      return node.limit().super();
+    } else if (node.has_append()) {
+      return node.append().super();
+    } else if (node.has_nestloop()) {
+      return node.nestloop().super();
+    } else if (node.has_hashjoin()) {
+      return node.hashjoin().super();
+    } else if (node.has_mergejoin()) {
+      return node.mergejoin().super();
+    } else if (node.has_material()) {
+      return node.material().super();
+    } else if (node.has_result()) {
+      return node.result().super();
+    } else if (node.has_hash()) {
+      return node.hash().super();
+    } else if (node.has_subqueryscan()) {
+      return node.subqueryscan().super();
+    } else if (node.has_unique()) {
+      return node.unique().super();
+    } else if (node.has_insert()) {
+      return node.insert().super();
+    } else if (node.has_shareinputscan()) {
+      return node.shareinputscan().super();
+    } else {
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "PlanNodeUtil::getPlanNode type %d not supported", node.type());
+    }
+  }
+
+  static UnivPlanPlanNode *getMutablePlanNode(UnivPlanPlanNodePoly *node) {
+    if (node->has_connector()) {
+      return node->mutable_connector()->mutable_super();
+    } else if (node->has_scanseq()) {
+      return node->mutable_scanseq()->mutable_super();
+    } else if (node->has_converge()) {
+      return node->mutable_converge()->mutable_super();
+    } else if (node->has_shuffle()) {
+      return node->mutable_shuffle()->mutable_super();
+    } else if (node->has_broadcast()) {
+      return node->mutable_broadcast()->mutable_super();
+    } else if (node->has_sink()) {
+      return node->mutable_sink()->mutable_super();
+    } else if (node->has_agg()) {
+      return node->mutable_agg()->mutable_super();
+    } else if (node->has_sort()) {
+      return node->mutable_sort()->mutable_super();
+    } else if (node->has_limit()) {
+      return node->mutable_limit()->mutable_super();
+    } else if (node->has_append()) {
+      return node->mutable_append()->mutable_super();
+    } else if (node->has_nestloop()) {
+      return node->mutable_nestloop()->mutable_super();
+    } else if (node->has_hashjoin()) {
+      return node->mutable_hashjoin()->mutable_super();
+    } else if (node->has_mergejoin()) {
+      return node->mutable_mergejoin()->mutable_super();
+    } else if (node->has_material()) {
+      return node->mutable_material()->mutable_super();
+    } else if (node->has_result()) {
+      return node->mutable_result()->mutable_super();
+    } else if (node->has_hash()) {
+      return node->mutable_hash()->mutable_super();
+    } else if (node->has_subqueryscan()) {
+      return node->mutable_subqueryscan()->mutable_super();
+    } else if (node->has_extgsscan()) {
+      return node->mutable_extgsscan()->mutable_super();
+    } else if (node->has_extgsfilter()) {
+      return node->mutable_extgsfilter()->mutable_super();
+    } else if (node->has_extgsproject()) {
+      return node->mutable_extgsproject()->mutable_super();
+    } else if (node->has_unique()) {
+      return node->mutable_unique()->mutable_super();
+    } else if (node->has_insert()) {
+      return node->mutable_insert()->mutable_super();
+    } else if (node->has_shareinputscan()) {
+      return node->mutable_shareinputscan()->mutable_super();
+    } else {
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "PlanNodeUtil::getPlanNode type %d not supported",
+                node->type());
+    }
+  }
+
+  static const UnivPlanPlan *getPlan(const UnivPlanPlan &cur, int32_t stageNo) {
+    if (cur.stageno() == stageNo) return &cur;
+    for (int i = 0; i < cur.childstages_size(); i++) {
+      const UnivPlanPlan *ret = getPlan(cur.childstages(i), stageNo);
+      if (ret != nullptr) return ret;
+    }
+    return nullptr;
+  }
+
+  static dbcommon::TypeKind exprType(const UnivPlanExprPoly &expr) {
+    if (expr.has_opexpr()) {
+      return typeKindMapping(expr.opexpr().rettype());
+    } else if (expr.has_val()) {
+      return typeKindMapping(expr.val().type());
+    } else if (expr.has_targetentry()) {
+      return exprType(expr.targetentry().expression());
+    } else if (expr.has_aggref()) {
+      return typeKindMapping(expr.aggref().rettype());
+    } else if (expr.has_var()) {
+      return typeKindMapping(expr.var().typeid_());
+    } else if (expr.has_funcexpr()) {
+      return typeKindMapping(expr.funcexpr().rettype());
+    } else if (expr.has_nulltest() || expr.has_boolexpr() ||
+               expr.has_booltest()) {
+      return dbcommon::TypeKind::BOOLEANID;
+    } else if (expr.has_caseexpr()) {
+      return typeKindMapping(expr.caseexpr().casetype());
+    } else if (expr.has_param()) {
+      return typeKindMapping(expr.param().typeid_());
+    } else if (expr.has_subplan()) {
+      if (expr.subplan().sublinktype() == univplan::SUBLINKTYPE::EXPR_SUBLINK)
+        return typeKindMapping(expr.subplan().typeid_());
+      else
+        return dbcommon::TypeKind::BOOLEANID;
+    } else if (expr.has_coalesceexpr()) {
+      return typeKindMapping(expr.coalesceexpr().coalescetype());
+    } else if (expr.has_nullifexpr()) {
+      return typeKindMapping(expr.nullifexpr().rettype());
+    } else if (expr.has_distinctexpr()) {
+      return typeKindMapping(expr.distinctexpr().rettype());
+    } else {
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "PlanNodeUtil::exprType expr %d not supported", expr.type());
+    }
+  }
+
+  static int64_t exprTypeMod(const UnivPlanExprPoly &expr) {
+    if (expr.has_val()) {
+      return (expr.val().typemod());
+    } else if (expr.has_var()) {
+      return (expr.var().typemod());
+    } else if (expr.has_targetentry()) {
+      return exprType(expr.targetentry().expression());
+    } else if (expr.has_param()) {
+      return (expr.param().typemod());
+    } else if (expr.has_subplan()) {
+      return (expr.subplan().typemod());
+    } else if (expr.has_coalesceexpr()) {
+      return expr.coalesceexpr().coalescetypemod();
+    } else if (expr.has_nullifexpr()) {
+      return expr.nullifexpr().typemod();
+    }
+
+    int64_t ret = -1;
+    if (expr.has_opexpr()) {
+      for (auto arg : expr.opexpr().args())
+        ret = std::max(exprTypeMod(arg), ret);
+      return ret;
+    } else if (expr.has_aggref()) {
+      for (auto arg : expr.aggref().args())
+        ret = std::max(exprTypeMod(arg), ret);
+      return ret;
+    } else if (expr.has_funcexpr()) {
+      for (auto arg : expr.funcexpr().args())
+        ret = std::max(exprTypeMod(arg), ret);
+      return ret;
+    } else if (expr.has_distinctexpr()) {
+      for (auto arg : expr.funcexpr().args())
+        ret = std::max(exprTypeMod(arg), ret);
+      return ret;
+    }
+
+    if (expr.has_nulltest() || expr.has_boolexpr() || expr.has_booltest() ||
+        expr.has_caseexpr()) {
+      return -1;
+    } else {
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "PlanNodeUtil::exprTypeMod expr %d not supported", expr.type());
+    }
+  }
+
+  static dbcommon::TypeKind typeKindMapping(int32_t type) {
+    dbcommon::TypeKind typeKind = static_cast<dbcommon::TypeKind>(type);
+    if (typeKind == dbcommon::TypeKind::DECIMALID)
+      typeKind = dbcommon::TypeKind::DECIMALNEWID;
+    return typeKind;
+  }
+
+  static dbcommon::FuncKind funcKindMapping(int32_t funcType) {
+    return static_cast<dbcommon::FuncKind>(funcType);
+  }
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_PLANNODE_UTIL_H_
diff --git a/depends/univplan/src/univplan/common/plannode-walker.h b/depends/univplan/src/univplan/common/plannode-walker.h
new file mode 100644
index 0000000..46f4551
--- /dev/null
+++ b/depends/univplan/src/univplan/common/plannode-walker.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_PLANNODE_WALKER_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_PLANNODE_WALKER_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+
+#include "dbcommon/log/logger.h"
+
+namespace univplan {
+
+class PlanNodeWalkerContext {
+ public:
+  PlanNodeWalkerContext() {}
+  virtual ~PlanNodeWalkerContext() {}
+};
+
+class PlanNodeWalker {
+ public:
+  PlanNodeWalker() {}
+  virtual ~PlanNodeWalker() {}
+
+  virtual void walk(UnivPlanExprPoly *expr, PlanNodeWalkerContext *ctx) = 0;
+
+  // Walk all dependent expressions.
+  // @param expr Root expr
+  // @param walker Apply walker->walk() for checking each expression
+  // @param ctx Context for checking list
+  static void walkExpressionTree(UnivPlanExprPoly *expr, PlanNodeWalker *walker,
+                                 PlanNodeWalkerContext *ctx) {
+    // Call walkExpressionTree to check all containing "UnivPlanExprPoly".
+    // Pay attention to check the "optional UnivPlanExprPoly" exist before
+    // calling walkExpressionTree.
+    if (expr->has_var() || expr->has_val() || expr->has_param()) {
+      // do nothing
+    } else if (expr->has_aggref()) {
+      UnivPlanAggref *aggref = expr->mutable_aggref();
+      walkExpressionTree(aggref->mutable_args(), walker, ctx);
+    } else if (expr->has_funcexpr()) {
+      UnivPlanFuncExpr *funcExpr = expr->mutable_funcexpr();
+      walkExpressionTree(funcExpr->mutable_args(), walker, ctx);
+    } else if (expr->has_opexpr()) {
+      UnivPlanOpExpr *opExpr = expr->mutable_opexpr();
+      walkExpressionTree(opExpr->mutable_args(), walker, ctx);
+    } else if (expr->has_boolexpr()) {
+      UnivPlanBoolExpr *boolExpr = expr->mutable_boolexpr();
+      walkExpressionTree(boolExpr->mutable_args(), walker, ctx);
+    } else if (expr->has_nulltest()) {
+      UnivPlanNullTest *nullTest = expr->mutable_nulltest();
+      walkExpressionTree(nullTest->mutable_arg(), walker, ctx);
+    } else if (expr->has_booltest()) {
+      UnivPlanBooleanTest *boolTest = expr->mutable_booltest();
+      walkExpressionTree(boolTest->mutable_arg(), walker, ctx);
+    } else if (expr->has_targetentry()) {
+      UnivPlanTargetEntry *targetEntry = expr->mutable_targetentry();
+      walkExpressionTree(targetEntry->mutable_expression(), walker, ctx);
+    } else if (expr->has_caseexpr()) {
+      UnivPlanCaseExpr *caseexpr = expr->mutable_caseexpr();
+      walkExpressionTree(caseexpr->mutable_args(), walker, ctx);
+      walkExpressionTree(caseexpr->mutable_defresult(), walker, ctx);
+    } else if (expr->has_casewhen()) {
+      UnivPlanCaseWhen *casewhen = expr->mutable_casewhen();
+      walkExpressionTree(casewhen->mutable_expr(), walker, ctx);
+      walkExpressionTree(casewhen->mutable_result(), walker, ctx);
+    } else if (expr->has_subplan()) {
+      UnivPlanSubPlan *subplan = expr->mutable_subplan();
+      walkExpressionTree(subplan->mutable_args(), walker, ctx);
+      if (subplan->has_testexpr())
+        walkExpressionTree(subplan->mutable_testexpr(), walker, ctx);
+    } else if (expr->has_scalararrayopexpr()) {
+      UnivPlanScalarArrayOpExpr *scalarArrayOpExpr =
+          expr->mutable_scalararrayopexpr();
+      walkExpressionTree(scalarArrayOpExpr->mutable_args(), walker, ctx);
+    } else if (expr->has_coalesceexpr()) {
+      UnivPlanCoalesceExpr *coalesceExpr = expr->mutable_coalesceexpr();
+      walkExpressionTree(coalesceExpr->mutable_args(), walker, ctx);
+    } else if (expr->has_nullifexpr()) {
+      UnivPlanNullIfExpr *nullIfExpr = expr->mutable_nullifexpr();
+      walkExpressionTree(nullIfExpr->mutable_args(), walker, ctx);
+    } else if (expr->has_distinctexpr()) {
+      UnivPlanDistinctExpr *distinctExpr = expr->mutable_distinctexpr();
+      walkExpressionTree(distinctExpr->mutable_args(), walker, ctx);
+    } else {
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "PlanNodeUtil::expressionTreeWalker expr %d not supported",
+                expr->type());
+    }
+    walker->walk(expr, ctx);
+  }
+
+  static void walkExpressionTree(UnivPlanExprPolyList *exprs,
+                                 PlanNodeWalker *walker,
+                                 PlanNodeWalkerContext *ctx) {
+    for (int i = 0; i < exprs->size(); ++i) {
+      walkExpressionTree(exprs->Mutable(i), walker, ctx);
+    }
+  }
+
+  virtual void walk(UnivPlanPlanNodePoly *planNodePoly, PlanNodeWalker *walker,
+                    PlanNodeWalkerContext *ctx) {}
+
+  static void walkPlanTree(UnivPlanPlanNodePoly *planNodePoly,
+                           PlanNodeWalker *walker, PlanNodeWalkerContext *ctx) {
+    auto planNode = PlanNodeUtil::getMutablePlanNode(planNodePoly);
+    if (planNode->has_leftplan())
+      walkPlanTree(planNode->mutable_leftplan(), walker, ctx);
+    if (planNode->has_rightplan())
+      walkPlanTree(planNode->mutable_rightplan(), walker, ctx);
+    if (planNodePoly->has_append()) {
+      auto append = planNodePoly->mutable_append();
+      for (auto i = 0; i < append->appendplans_size(); i++)
+        walkPlanTree(append->mutable_appendplans(i), walker, ctx);
+    }
+
+    walker->walk(planNodePoly, walker, ctx);
+  }
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_PLANNODE_WALKER_H_
diff --git a/depends/univplan/src/univplan/common/stagize.cc b/depends/univplan/src/univplan/common/stagize.cc
new file mode 100644
index 0000000..bfa8d50
--- /dev/null
+++ b/depends/univplan/src/univplan/common/stagize.cc
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#include "univplan/common/stagize.h"
+
+#include <utility>
+
+#include "dbcommon/log/logger.h"
+#include "univplan/common/plannode-util.h"
+#include "univplan/common/subplan-util.h"
+
+namespace univplan {
+
+// return a totally new Plan inside ctx->upb
+void PlanStagizer::stagize(const UnivPlanPlan *plan, StagizerContext *ctx) {
+  ctx->upb.reset(new UnivPlanBuilder);
+  ctx->upb->getPlanBuilderPlan()->from(*plan);
+
+  // stage main plan
+  ctx->pid = -1;
+  ctx->isleft = true;
+  stageNo = 0;
+  // -1 is a mark for the root stage, the stageNo of the plan tree's stage
+  // is a postorder traversal except that the stageNo of root stage is zero
+  ctx->upb->getPlanBuilderPlan()->setStageNo(-1);
+  stagize(plan->plan(), ctx, 0);
+  ctx->upb->getPlanBuilderPlan()->setStageNo(0);
+
+  // stage subplan
+  for (auto i = 0; i < plan->subplans_size(); i++) {
+    ctx->pid = -1;
+    ctx->isleft = true;
+    if (ctx->subplanStageNo[i + 1] <= ctx->totalStageNo) {
+      // retrieve stageNo for SubPlan
+      stagize(plan->subplans(i), ctx, ctx->subplanStageNo[i + 1]);
+    } else {
+      // allocate new stage for InitPlan
+      auto newStage =
+          ctx->upb->getPlanBuilderPlan()->addChildStageAndGetBuilder();
+      newStage->from(*(ctx->upb->getPlanBuilderPlan()->getPlan()));
+      newStage->setStageNo(ctx->subplanStageNo[i + 1]);
+      auto backupStage = ctx->upb->swapBuilder(std::move(newStage));
+      stagize(plan->subplans(i), ctx, ctx->subplanStageNo[i + 1]);
+      ctx->upb->swapBuilder(std::move(backupStage));
+      ctx->upb->getPlanBuilderPlan()->getPlan()->add_subplans();  // placeholder
+    }
+  }
+  assert(ctx->subplanStageNo.size() == plan->subplans_size());
+
+  // link subplan to correlated stage
+  auto rootPlan = ctx->upb->getPlanBuilderPlan()->getPlan();
+  assert(rootPlan->subplans_size() == plan->subplans_size());
+  auto rootStage = rootPlan;
+  UnivPlanPlan backup;  // store stagized subplans
+  backup.mutable_subplans()->Swap(rootPlan->mutable_subplans());
+  SubplanUtil::linkSubplan(&backup, rootStage);
+}
+
+// FIXME(chiyang): To support subplan with correct stageNo(i.e. the
+// corresponding sliceID/MotionID in HAWQ), we copy the motionID from HAWQ.
+// However, motionID is not set in previous unittest, as a result of which we
+// keep the old stageNo allocate strategy when needed(i.e. when the
+// connector.stageno() == -1).
+void PlanStagizer::stagize(const UnivPlanPlanNodePoly &originalNode,
+                           StagizerContext *ctx, int32_t currentStageNo) {
+  // ctx->upb->getPlanBuilderPlan referred to the current stage
+  // ctx->pid referred to the pid of the new plan tree
+  // ctx->isleft determine whether the current plannode is the left child of its
+  // parent
+  if (!originalNode.IsInitialized()) {
+    LOG_ERROR(ERRCODE_INTERNAL_ERROR, "stagize node is empty");
+  }
+  const UnivPlanPlan *currentStage = ctx->upb->getPlanBuilderPlan()->getPlan();
+  if (originalNode.has_connector()) {
+    const UnivPlanConnector &connector = originalNode.connector();
+
+    UnivPlanBuilderSinkInput::uptr builderSinkInput;
+    switch (connector.type()) {
+      case CONNECTORTYPE_CONVERGE:
+        builderSinkInput.reset(new UnivPlanBuilderConverge());
+        break;
+      case CONNECTORTYPE_SHUFFLE:
+        builderSinkInput.reset(new UnivPlanBuilderShuffle());
+        break;
+      case CONNECTORTYPE_BROADCAST:
+        builderSinkInput.reset(new UnivPlanBuilderBroadcast());
+        break;
+      default:
+        LOG_ERROR(ERRCODE_INTERNAL_ERROR, "unexpected connector node type %d",
+                  connector.type());
+    }
+    // build sink
+    UnivPlanBuilderSink::uptr builderSink(new UnivPlanBuilderSink());
+    builderSink->from(connector);
+    builderSink->setSourceStageNo(stageNo);
+    builderSink->setCurrentStageNo(currentStage->stageno());
+    builderSink->setConnectorType(connector.type());
+    builderSink->uid = nodeId++;
+    builderSink->pid = ctx->pid;
+    UnivPlanBuilderSink *backupBuilderSink = builderSink.get();
+    ctx->upb->addPlanNode(ctx->isleft, std::move(builderSink));
+
+    // build new stage, need to be done before add sinkinput
+    UnivPlanBuilderPlan::uptr builderNewStage =
+        ctx->upb->getPlanBuilderPlan()->addChildStageAndGetBuilder();
+    builderNewStage->from(*(ctx->upb->getPlanBuilderPlan()->getPlan()));
+    builderNewStage->setStageNo(connector.stageno() != -1 ? connector.stageno()
+                                                          : stageNo);
+
+    UnivPlanBuilderPlan::uptr backupStage =
+        ctx->upb->swapBuilder(std::move(builderNewStage));
+
+    // sinkInput info
+    builderSinkInput->from(connector);
+    builderSinkInput->setTargetStageNo(currentStage->stageno());
+    builderSinkInput->setCurrentStageNo(stageNo);
+    builderSinkInput->uid = nodeId++;
+    builderSinkInput->pid = -1;
+    UnivPlanBuilderSinkInput *backupBuilderSinkInput = builderSinkInput.get();
+    ctx->upb->addPlanNode(ctx->isleft, std::move(builderSinkInput));
+
+    // sinkinput's left child comes from connector,
+    // and there will be only one child
+    const UnivPlanPlanNodePoly &originalNodeLeft =
+        PlanNodeUtil::getPlanNode(originalNode).leftplan();
+    ctx->pid = nodeId - 1;  // the nodeId of sinkinput
+    ctx->isleft = true;
+    stageNo++;
+    stagize(originalNodeLeft, ctx, connector.stageno());
+
+    builderNewStage = ctx->upb->swapBuilder(std::move(backupStage));
+
+    // the old stageNo assign strategy, a post-order traversal
+    int32_t childStageNo =
+        ctx->totalStageNo - builderNewStage->getPlan()->stageno();
+    if (currentStageNo == -1)
+      currentStageNo =
+          (backupBuilderSinkInput->getTargetStageNo() != -1)
+              ? (ctx->totalStageNo - backupBuilderSinkInput->getTargetStageNo())
+              : 0;
+    // to bypass the old stageNo assign strategy when possible
+    if (connector.stageno() != -1) {
+      childStageNo = connector.stageno();
+      if (currentStageNo == -1) currentStageNo = currentStage->stageno();
+      if (currentStageNo == -1) currentStageNo = 0;
+    }
+
+    builderNewStage->setStageNo(childStageNo);
+    backupBuilderSink->setCurrentStageNo(currentStageNo);
+    backupBuilderSink->setSourceStageNo(childStageNo);
+    backupBuilderSinkInput->setCurrentStageNo(childStageNo);
+    backupBuilderSinkInput->setTargetStageNo(currentStageNo);
+  } else {
+    UnivPlanBuilderNode::uptr builderNodeNew =
+        PlanNodeUtil::createPlanBuilderNode(originalNode.type());
+    builderNodeNew->from(originalNode);
+    int32_t builderNodeNewId = nodeId++;
+    builderNodeNew->uid = builderNodeNewId;
+    builderNodeNew->pid = ctx->pid;
+    ctx->upb->addPlanNode(ctx->isleft, std::move(builderNodeNew));
+
+    const UnivPlanPlanNode &originalNodePlanNode =
+        PlanNodeUtil::getPlanNode(originalNode);
+
+    auto relatedSubplans = SubplanUtil::getRelatedSubplanIds(originalNode);
+    for (auto subplanId : relatedSubplans) {
+      ctx->subplanStageNo[subplanId] = currentStageNo;
+    }
+
+    if (originalNodePlanNode.has_rightplan()) {
+      ctx->pid = builderNodeNewId;
+      ctx->isleft = false;
+      const UnivPlanPlanNodePoly &originalNodeRight =
+          originalNodePlanNode.rightplan();
+      stagize(originalNodeRight, ctx, currentStageNo);
+    }
+
+    if (originalNodePlanNode.has_leftplan()) {
+      ctx->pid = builderNodeNewId;
+      ctx->isleft = true;
+      const UnivPlanPlanNodePoly &originalNodeLeft =
+          originalNodePlanNode.leftplan();
+      stagize(originalNodeLeft, ctx, currentStageNo);
+    }
+
+    if (originalNode.has_subqueryscan()) {
+      ctx->pid = builderNodeNewId;
+      ctx->isleft = true;
+      const UnivPlanPlanNodePoly &subplan =
+          originalNode.subqueryscan().subplan();
+      stagize(subplan, ctx, currentStageNo);
+    }
+  }
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/common/stagize.h b/depends/univplan/src/univplan/common/stagize.h
new file mode 100644
index 0000000..6f0b056
--- /dev/null
+++ b/depends/univplan/src/univplan/common/stagize.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_STAGIZE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_STAGIZE_H_
+
+#include <map>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder.h"
+
+namespace univplan {
+
+typedef struct StagizerContext {
+  UnivPlanBuilder::uptr upb;
+  int32_t pid;
+  bool isleft;
+  uint32_t totalStageNo;
+  std::map<int32_t, int32_t> subplanStageNo;
+} StagizerContext;
+
+class PlanStagizer {
+ public:
+  PlanStagizer() {}
+  ~PlanStagizer() {}
+
+  void stagize(const UnivPlanPlan *plan, StagizerContext *ctx);
+
+ private:
+  void stagize(const UnivPlanPlanNodePoly &node, StagizerContext *ctx,
+               int32_t currentStageNo);
+  int32_t nodeId = 0;
+  int32_t stageNo = 0;  // stageNo start from 0
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_STAGIZE_H_
diff --git a/depends/univplan/src/univplan/common/statistics.h b/depends/univplan/src/univplan/common/statistics.h
new file mode 100644
index 0000000..3090918
--- /dev/null
+++ b/depends/univplan/src/univplan/common/statistics.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_STATISTICS_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_STATISTICS_H_
+
+namespace univplan {
+// Statistics that are available for all types of columns.
+class ColumnStatistics {
+ public:
+  virtual ~ColumnStatistics() {}
+
+  // Get the number of values in this column. It will differ from the number
+  // of rows because of NULL values and repeated values.
+  // @return the number of values
+  uint64_t getNumberOfValues() const { return valueCount; }
+
+  // Returns true if there are nulls in the scope of column statistics
+  bool hasNull() const { return hasNullValue; }
+
+  virtual void reset() {
+    valueCount = 0;
+    hasNullValue = true;
+  }
+
+  void increment(uint64_t count) { valueCount += count; }
+
+  void unsetNull() { hasNullValue = false; }
+
+  virtual void merge(const ColumnStatistics& stats) {
+    valueCount += stats.getNumberOfValues();
+    hasNullValue |= stats.hasNull();
+  }
+
+ protected:
+  uint64_t valueCount;
+  bool hasNullValue;
+};
+
+class Statistics {
+ public:
+  virtual ~Statistics() {}
+
+  // Get the statistics of colId column.
+  // @return one column's statistics
+  virtual const ColumnStatistics* getColumnStatistics(uint32_t colId) const = 0;
+
+  // Get the number of columns
+  // @return the number of columns
+  virtual uint32_t getNumberOfColumns() const = 0;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_STATISTICS_H_
diff --git a/depends/univplan/src/univplan/common/subplan-util.cc b/depends/univplan/src/univplan/common/subplan-util.cc
new file mode 100644
index 0000000..b7e9fe7
--- /dev/null
+++ b/depends/univplan/src/univplan/common/subplan-util.cc
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#include "univplan/common/subplan-util.h"
+#include "univplan/common/plannode-util.h"
+#include "univplan/common/plannode-walker.h"
+
+namespace univplan {
+
+class SubplanWalker : public PlanNodeWalker {
+ public:
+  void walk(UnivPlanPlanNodePoly* planNodePoly, PlanNodeWalker* walker,
+            PlanNodeWalkerContext* ctx) override {
+    auto planNode = PlanNodeUtil::getMutablePlanNode(planNodePoly);
+
+    // Basic check
+    PlanNodeWalker::walkExpressionTree(planNode->mutable_targetlist(), walker,
+                                       ctx);
+    PlanNodeWalker::walkExpressionTree(planNode->mutable_quallist(), walker,
+                                       ctx);
+    PlanNodeWalker::walkExpressionTree(planNode->mutable_initplan(), walker,
+                                       ctx);
+
+    // Special check for shuffle
+    if (planNodePoly->has_shuffle()) {
+      auto sf = planNodePoly->mutable_shuffle();
+      PlanNodeWalker::walkExpressionTree(sf->mutable_hashexpr(), walker, ctx);
+    }
+
+    // Special check for joinQual
+    if (planNodePoly->has_nestloop()) {
+      auto nl = planNodePoly->mutable_nestloop();
+      PlanNodeWalker::walkExpressionTree(nl->mutable_joinqual(), walker, ctx);
+    }
+    if (planNodePoly->has_hashjoin()) {
+      auto hj = planNodePoly->mutable_hashjoin();
+      PlanNodeWalker::walkExpressionTree(hj->mutable_joinqual(), walker, ctx);
+      PlanNodeWalker::walkExpressionTree(hj->mutable_hashclauses(), walker,
+                                         ctx);
+      PlanNodeWalker::walkExpressionTree(hj->mutable_hashqualclauses(), walker,
+                                         ctx);
+    }
+    if (planNodePoly->has_mergejoin()) {
+      auto mj = planNodePoly->mutable_mergejoin();
+      PlanNodeWalker::walkExpressionTree(mj->mutable_joinqual(), walker, ctx);
+      PlanNodeWalker::walkExpressionTree(mj->mutable_mergeclauses(), walker,
+                                         ctx);
+    }
+
+    // Special check for limit
+    if (planNodePoly->has_limit()) {
+      auto limit = planNodePoly->mutable_limit();
+      if (limit->has_limitoffset())
+        PlanNodeWalker::walkExpressionTree(limit->mutable_limitoffset(), walker,
+                                           ctx);
+      if (limit->has_limitcount())
+        PlanNodeWalker::walkExpressionTree(limit->mutable_limitcount(), walker,
+                                           ctx);
+    }
+    if (planNodePoly->has_sort()) {
+      auto sort = planNodePoly->mutable_sort();
+      if (sort->has_limitoffset())
+        PlanNodeWalker::walkExpressionTree(sort->mutable_limitoffset(), walker,
+                                           ctx);
+      if (sort->has_limitcount())
+        PlanNodeWalker::walkExpressionTree(sort->mutable_limitcount(), walker,
+                                           ctx);
+    }
+
+    // Special check from result
+    if (planNodePoly->has_result()) {
+      auto result = planNodePoly->mutable_result();
+      PlanNodeWalker::walkExpressionTree(result->mutable_resconstantqual(),
+                                         walker, ctx);
+    }
+  }
+};
+
+class SubplanPlanidWalkerContext : public PlanNodeWalkerContext {
+ public:
+  std::vector<int32_t> planids;
+};
+
+class SubplanPlanidWalker : public SubplanWalker {
+ public:
+  void walk(UnivPlanExprPoly* expr, PlanNodeWalkerContext* ctx) override {
+    if (expr->has_subplan() && !expr->subplan().initplan()) {
+      SubplanPlanidWalkerContext* context =
+          reinterpret_cast<SubplanPlanidWalkerContext*>(ctx);
+      context->planids.push_back(expr->subplan().planid());
+    }
+  }
+};
+
+std::vector<int32_t> SubplanUtil::getRelatedSubplanIds(
+    const UnivPlanPlanNodePoly& planNodePolyInput) {
+  SubplanPlanidWalkerContext ctx;
+  SubplanPlanidWalker walker;
+  walker.SubplanWalker::walk(
+      &(const_cast<UnivPlanPlanNodePoly&>(planNodePolyInput)), &walker, &ctx);
+  return ctx.planids;
+}
+
+class SubplanAttachWalkerContext : public PlanNodeWalkerContext {
+ public:
+  const UnivPlanPlan* rootPlan;
+  UnivPlanPlan* currStage;
+};
+
+class SubplanAttachWalker : public SubplanWalker {
+ public:
+  void walk(UnivPlanExprPoly* expr, PlanNodeWalkerContext* ctx) override {
+    if (expr->has_subplan() && !expr->subplan().initplan()) {
+      auto context = reinterpret_cast<SubplanAttachWalkerContext*>(ctx);
+      // count plan ID from 1
+      auto oldPlanId = expr->subplan().planid();
+      context->currStage->add_subplans()->CopyFrom(
+          context->rootPlan->subplans(oldPlanId - 1));
+      expr->mutable_subplan()->set_planid(context->currStage->subplans_size());
+    }
+  }
+};
+
+void SubplanUtil::linkSubplan(UnivPlanPlan* rootPlan, UnivPlanPlan* currStage) {
+  SubplanAttachWalker walker;
+  SubplanAttachWalkerContext ctx;
+  ctx.rootPlan = rootPlan;
+  ctx.currStage = currStage;
+  PlanNodeWalker::walkPlanTree(currStage->mutable_plan(), &walker, &ctx);
+  for (auto i = 0; i < currStage->childstages_size(); i++) {
+    linkSubplan(rootPlan, currStage->mutable_childstages(i));
+  }
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/common/subplan-util.h b/depends/univplan/src/univplan/common/subplan-util.h
new file mode 100644
index 0000000..8c1c942
--- /dev/null
+++ b/depends/univplan/src/univplan/common/subplan-util.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_SUBPLAN_UTIL_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_SUBPLAN_UTIL_H_
+
+#include <vector>
+
+#include "univplan/common/univplan-type.h"
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class SubplanUtil {
+ public:
+  // @return Return related subplan IDs.
+  static std::vector<int32_t> getRelatedSubplanIds(
+      const UnivPlanPlanNodePoly& planNodePolyInput);
+
+  // Link subplan to its correlated plan stage.
+  // Inside stage, each subplan expression will change its planId to a
+  // new number according to the order of attaching subplans.
+  //
+  // @param rootPlan The plan that contains fully stagized subplans.
+  static void linkSubplan(UnivPlanPlan* rootPlan, UnivPlanPlan* currStage);
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_SUBPLAN_UTIL_H_
diff --git a/depends/univplan/src/univplan/common/univplan-type.h b/depends/univplan/src/univplan/common/univplan-type.h
new file mode 100644
index 0000000..b38f61d
--- /dev/null
+++ b/depends/univplan/src/univplan/common/univplan-type.h
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_UNIVPLAN_TYPE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_UNIVPLAN_TYPE_H_
+
+#include <list>
+#include <string>
+
+#include "dbcommon/common/tuple-batch.h"
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-scan-task.h"
+
+namespace univplan {
+typedef google::protobuf::RepeatedPtrField<univplan::UnivPlanExprPoly>
+    UnivPlanExprPolyList;
+
+typedef google::protobuf::RepeatedPtrField<univplan::UnivPlanScanTask>
+    UnivPlanScanTaskList;
+
+// this class provides interface of accessing scan task and its splits
+class UnivPlanScanFileSplitList {
+ public:
+  UnivPlanScanFileSplitList() : splitsTb_(nullptr) {}
+  explicit UnivPlanScanFileSplitList(dbcommon::TupleBatch *tb)
+      : splitsTb_(tb) {}
+
+  void setSplitsTb(dbcommon::TupleBatch *tb) { splitsTb_ = tb; }
+  uint32_t splits_size() const {
+    return splitsTb_ == nullptr ? 0 : splitsTb_->getNumOfRows();
+  }
+
+  // methods of getting split properties indexed in tb
+  void splits_filename(int index, std::string *filename) {
+    const dbcommon::TupleBatchReader &readers =
+        splitsTb_->getTupleBatchReader();
+    uint64_t len = 0;
+    bool isNull = false;
+    const char *res =
+        readers[TBSPLITS_COL_INDEX_FILENAME]->read(index, &len, &isNull);
+    filename->assign(res, len);
+  }
+
+  int64_t splits_start(int index) const {
+    const dbcommon::TupleBatchReader &readers =
+        splitsTb_->getTupleBatchReader();
+    uint64_t len = 0;
+    bool isNull = false;
+    const char *res =
+        readers[TBSPLITS_COL_INDEX_START]->read(index, &len, &isNull);
+    return *(reinterpret_cast<const int64_t *>(res));
+  }
+
+  int64_t splits_len(int index) const {
+    const dbcommon::TupleBatchReader &readers =
+        splitsTb_->getTupleBatchReader();
+    uint64_t len = 0;
+    bool isNull = false;
+    const char *res =
+        readers[TBSPLITS_COL_INDEX_LEN]->read(index, &len, &isNull);
+    return *(reinterpret_cast<const int64_t *>(res));
+  }
+
+  int64_t splits_rangeid(int index) const {
+    const dbcommon::TupleBatchReader &readers =
+        splitsTb_->getTupleBatchReader();
+    uint64_t len = 0;
+    bool isNull = false;
+    const char *res =
+        readers[TBSPLITS_COL_INDEX_RANGEID]->read(index, &len, &isNull);
+    return *(reinterpret_cast<const int64_t *>(res));
+  }
+
+  int32_t splits_rgid(int index) const {
+    const dbcommon::TupleBatchReader &readers =
+        splitsTb_->getTupleBatchReader();
+    uint64_t len = 0;
+    bool isNull = false;
+    const char *res =
+        readers[TBSPLITS_COL_INDEX_RGID]->read(index, &len, &isNull);
+    return *(reinterpret_cast<const int32_t *>(res));
+  }
+
+  void debugOuput() {
+    if (splitsTb_ == nullptr) {
+      LOG_INFO("no rows in splits tb");
+      return;
+    }
+    for (int i = 0; i < splitsTb_->getNumOfRows(); ++i) {
+      std::string filename;
+      splits_filename(i, &filename);
+      int64_t start = splits_start(i);
+      int64_t len = splits_len(i);
+      int64_t rangeid = splits_rangeid(i);
+      int32_t rgid = splits_rgid(i);
+      LOG_INFO("[%d] %s,%lld,%lld,%lld,%d", i, filename.c_str(), start, len,
+               rangeid, rgid);
+    }
+  }
+
+ protected:
+  dbcommon::TupleBatch *splitsTb_;
+};
+
+// this subclass owns deserialized tb
+class UnivPlanScanFileSplitListTb : public UnivPlanScanFileSplitList {
+ public:
+  UnivPlanScanFileSplitListTb() : UnivPlanScanFileSplitList(nullptr) {}
+  explicit UnivPlanScanFileSplitListTb(dbcommon::TupleBatch::uptr tb)
+      : UnivPlanScanFileSplitList(tb.get()) {
+    splitsTbInst_ = std::move(tb);
+  }
+  void deserialize(const std::string &serializedTb) {
+    splitsTbInst_.reset(new dbcommon::TupleBatch());
+    splitsTbInst_->deserialize(serializedTb);
+    setSplitsTb(splitsTbInst_.get());
+  }
+
+ protected:
+  std::unique_ptr<dbcommon::TupleBatch> splitsTbInst_;
+};
+
+typedef std::list<std::unique_ptr<univplan::UnivPlanScanFileSplitList>>
+    UnivPlanScanFileSplitListList;
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_UNIVPLAN_TYPE_H_
diff --git a/depends/univplan/src/univplan/common/var-util.cc b/depends/univplan/src/univplan/common/var-util.cc
new file mode 100644
index 0000000..e31be79
--- /dev/null
+++ b/depends/univplan/src/univplan/common/var-util.cc
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#include "univplan/common/var-util.h"
+
+#include <utility>
+
+#include "dbcommon/log/logger.h"
+#include "dbcommon/utils/macro.h"
+
+#include "univplan/common/plannode-util.h"
+#include "univplan/common/plannode-walker.h"
+
+namespace univplan {
+
+class FixVarTypeContext : public PlanNodeWalkerContext {
+ public:
+  FixVarTypeContext() {}
+  virtual ~FixVarTypeContext() {}
+
+ public:
+  UnivPlanPlanNode* planNode = nullptr;
+};
+
+class FixVarTypeWalker : public PlanNodeWalker {
+ public:
+  void walk(UnivPlanExprPoly* expr, PlanNodeWalkerContext* ctx) override {
+    if (expr->has_var()) {
+      FixVarTypeContext* context = reinterpret_cast<FixVarTypeContext*>(ctx);
+      UnivPlanPlanNode* planNode = context->planNode;
+      UnivPlanVar* var = expr->mutable_var();
+      if (var->varno() == OUTER_VAR && planNode->has_leftplan()) {
+        UnivPlanPlanNode* leftPlanNode =
+            PlanNodeUtil::getMutablePlanNode(planNode->mutable_leftplan());
+        UnivPlanExprPoly* refTargetEntry =
+            leftPlanNode->mutable_targetlist(var->varattno() - 1);
+        var->set_typeid_(
+            static_cast<int32_t>(PlanNodeUtil::exprType(*refTargetEntry)));
+      } else if (var->varno() == INNER_VAR && planNode->has_rightplan()) {
+        UnivPlanPlanNode* rightPlanNode =
+            PlanNodeUtil::getMutablePlanNode(planNode->mutable_rightplan());
+        UnivPlanExprPoly* refTargetEntry =
+            rightPlanNode->mutable_targetlist(var->varattno() - 1);
+        var->set_typeid_(
+            static_cast<int32_t>(PlanNodeUtil::exprType(*refTargetEntry)));
+      }
+    }
+  }
+};
+
+void VarUtil::fixVarType(UnivPlanPlanNodePoly* plantree) {
+  UnivPlanPlanNode* planNode = PlanNodeUtil::getMutablePlanNode(plantree);
+
+  // workaround here
+  if (plantree->type() == UNIVPLAN_APPEND) return;
+
+  if (planNode->has_leftplan()) fixVarType(planNode->mutable_leftplan());
+  if (planNode->has_rightplan()) fixVarType(planNode->mutable_rightplan());
+  FixVarTypeContext walkerContext;
+  walkerContext.planNode = planNode;
+  FixVarTypeWalker walker;
+
+  PlanNodeWalker::walkExpressionTree(planNode->mutable_targetlist(), &walker,
+                                     &walkerContext);
+}
+
+class CollectVarTypeContext : public PlanNodeWalkerContext {
+ public:
+  CollectVarTypeContext() {}
+  virtual ~CollectVarTypeContext() {}
+
+ public:
+  std::vector<int32_t> varAttNoVec;
+};
+
+class CollectVarTypeWalker : public PlanNodeWalker {
+ public:
+  void walk(UnivPlanExprPoly* expr, PlanNodeWalkerContext* ctx) override {
+    if (expr->has_var()) {
+      CollectVarTypeContext* context =
+          reinterpret_cast<CollectVarTypeContext*>(ctx);
+      context->varAttNoVec.push_back(expr->var().varattno());
+    }
+  }
+};
+
+std::vector<int32_t> VarUtil::collectVarAttNo(UnivPlanExprPoly* expr) {
+  CollectVarTypeContext walkerContext;
+  CollectVarTypeWalker walker;
+
+  PlanNodeWalker::walkExpressionTree(expr, &walker, &walkerContext);
+  return std::move(walkerContext.varAttNoVec);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/common/var-util.h b/depends/univplan/src/univplan/common/var-util.h
new file mode 100644
index 0000000..c185e38
--- /dev/null
+++ b/depends/univplan/src/univplan/common/var-util.h
@@ -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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_COMMON_VAR_UTIL_H_
+#define UNIVPLAN_SRC_UNIVPLAN_COMMON_VAR_UTIL_H_
+
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class VarUtil {
+ public:
+  VarUtil() {}
+  ~VarUtil() {}
+
+  void fixVarType(UnivPlanPlanNodePoly* plantree);
+  std::vector<int32_t> collectVarAttNo(UnivPlanExprPoly* expr);
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_COMMON_VAR_UTIL_H_
diff --git a/depends/univplan/src/univplan/cwrapper/univplan-c.cc b/depends/univplan/src/univplan/cwrapper/univplan-c.cc
new file mode 100644
index 0000000..0bce15e
--- /dev/null
+++ b/depends/univplan/src/univplan/cwrapper/univplan-c.cc
@@ -0,0 +1,1172 @@
+/*
+ * 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.
+ */
+
+#include "univplan/cwrapper/univplan-c.h"
+
+#include <cassert>
+#include <map>
+#include <stack>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "dbcommon/function/func-kind.cg.h"
+#include "dbcommon/function/func.h"
+#include "dbcommon/log/logger.h"
+#include "dbcommon/utils/comp/lz4-compressor.h"
+#include "dbcommon/utils/macro.h"
+
+#include "univplan/common/plannode-util.h"
+#include "univplan/common/stagize.h"
+#include "univplan/common/var-util.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-tree.h"
+#include "univplan/univplanbuilder/univplanbuilder-plan.h"
+#include "univplan/univplanbuilder/univplanbuilder-table.h"
+#include "univplan/univplanbuilder/univplanbuilder.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static void univPlanSetError(UnivPlanCatchedError *ce, int errCode,
+                             const char *errMsg);
+
+struct UnivPlanC {
+  univplan::UnivPlanBuilder::uptr upb;
+  std::stack<univplan::UnivPlanBuilder::uptr> upbBak;
+  univplan::UnivPlanBuilderNode::uptr curNode;
+  std::stack<univplan::UnivPlanBuilderNode::uptr> curNodeBak;
+  univplan::UnivPlanBuilderExprTree::uptr curExpr;
+  std::string serializedPlan;
+  UnivPlanCatchedError error;
+  std::string debugString;
+  uint16_t totalStageNo;
+  std::map<int32_t, int32_t> subplanStageNo;
+};
+
+UnivPlanC *univPlanNewInstance() {
+  UnivPlanC *instance = new UnivPlanC();
+  instance->upb.reset(new univplan::UnivPlanBuilder);
+  instance->error.errCode = ERRCODE_SUCCESSFUL_COMPLETION;
+  instance->totalStageNo = 0;
+  return instance;
+}
+
+void univPlanFreeInstance(UnivPlanC **up) {
+  assert(up != nullptr);
+  if (*up == nullptr) return;
+  delete *up;
+  *up = nullptr;
+}
+
+void univPlanNewSubPlanNode(UnivPlanC *up) {
+  up->upbBak.push(std::move(up->upb));
+  up->curNodeBak.push(std::move(up->curNode));
+  up->upb.reset(new univplan::UnivPlanBuilder);
+}
+
+void univPlanFreeSubPlanNode(UnivPlanC *up) {
+  up->upb = std::move(up->upbBak.top());
+  up->upbBak.pop();
+  up->curNode = std::move(up->curNodeBak.top());
+  up->curNodeBak.pop();
+}
+
+void univPlanRangeTblEntryAddTable(UnivPlanC *up, uint64_t tid,
+                                   FormatType format, const char *location,
+                                   const char *optStrInJson, uint32_t columnNum,
+                                   const char **columnName,
+                                   int32_t *columnDataType,
+                                   int64_t *columnDataTypeMod) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  univplan::UnivPlanBuilderRangeTblEntry::uptr rte =
+      bld->addRangeTblEntryAndGetBuilder();
+  univplan::UnivPlanBuilderTable::uptr table(
+      new univplan::UnivPlanBuilderTable);
+  table->setTableId(tid);
+  univplan::UNIVPLANFORMATTYPE fmtType;
+  switch (format) {
+    case FormatType::UnivPlanCsvFormat:
+      fmtType = univplan::CSV_FORMAT;
+      break;
+    case FormatType::UnivPlanTextFormat:
+      fmtType = univplan::TEXT_FORMAT;
+      break;
+    case FormatType::UnivPlanOrcFormat:
+      fmtType = univplan::ORC_FORMAT;
+      break;
+    case FormatType::UnivPlanMagmaFormat:
+      fmtType = univplan::MAGMA_FORMAT;
+      break;
+    default:
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "univPlanRangeTblEntryAddTable can't handle FormatType %d",
+                format);
+  }
+  table->setTableFormat(fmtType);
+  table->setTableLocation(location);
+
+  std::string cvKey = "TableOptionsInJson_" + std::to_string(tid);
+  std::string nCvKey;
+  bld->addCommonValue(cvKey, optStrInJson, &nCvKey);
+  table->setTableOptionsInJson(nCvKey);
+  for (int i = 0; i < columnNum; ++i) {
+    univplan::UnivPlanBuilderColumn::uptr column =
+        table->addPlanColumnAndGetBuilder();
+    column->setColumnName(columnName[i]);
+    column->setTypeId(columnDataType[i]);
+    column->setTypeMod(columnDataTypeMod[i]);
+  }
+
+  rte->setTable(std::move(table->ownTable()));
+}
+
+void univPlanRangeTblEntryAddDummy(UnivPlanC *up) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  univplan::UnivPlanBuilderRangeTblEntry::uptr rte =
+      bld->addRangeTblEntryAndGetBuilder();
+  univplan::UnivPlanBuilderTable::uptr table(
+      new univplan::UnivPlanBuilderTable);
+  table->setTableId(0);
+  univplan::UNIVPLANFORMATTYPE fmtType = univplan::INVALID_FORMAT;
+  table->setTableFormat(fmtType);
+  table->setTableLocation("");
+  table->setTableOptionsInJson("");
+  rte->setTable(std::move(table->ownTable()));
+}
+
+void univPlanReceiverAddListeners(UnivPlanC *up, uint32_t listenerNum,
+                                  const char **addr, int32_t *port) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  univplan::UnivPlanBuilderReceiver::uptr rev = bld->addReceiverAndGetBuilder();
+  for (int i = 0; i < listenerNum; ++i) {
+    univplan::UnivPlanBuilderListener::uptr listener =
+        rev->addPlanListenerAndGetBuilder();
+    listener->setAddress(addr[i]);
+    listener->setPort(port[i]);
+  }
+}
+
+void univPlanAddParamInfo(UnivPlanC *up, int32_t type, bool isNull,
+                          const char *buffer) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  univplan::UnivPlanBuilderParamInfo::uptr paramInfo =
+      bld->addParamInfoAndGetBuilder();
+  paramInfo->setType(type).setIsNull(isNull);
+  if (!isNull && buffer) paramInfo->setValue(buffer);
+}
+
+void univPlanSetDoInstrument(UnivPlanC *up, bool doInstrument) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  bld->setDoInstrument(doInstrument);
+}
+
+void univPlanSetNCrossLevelParams(UnivPlanC *up, int32_t nCrossLevelParams) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  bld->setNCrossLevelParams(nCrossLevelParams);
+}
+
+void univPlanSetCmdType(UnivPlanC *up, UnivPlanCCmdType type) {
+  univplan::UnivPlanBuilderPlan *bld = up->upb->getPlanBuilderPlan();
+  univplan::UNIVPLANCMDTYPE cmdType;
+  switch (type) {
+    case UnivPlanCCmdType::UNIVPLAN_CMD_SELECT:
+      cmdType = univplan::UNIVPLANCMDTYPE::CMD_SELECT;
+      break;
+    case UnivPlanCCmdType::UNIVPLAN_CMD_INSERT:
+      cmdType = univplan::UNIVPLANCMDTYPE::CMD_INSERT;
+      break;
+    default:
+      LOG_ERROR(ERRCODE_FEATURE_NOT_SUPPORTED, "CmdType(%d) not supported yet",
+                type);
+  }
+
+  bld->setCmdType(cmdType);
+}
+
+void univPlanSetPlanNodeInfo(UnivPlanC *up, double planRows,
+                             int32_t planRowWidth, uint64_t operatorMemKB) {
+  up->curNode->setNodePlanRows(planRows);
+  up->curNode->setNodePlanRowWidth(planRowWidth);
+  up->curNode->setNodePlanOperatorMemKB(operatorMemKB);
+}
+
+int32_t univPlanConnectorNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_CONNECTOR);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  up->totalStageNo += 1;
+  return up->curNode->uid;
+}
+
+void univPlanConnectorSetType(UnivPlanC *up, ConnectorType type) {
+  switch (type) {
+    case ConnectorType::UnivPlanShuffle:
+      dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+          ->setConnectorType(univplan::CONNECTORTYPE_SHUFFLE);
+      break;
+    case ConnectorType::UnivPlanBroadcast:
+      dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+          ->setConnectorType(univplan::CONNECTORTYPE_BROADCAST);
+      break;
+    case ConnectorType::UnivPlanConverge:
+      dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+          ->setConnectorType(univplan::CONNECTORTYPE_CONVERGE);
+      break;
+    default:
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "univPlanConnectorSetType can't handle ConnectorType %d", type);
+  }
+}
+
+void univPlanConnectorSetStageNo(UnivPlanC *up, int32_t stageNo) {
+  dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+      ->setStageNo(stageNo);
+}
+
+void univPlanConnectorSetRangeVsegMap(UnivPlanC *up, int *map,
+                                      bool magmaTable) {
+  dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+      ->setMagmaMap(map);
+  dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+      ->setMagmaTable(magmaTable);
+}
+
+void univPlanSetRangeNum(UnivPlanC *up, int rangeNum) {
+  dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+      ->setRangeNum(rangeNum);
+}
+
+void univPlanConnectorSetColIdx(UnivPlanC *up, int64_t numCols,
+                                const int32_t *colIdx) {
+  univplan::UnivPlanBuilderConnector *conn =
+      dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get());
+  std::vector<int32_t> nArray(colIdx, colIdx + numCols);
+  conn->setColIdx(nArray);
+}
+
+void univPlanConnectorSetSortFuncId(UnivPlanC *up, int64_t numCols,
+                                    const int32_t *sortFuncId) {
+  univplan::UnivPlanBuilderConnector *conn =
+      dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get());
+  std::vector<int32_t> nArray(sortFuncId, sortFuncId + numCols);
+  conn->setSortFuncId(nArray);
+}
+
+int32_t univPlanExtScanNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_EXT_GS_SCAN);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanExtScanSetRelId(UnivPlanC *up, uint32_t relId) {
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->setScanRelId(relId);
+}
+
+void univPlanExtScanSetIndex(UnivPlanC *up, bool index) {
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->setScanIndex(index);
+}
+
+void univPlanExtScanSetScanType(UnivPlanC *up, int type) {
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->setIndexScanType(univplan::ExternalScanType(type));
+}
+
+void univPlanExtScanDirection(UnivPlanC *up, int direction) {
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->setDirectionType(univplan::ExternalScanDirection(direction));
+}
+
+void univPlanExtScanSetIndexName(UnivPlanC *up, const char *indexName) {
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->setIndexName(indexName);
+}
+
+void univPlanIndexQualListAddExpr(UnivPlanC *up) {
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->addIndexQual(std::move(up->curExpr));
+}
+
+void univPlanExtScanSetColumnsToRead(UnivPlanC *up, int64_t numCols,
+                                     const int32_t *columnsToRead) {
+  std::vector<int32_t> nArray(columnsToRead, columnsToRead + numCols);
+  dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+      ->setColumnsToRead(nArray);
+}
+
+int32_t univPlanSeqScanNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_SCAN_SEQ);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanSeqScanSetRelId(UnivPlanC *up, uint32_t relId) {
+  dynamic_cast<univplan::UnivPlanBuilderScanSeq *>(up->curNode.get())
+      ->setScanRelId(relId);
+}
+
+void univPlanSeqScanSetColumnsToRead(UnivPlanC *up, int64_t numCols,
+                                     const int32_t *columnsToRead) {
+  std::vector<int32_t> nArray(columnsToRead, columnsToRead + numCols);
+  dynamic_cast<univplan::UnivPlanBuilderScanSeq *>(up->curNode.get())
+      ->setColumnsToRead(nArray);
+}
+
+void univPlanSeqScanSetReadStatsOnly(UnivPlanC *up, bool readStatsOnly) {
+  dynamic_cast<univplan::UnivPlanBuilderScanSeq *>(up->curNode.get())
+      ->setReadStatsOnly(readStatsOnly);
+}
+
+void univPlanSeqScanAddTaskWithFileSplits(bool isMagma, UnivPlanC *up,
+                                          uint32_t fileSplitNum,
+                                          const char **fileName, int64_t *start,
+                                          int64_t *len, int32_t *rangeid,
+                                          int32_t *rgid) {
+  univplan::UnivPlanBuilderScanTask::uptr task;
+  if (isMagma) {
+    task = dynamic_cast<univplan::UnivPlanBuilderExtGSScan *>(up->curNode.get())
+               ->addScanTaskAndGetBuilder();
+  } else {
+    task = dynamic_cast<univplan::UnivPlanBuilderScanSeq *>(up->curNode.get())
+               ->addScanTaskAndGetBuilder();
+  }
+  for (int i = 0; i < fileSplitNum; ++i) {
+    task->addScanFileSplit(fileName[i], start[i], len[i],
+                           (rangeid == nullptr ? -1 : rangeid[i]),
+                           (rgid == nullptr ? -1 : rgid[i]));
+  }
+  task->generate();
+}
+
+int32_t univPlanAggNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_AGG);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanAggSetNumGroupsAndGroupColIndexes(UnivPlanC *up, int64_t numGroups,
+                                               int64_t numCols,
+                                               const int32_t *grpColIdx) {
+  univplan::UnivPlanBuilderAgg *agg =
+      dynamic_cast<univplan::UnivPlanBuilderAgg *>(up->curNode.get());
+  agg->setNumGroups(numGroups);
+  std::vector<int32_t> nArray(grpColIdx, grpColIdx + numCols);
+  agg->setGroupColIndexes(nArray);
+}
+
+int32_t univPlanSortNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_SORT);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanSortSetColIdx(UnivPlanC *up, int64_t numCols,
+                           const int32_t *colIdx) {
+  univplan::UnivPlanBuilderSort *sort =
+      dynamic_cast<univplan::UnivPlanBuilderSort *>(up->curNode.get());
+  std::vector<int32_t> nArray(colIdx, colIdx + numCols);
+  sort->setColIdx(nArray);
+}
+
+void univPlanSortSetSortFuncId(UnivPlanC *up, int64_t numCols,
+                               const int32_t *sortFuncId) {
+  univplan::UnivPlanBuilderSort *sort =
+      dynamic_cast<univplan::UnivPlanBuilderSort *>(up->curNode.get());
+  std::vector<int32_t> nArray(sortFuncId, sortFuncId + numCols);
+  sort->setSortFuncId(nArray);
+}
+
+void univPlanSortAddLimitOffset(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderSort *>(up->curNode.get())
+      ->setLimitOffset(std::move(up->curExpr));
+}
+
+void univPlanSortAddLimitCount(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderSort *>(up->curNode.get())
+      ->setLimitCount(std::move(up->curExpr));
+}
+
+int32_t univPlanLimitNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_LIMIT);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+int32_t univPlanAppendNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_APPEND);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanAppendAddAppendPlan(UnivPlanC *up) {
+  const univplan::UnivPlanPlanNodePoly &poly =
+      up->upb->getPlanBuilderPlan()->getPlan()->plan();
+  dynamic_cast<univplan::UnivPlanBuilderAppend *>(up->curNodeBak.top().get())
+      ->addAppendPlan(poly);
+}
+
+static univplan::UNIVPLANJOINTYPE joinTypeMapping(UnivPlanCJoinType type) {
+  switch (type) {
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_INNER:
+      return univplan::UNIVPLANJOINTYPE::JOIN_INNER;
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_LEFT:
+      return univplan::UNIVPLANJOINTYPE::JOIN_LEFT;
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_RIGHT:
+      return univplan::UNIVPLANJOINTYPE::JOIN_RIGHT;
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_FULL:
+      return univplan::UNIVPLANJOINTYPE::JOIN_FULL;
+    default:
+      LOG_INFO("JoinType(%d) not supported yet", type);
+      return univplan::UNIVPLANJOINTYPE::JOIN_NOT_SUPPORTED;
+  }
+}
+
+int32_t univPlanNestLoopNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_NESTLOOP);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+bool univPlanNestLoopSetType(UnivPlanC *up, UnivPlanCJoinType type) {
+  univplan::UNIVPLANJOINTYPE univplanType = joinTypeMapping(type);
+  if (univplanType == univplan::UNIVPLANJOINTYPE::JOIN_NOT_SUPPORTED)
+    return false;
+  dynamic_cast<univplan::UnivPlanBuilderNestLoop *>(up->curNode.get())
+      ->setJoinType(univplanType);
+  return true;
+}
+
+void univPlanNestLoopAddJoinQual(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderNestLoop *>(up->curNode.get())
+      ->addJoinQual(std::move(up->curExpr));
+}
+
+int32_t univPlanHashJoinNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_HASHJOIN);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+bool univPlanHashJoinSetType(UnivPlanC *up, UnivPlanCJoinType type) {
+  switch (type) {
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_INNER:
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_LEFT:
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_IN:
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_LASJ:
+    case UnivPlanCJoinType::UNIVPLAN_JOIN_LASJ_NOTIN:
+      break;
+    default:
+      return false;
+  }
+  dynamic_cast<univplan::UnivPlanBuilderHashJoin *>(up->curNode.get())
+      ->setJoinType(static_cast<univplan::UNIVPLANJOINTYPE>(type));
+  return true;
+}
+
+void univPlanHashJoinAddJoinQual(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderHashJoin *>(up->curNode.get())
+      ->addJoinQual(std::move(up->curExpr));
+}
+
+void univPlanHashJoinAddHashClause(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderHashJoin *>(up->curNode.get())
+      ->addHashClause(std::move(up->curExpr));
+}
+
+void univPlanHashJoinAddHashQualClause(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderHashJoin *>(up->curNode.get())
+      ->addHashQualClause(std::move(up->curExpr));
+}
+
+int32_t univPlanMergeJoinNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_MERGEJOIN);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+bool univPlanMergeJoinSetType(UnivPlanC *up, UnivPlanCJoinType type) {
+  univplan::UNIVPLANJOINTYPE univplanType = joinTypeMapping(type);
+  if (univplanType == univplan::UNIVPLANJOINTYPE::JOIN_NOT_SUPPORTED)
+    return false;
+  dynamic_cast<univplan::UnivPlanBuilderMergeJoin *>(up->curNode.get())
+      ->setJoinType(joinTypeMapping(type));
+  return true;
+}
+
+void univPlanMergeJoinAddJoinQual(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderMergeJoin *>(up->curNode.get())
+      ->addJoinQual(std::move(up->curExpr));
+}
+
+void univPlanMergeJoinAddMergeClause(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderMergeJoin *>(up->curNode.get())
+      ->addMergeClause(std::move(up->curExpr));
+}
+
+int32_t univPlanHashNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_HASH);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+static univplan::UNIVPLANSHARETYPE shareTypeMapping(UnivPlanCShareType type) {
+  switch (type) {
+    case UnivPlanCShareType::UNIVPLAN_SHARE_NOTSHARED:
+      return univplan::UNIVPLANSHARETYPE::SHARE_NOTSHARED;
+    case UnivPlanCShareType::UNIVPLAN_SHARE_MATERIAL_XSLICE:
+      return univplan::UNIVPLANSHARETYPE::SHARE_MATERIAL_XSLICE;
+    default:
+      LOG_INFO("ShareType(%d) not supported yet", type);
+      return univplan::UNIVPLANSHARETYPE::SHARE_NOT_SUPPORTED;
+  }
+}
+
+int32_t univPlanMaterialNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_MATERIAL);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+bool univPlanMaterialSetAttr(UnivPlanC *up, UnivPlanCShareType type,
+                             bool cdbStrict, int32_t shareId,
+                             int32_t driverSlice, int32_t nsharer,
+                             int32_t xslice) {
+  univplan::UNIVPLANSHARETYPE univplanType = shareTypeMapping(type);
+  if (univplanType == univplan::UNIVPLANSHARETYPE::SHARE_NOT_SUPPORTED)
+    return false;
+  dynamic_cast<univplan::UnivPlanBuilderMaterial *>(up->curNode.get())
+      ->setShareType(univplanType)
+      .setCdbStrict(cdbStrict)
+      .setShareId(shareId)
+      .setDriverSlice(driverSlice)
+      .setNSharer(nsharer)
+      .setNSharerXSlice(xslice);
+  return true;
+}
+
+int32_t univPlanShareInputScanNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_SHAREINPUTSCAN);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+bool univPlanShareInputScanSetAttr(UnivPlanC *up, UnivPlanCShareType type,
+                                   int32_t shareId, int32_t driverSlice) {
+  univplan::UNIVPLANSHARETYPE univplanType = shareTypeMapping(type);
+  if (univplanType == univplan::UNIVPLANSHARETYPE::SHARE_NOT_SUPPORTED)
+    return false;
+  univplan::UnivPlanBuilderShareInputScan *bld =
+      dynamic_cast<univplan::UnivPlanBuilderShareInputScan *>(
+          up->curNode.get());
+  bld->setShareType(univplanType);
+  bld->setShareId(shareId);
+  bld->setDriverSlice(driverSlice);
+  return true;
+}
+
+int32_t univPlanResultNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_RESULT);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanResultAddResConstantQual(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderResult *>(up->curNode.get())
+      ->addResConstantQual(std::move(up->curExpr));
+}
+
+int32_t univPlanSubqueryScanNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode = univplan::PlanNodeUtil::createPlanBuilderNode(
+      univplan::UNIVPLAN_SUBQUERYSCAN);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+
+void univPlanSubqueryScanAddSubPlan(UnivPlanC *up) {
+  const univplan::UnivPlanPlanNodePoly &poly =
+      up->upb->getPlanBuilderPlan()->getPlan()->plan();
+  dynamic_cast<univplan::UnivPlanBuilderSubqueryScan *>(
+      up->curNodeBak.top().get())
+      ->setSubPlan(poly);
+}
+
+int32_t univPlanUniqueNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_UNIQUE);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  return up->curNode->uid;
+}
+void univPlanUniqueSetNumGroupsAndUniqColIdxs(UnivPlanC *up, int64_t numCols,
+                                              const int32_t *uniqColIdxs) {
+  auto uniq =
+      dynamic_cast<univplan::UnivPlanBuilderUnique *>(up->curNode.get());
+  uniq->setUniqColIdxs(numCols, uniqColIdxs);
+}
+
+int32_t univPlanInsertNewInstance(UnivPlanC *up, int32_t pid) {
+  up->curNode =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_INSERT);
+  up->curNode->pid = pid;
+  up->curNode->uid = up->upb->getUid();
+  up->totalStageNo += 1;
+  return up->curNode->uid;
+}
+
+void univPlanInsertSetRelId(UnivPlanC *up, uint32_t relId) {
+  dynamic_cast<univplan::UnivPlanBuilderInsert *>(up->curNode.get())
+      ->setInsertRelId(relId);
+}
+
+void univPlanAddToPlanNode(UnivPlanC *up, bool isLeft) {
+  up->upb->addPlanNode(isLeft, std::move(up->curNode));
+}
+
+UnivPlanCatchedError *univPlanGetLastError(UnivPlanC *up) {
+  return &(up->error);
+}
+
+void univPlanFixVarType(UnivPlanC *up) {
+  univplan::VarUtil varUtil;
+  varUtil.fixVarType(up->upb->getPlanBuilderPlan()->getPlan()->mutable_plan());
+}
+
+void univPlanStagize(UnivPlanC *up) {
+  univplan::PlanStagizer stagizer;
+  univplan::StagizerContext ctx;
+  ctx.totalStageNo = up->totalStageNo;
+  ctx.subplanStageNo = up->subplanStageNo;
+  stagizer.stagize(up->upb->getPlanBuilderPlan()->getPlan(), &ctx);
+  up->upb = std::move(ctx.upb);
+}
+
+void univPlanAddGuc(UnivPlanC *up, const char *name, const char *value) {
+  up->upb->getPlanBuilderPlan()->addGuc(name, value);
+}
+
+const char *univPlanSerialize(UnivPlanC *up, int32_t *size, bool compress) {
+  // compress
+  if (compress) {
+    std::string serializedPlan = up->upb->serialize();
+    dbcommon::LZ4Compressor comp;
+    comp.compress(serializedPlan.c_str(), serializedPlan.size(),
+                  &up->serializedPlan);
+
+  } else {
+    up->serializedPlan = up->upb->serialize();
+  }
+  *size = up->serializedPlan.size();
+  return up->serializedPlan.data();
+}
+
+const char *univPlanGetJsonFormatedPlan(UnivPlanC *up) {
+  up->debugString = up->upb->getJsonFormatedPlan();
+  return up->debugString.c_str();
+}
+
+void univPlanSetError(UnivPlanCatchedError *ce, int errCode,
+                      const char *errMsg) {
+  assert(ce != nullptr);
+  ce->errCode = errCode;
+  snprintf(ce->errMessage, strlen(errMsg) + 1, "%s", errMsg);
+}
+
+void univPlanNewExpr(UnivPlanC *up) {
+  up->curExpr.reset(new univplan::UnivPlanBuilderExprTree);
+}
+
+void univPlanQualListAddExpr(UnivPlanC *up) {
+  assert(up->curExpr);
+  up->curNode->addQualList(std::move(up->curExpr));
+}
+
+void univPlanInitplanAddExpr(UnivPlanC *up) {
+  assert(up->curExpr);
+  up->curNode->addInitplan(std::move(up->curExpr));
+}
+
+void univPlanTargetListAddTargetEntry(UnivPlanC *up, bool resJunk) {
+  assert(up->curExpr);
+  univplan::UnivPlanBuilderTargetEntry::uptr te =
+      up->curNode->addTargetEntryAndGetBuilder();
+  te->setResJunk(resJunk);
+  te->setExpr(std::move(up->curExpr));
+}
+
+void univPlanConnectorAddHashExpr(UnivPlanC *up) {
+  assert(up->curExpr);
+  // todo assert curNode is connector
+  dynamic_cast<univplan::UnivPlanBuilderConnector *>(up->curNode.get())
+      ->addHashExpr(std::move(up->curExpr));
+}
+
+void univPlanLimitAddLimitOffset(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderLimit *>(up->curNode.get())
+      ->setLimitOffset(std::move(up->curExpr));
+}
+
+void univPlanLimitAddLimitCount(UnivPlanC *up) {
+  assert(up->curExpr);
+  dynamic_cast<univplan::UnivPlanBuilderLimit *>(up->curNode.get())
+      ->setLimitCount(std::move(up->curExpr));
+}
+
+int32_t univPlanExprAddConst(UnivPlanC *up, int32_t pid, int32_t type,
+                             bool isNull, const char *buffer, int64_t typeMod) {
+  assert(up->curExpr);
+  auto constVal =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderConst>(pid);
+  constVal->setType(type).setTypeMod(typeMod).setIsNull(isNull);
+  if (!isNull) constVal->setValue(buffer);
+
+  int32_t uid = constVal->uid;
+  up->curExpr->addExprNode(std::move(constVal));
+  return uid;
+}
+
+int32_t univPlanExprAddVar(UnivPlanC *up, int32_t pid, uint32_t varNo,
+                           int32_t varAttNo, int32_t typeId, int64_t typeMod) {
+  assert(up->curExpr);
+  auto var = up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderVar>(pid);
+  var->setVarNo(varNo).setVarAttNo(varAttNo).setTypeId(typeId).setTypeMod(
+      typeMod);
+
+  int32_t uid = var->uid;
+  up->curExpr->addExprNode(std::move(var));
+  return uid;
+}
+
+int32_t univPlanExprAddOpExpr(UnivPlanC *up, int32_t pid, int32_t funcId) {
+  assert(up->curExpr);
+  auto opExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderOpExpr>(pid);
+  opExpr->setFuncId(funcId);
+  opExpr->setRetType(
+      dbcommon::Func::instance()
+          ->getFuncEntryById(static_cast<dbcommon::FuncKind>(funcId))
+          ->retType);
+
+  int32_t uid = opExpr->uid;
+  up->curExpr->addExprNode(std::move(opExpr));
+  return uid;
+}
+
+int32_t univPlanExprAddFuncExpr(UnivPlanC *up, int32_t pid, int32_t funcId) {
+  assert(up->curExpr);
+  auto funcExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderFuncExpr>(pid);
+  funcExpr->setFuncId(funcId);
+  funcExpr->setRetType(
+      dbcommon::Func::instance()
+          ->getFuncEntryById(static_cast<dbcommon::FuncKind>(funcId))
+          ->retType);
+
+  int32_t uid = funcExpr->uid;
+  up->curExpr->addExprNode(std::move(funcExpr));
+  return uid;
+}
+
+// Add type cast for final stage AGG function, in order to keep compatible with
+// HAWQ's optimizer and executor
+static int32_t univPlanAggrefAddFinalStageTypeCast(
+    UnivPlanC *up, int32_t pid, const dbcommon::AggEntry *aggEnt) {
+  if (aggEnt->aggFnId == dbcommon::FuncKind::AVG_SMALLINT ||
+      aggEnt->aggFnId == dbcommon::FuncKind::AVG_INT ||
+      aggEnt->aggFnId == dbcommon::FuncKind::AVG_BIGINT)
+    return univPlanExprAddFuncExpr(up, pid,
+                                   dbcommon::FuncKind::DOUBLE_TO_DECIMAL);
+  if (aggEnt->aggFnId == dbcommon::FuncKind::SUM_BIGINT)
+    return univPlanExprAddFuncExpr(up, pid,
+                                   dbcommon::FuncKind::BIGINT_TO_DECIMAL);
+  if (aggEnt->aggFnId == dbcommon::FuncKind::SUM_FLOAT)
+    return univPlanExprAddFuncExpr(up, pid,
+                                   dbcommon::FuncKind::DOUBLE_TO_FLOAT);
+  return pid;
+}
+
+int32_t univPlanAggrefAddPartialStage(UnivPlanC *up, int32_t pid,
+                                      int32_t funcId) {
+  assert(up->curExpr);
+  auto aggref =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderAggref>(pid);
+  const dbcommon::AggEntry *aggEntry =
+      dbcommon::Func::instance()->getAggEntryById(
+          static_cast<dbcommon::FuncKind>(funcId));
+  dbcommon::TypeKind retType = dbcommon::Func::instance()
+                                   ->getFuncEntryById(aggEntry->aggTransFnId)
+                                   ->retType;
+  aggref->setFuncId(funcId)
+      .setTransFuncId(aggEntry->aggTransFnId)
+      .setRetType(retType)
+      .setTransInitVal(aggEntry->aggInitVal);
+
+  int32_t uid = aggref->uid;
+  up->curExpr->addExprNode(std::move(aggref));
+  return uid;
+}
+
+int32_t univPlanAggrefAddIntermediateStage(UnivPlanC *up, int32_t pid,
+                                           int32_t funcId) {
+  assert(up->curExpr);
+  auto aggref =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderAggref>(pid);
+  const dbcommon::AggEntry *aggEntry =
+      dbcommon::Func::instance()->getAggEntryById(
+          static_cast<dbcommon::FuncKind>(funcId));
+  dbcommon::TypeKind retType = dbcommon::Func::instance()
+                                   ->getFuncEntryById(aggEntry->aggPrelimFnId)
+                                   ->retType;
+  aggref->setFuncId(funcId)
+      .setTransFuncId(aggEntry->aggPrelimFnId)
+      .setFinalFuncId(dbcommon::FuncKind::FUNCINVALID)
+      .setRetType(retType)
+      .setTransInitVal(aggEntry->aggInitVal);
+
+  int32_t uid = aggref->uid;
+  up->curExpr->addExprNode(std::move(aggref));
+  return uid;
+}
+
+int32_t univPlanAggrefAddFinalStage(UnivPlanC *up, int32_t pid,
+                                    int32_t funcId) {
+  assert(up->curExpr);
+  auto aggref =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderAggref>(pid);
+  const dbcommon::AggEntry *aggEntry =
+      dbcommon::Func::instance()->getAggEntryById(
+          static_cast<dbcommon::FuncKind>(funcId));
+  dbcommon::TypeKind retType =
+      dbcommon::Func::instance()
+          ->getFuncEntryById(aggEntry->aggFinalFn ==
+                                     dbcommon::FuncKind::FUNCINVALID
+                                 ? aggEntry->aggTransFnId
+                                 : aggEntry->aggFinalFn)
+          ->retType;
+  aggref->setFuncId(funcId)
+      .setTransFuncId(aggEntry->aggPrelimFnId)
+      .setFinalFuncId(aggEntry->aggFinalFn)
+      .setRetType(retType)
+      .setTransInitVal(aggEntry->aggInitVal);
+  if (aggEntry->aggFinalFn != dbcommon::FuncKind::FUNCINVALID)
+    aggref->setRetType(dbcommon::Func::instance()
+                           ->getFuncEntryById(aggEntry->aggFinalFn)
+                           ->retType);
+
+  int32_t uid = aggref->uid;
+  aggref->pid = univPlanAggrefAddFinalStageTypeCast(up, pid, aggEntry);
+  up->curExpr->addExprNode(std::move(aggref));
+  return uid;
+}
+
+int32_t univPlanAggrefAddOneStage(UnivPlanC *up, int32_t pid, int32_t funcId) {
+  assert(up->curExpr);
+  auto aggref =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderAggref>(pid);
+  const dbcommon::AggEntry *aggEntry =
+      dbcommon::Func::instance()->getAggEntryById(
+          static_cast<dbcommon::FuncKind>(funcId));
+  dbcommon::TypeKind retType =
+      dbcommon::Func::instance()
+          ->getFuncEntryById(aggEntry->aggFinalFn ==
+                                     dbcommon::FuncKind::FUNCINVALID
+                                 ? aggEntry->aggTransFnId
+                                 : aggEntry->aggFinalFn)
+          ->retType;
+  aggref->setFuncId(funcId)
+      .setTransFuncId(aggEntry->aggTransFnId)
+      .setFinalFuncId(aggEntry->aggFinalFn)
+      .setRetType(retType)
+      .setTransInitVal(aggEntry->aggInitVal);
+
+  int32_t uid = aggref->uid;
+  aggref->pid = univPlanAggrefAddFinalStageTypeCast(up, pid, aggEntry);
+  up->curExpr->addExprNode(std::move(aggref));
+  return uid;
+}
+
+int32_t univPlanAggrefAddProxyVar(UnivPlanC *up, int32_t pid, int32_t varAttNo,
+                                  int32_t funcId, int64_t typeMod) {
+  assert(up->curExpr);
+  auto var = up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderVar>(pid);
+  const dbcommon::AggEntry *aggEntry =
+      dbcommon::Func::instance()->getAggEntryById(
+          static_cast<dbcommon::FuncKind>(funcId));
+  dbcommon::TypeKind retType = dbcommon::Func::instance()
+                                   ->getFuncEntryById(aggEntry->aggTransFnId)
+                                   ->retType;
+  var->setVarNo(OUTER_VAR).setVarAttNo(varAttNo).setTypeId(retType).setTypeMod(
+      typeMod);
+
+  int32_t uid = var->uid;
+  up->curExpr->addExprNode(std::move(var));
+  return uid;
+}
+
+int32_t univPlanExprAddParam(UnivPlanC *up, int32_t pid,
+                             UnivplanParamKind paramKind, int32_t paramId,
+                             int32_t typeId, int64_t typeMod) {
+  assert(up->curExpr);
+  auto param =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderParam>(pid);
+  param->setParamKind(static_cast<univplan::PARAMKIND>(paramKind))
+      .setParamId(paramId)
+      .setTypeId(typeId)
+      .setTypeMod(typeMod);
+
+  int32_t uid = param->uid;
+  up->curExpr->addExprNode(std::move(param));
+  return uid;
+}
+
+int32_t univPlanExprAddSubPlan(UnivPlanC *up, int32_t pid,
+                               UnivplanSubLinkType sublinkType, int32_t planId,
+                               int32_t stageNo, int32_t typeId, int64_t typeMod,
+                               bool useHashTable, bool initPlan) {
+  assert(up->curExpr);
+  auto subplan =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderSubPlan>(pid);
+  subplan->setSubLinkType(static_cast<univplan::SUBLINKTYPE>(sublinkType))
+      .setPlanId(planId)
+      .setTypeId(typeId)
+      .setTypeMod(typeMod)
+      .setUseHashTable(useHashTable)
+      .setInitPlan(initPlan);
+
+  int32_t uid = subplan->uid;
+  up->curExpr->addExprNode(std::move(subplan));
+  if (initPlan) up->subplanStageNo[planId] = stageNo;
+  return uid;
+}
+void univPlanExprAddSubPlanTestexpr(UnivPlanC *up, int32_t subplanId) {
+  reinterpret_cast<univplan::UnivPlanBuilderSubPlan *>(
+      up->curExpr->getExprNode(subplanId))
+      ->setAddingTestexpr();
+}
+
+void univPlanAddTokenEntry(UnivPlanC *up, FileSystemCredentialCPtr tokenEntry) {
+  std::string protocol(tokenEntry->key.protocol);
+  std::string host(tokenEntry->key.host);
+  int port = tokenEntry->key.port;
+  std::string token(tokenEntry->credential);
+  up->upb->getPlanBuilderPlan()->setTokenEntry(protocol, host, port, token);
+}
+
+void univPlanAddSnapshot(UnivPlanC *up, char *snapshot, int32_t snapshot_len) {
+  std::string Snapshot(snapshot, snapshot_len);
+  up->upb->getPlanBuilderPlan()->setSnapshot(Snapshot);
+  // LOG_DEBUG("univplan c add snapshot: %s, size: %d", Snapshot.c_str(),
+  //          snapshot_len);
+}
+
+void univPlanSubPlanAddSetParam(UnivPlanC *up, int32_t subplanId, int32_t num,
+                                int32_t *setParam) {
+  univplan::UnivPlanBuilderSubPlan *builder =
+      reinterpret_cast<univplan::UnivPlanBuilderSubPlan *>(
+          up->curExpr->getExprNode(subplanId));
+  for (int32_t i = 0; i < num; ++i) {
+    builder->addSetParam(setParam[i]);
+  }
+}
+
+void univPlanSubPlanAddParParam(UnivPlanC *up, int32_t subplanId, int32_t num,
+                                int32_t *parParam) {
+  univplan::UnivPlanBuilderSubPlan *builder =
+      reinterpret_cast<univplan::UnivPlanBuilderSubPlan *>(
+          up->curExpr->getExprNode(subplanId));
+  for (int32_t i = 0; i < num; ++i) {
+    builder->addParParam(parParam[i]);
+  }
+}
+
+void univPlanSubPlanAddTestexprParam(UnivPlanC *up, int32_t subplanId,
+                                     int32_t num, int32_t *testexprParam) {
+  univplan::UnivPlanBuilderSubPlan *builder =
+      reinterpret_cast<univplan::UnivPlanBuilderSubPlan *>(
+          up->curExpr->getExprNode(subplanId));
+  for (int32_t i = 0; i < num; ++i) {
+    builder->addTestexprParam(testexprParam[i]);
+  }
+}
+
+int32_t univPlanExprAddBoolExpr(UnivPlanC *up, int32_t pid,
+                                UnivplanBoolExprType boolExprType) {
+  assert(up->curExpr);
+  auto boolExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderBoolExpr>(pid);
+  boolExpr->setType(static_cast<univplan::BOOLEXPRTYPE>(boolExprType));
+
+  int32_t uid = boolExpr->uid;
+  up->curExpr->addExprNode(std::move(boolExpr));
+  return uid;
+}
+int32_t univPlanExprAddNullTestExpr(UnivPlanC *up, int32_t pid,
+                                    UnivplanNullTestType nullTestType) {
+  assert(up->curExpr);
+  auto nullTest =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderNullTest>(pid);
+  nullTest->setType(static_cast<univplan::NULLTESTTYPE>(nullTestType));
+
+  int32_t uid = nullTest->uid;
+  up->curExpr->addExprNode(std::move(nullTest));
+  return uid;
+}
+
+int32_t univPlanExprAddBoolTestExpr(UnivPlanC *up, int32_t pid,
+                                    UnivplanBooleanTestType boolTestType) {
+  assert(up->curExpr);
+  auto boolTest =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderBooleanTest>(pid);
+  boolTest->setType(static_cast<univplan::BOOLTESTTYPE>(boolTestType));
+
+  int32_t uid = boolTest->uid;
+  up->curExpr->addExprNode(std::move(boolTest));
+  return uid;
+}
+
+int32_t univPlanExprAddCaseExpr(UnivPlanC *up, int32_t pid, int32_t casetype) {
+  auto caseexpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderCaseExpr>(pid);
+  caseexpr->setCasetype(casetype);
+
+  int32_t uid = caseexpr->uid;
+  up->curExpr->addExprNode(std::move(caseexpr));
+  return uid;
+}
+
+void univPlanExprAddCaseExprDefresult(UnivPlanC *up, int32_t caseexpr_id) {
+  reinterpret_cast<univplan::UnivPlanBuilderCaseExpr *>(
+      up->curExpr->getExprNode(caseexpr_id))
+      ->setAddingDefresult();
+}
+
+int32_t univPlanExprAddCaseWhen(UnivPlanC *up, int32_t pid) {
+  auto casewhen =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderCaseWhen>(pid);
+
+  int32_t uid = casewhen->uid;
+  up->curExpr->addExprNode(std::move(casewhen));
+  return uid;
+}
+
+void univPlanExprAddCaseWhenExpr(UnivPlanC *up, int32_t casewhen_id) {}
+
+void univPlanExprAddCaseWhenResult(UnivPlanC *up, int32_t casewhen_id) {
+  reinterpret_cast<univplan::UnivPlanBuilderCaseWhen *>(
+      up->curExpr->getExprNode(casewhen_id))
+      ->setAddingResult();
+}
+
+int32_t univPlanExprAddScalarArrayOpExpr(UnivPlanC *up, int32_t pid,
+                                         int32_t funcId, bool useOr) {
+  assert(up->curExpr);
+  auto opExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderScalarArrayOpExpr>(
+          pid);
+  opExpr->setFuncId(funcId).setUseOr(useOr);
+
+  int32_t uid = opExpr->uid;
+  up->curExpr->addExprNode(std::move(opExpr));
+  return uid;
+}
+
+int32_t univPlanExprAddCoalesceExpr(UnivPlanC *up, int32_t pid,
+                                    int32_t coalesceType,
+                                    int32_t coalesceTypeMod) {
+  auto coalesceExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderCoalesceExpr>(pid);
+  coalesceExpr->setCoalesceType(coalesceType)
+      .setCoalesceTypeMod(coalesceTypeMod);
+  int32_t uid = coalesceExpr->uid;
+  up->curExpr->addExprNode(std::move(coalesceExpr));
+  return uid;
+}
+
+int32_t univPlanExprAddNullIfExpr(UnivPlanC *up, int32_t pid, int32_t funcId,
+                                  int32_t retType, int32_t typeMod) {
+  assert(up->curExpr);
+  auto nullIfExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderNullIfExpr>(pid);
+  nullIfExpr->setFuncId(funcId);
+  nullIfExpr->setRetType(retType);
+  nullIfExpr->setTypeMod(typeMod);
+
+  int32_t uid = nullIfExpr->uid;
+  up->curExpr->addExprNode(std::move(nullIfExpr));
+  return uid;
+}
+
+int32_t univPlanExprAddDistinctExpr(UnivPlanC *up, int32_t pid,
+                                    int32_t funcId) {
+  assert(up->curExpr);
+  auto distinctExpr =
+      up->curExpr->ExprNodeFactory<univplan::UnivPlanBuilderDistinctExpr>(pid);
+  distinctExpr->setFuncId(funcId);
+  distinctExpr->setRetType(
+      dbcommon::Func::instance()
+          ->getFuncEntryById(static_cast<dbcommon::FuncKind>(funcId))
+          ->retType);
+
+  int32_t uid = distinctExpr->uid;
+  up->curExpr->addExprNode(std::move(distinctExpr));
+  return uid;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/depends/univplan/src/univplan/cwrapper/univplan-c.h b/depends/univplan/src/univplan/cwrapper/univplan-c.h
new file mode 100644
index 0000000..ae7ddfe
--- /dev/null
+++ b/depends/univplan/src/univplan/cwrapper/univplan-c.h
@@ -0,0 +1,352 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_CWRAPPER_UNIVPLAN_C_H_
+#define UNIVPLAN_SRC_UNIVPLAN_CWRAPPER_UNIVPLAN_C_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ERROR_MESSAGE_BUFFER_SIZE
+#define ERROR_MESSAGE_BUFFER_SIZE 4096
+#endif
+
+struct UnivPlanC;
+
+typedef struct UnivPlanC UnivPlanC;
+
+typedef struct FileSystemCredentialKeyC {
+  char *protocol;
+  char *host;
+  int port;
+} FileSystemCredentialKeyC;
+
+typedef struct FileSystemCredentialC {
+  struct FileSystemCredentialKeyC key;
+  char *credential;
+} FileSystemCredentialC;
+
+typedef struct FileSystemCredentialC *FileSystemCredentialCPtr;
+
+typedef struct UnivPlanCatchedError {
+  int errCode;
+  char errMessage[ERROR_MESSAGE_BUFFER_SIZE];
+} UnivPlanCatchedError;
+
+UnivPlanC *univPlanNewInstance();
+void univPlanFreeInstance(UnivPlanC **up);
+
+void univPlanNewSubPlanNode(UnivPlanC *up);
+void univPlanFreeSubPlanNode(UnivPlanC *up);
+
+// fill RangeTblEntry
+typedef enum FormatType {
+  UnivPlanTextFormat,
+  UnivPlanCsvFormat,
+  UnivPlanOrcFormat,
+  UnivPlanMagmaFormat
+} FormatType;
+void univPlanRangeTblEntryAddTable(UnivPlanC *up, uint64_t tid,
+                                   FormatType format, const char *location,
+                                   const char *optStrInJson, uint32_t columnNum,
+                                   const char **columnName,
+                                   int32_t *columnDataType,
+                                   int64_t *columnDataTypeMod);
+void univPlanRangeTblEntryAddDummy(UnivPlanC *up);
+
+// construct interconnect info
+void univPlanReceiverAddListeners(UnivPlanC *up, uint32_t listenerNum,
+                                  const char **addr, int32_t *port);
+
+// add param info
+void univPlanAddParamInfo(UnivPlanC *up, int32_t type, bool isNull,
+                          const char *buffer);
+
+void univPlanSetDoInstrument(UnivPlanC *up, bool doInstrument);
+
+void univPlanSetNCrossLevelParams(UnivPlanC *up, int32_t nCrossLevelParams);
+
+typedef enum UnivPlanCCmdType {
+  UNIVPLAN_CMD_UNKNOWN,
+  UNIVPLAN_CMD_SELECT,
+  UNIVPLAN_CMD_UPDATE,
+  UNIVPLAN_CMD_INSERT,
+  UNIVPLAN_CMD_DELETE,
+  UNIVPLAN_CMD_UTILITY,
+  UNIVPLAN_CMD_NOTHING
+} UnivPlanCCmdType;
+void univPlanSetCmdType(UnivPlanC *up, UnivPlanCCmdType type);
+
+void univPlanSetPlanNodeInfo(UnivPlanC *up, double planRows,
+                             int32_t planRowWidth, uint64_t operatorMemKB);
+
+/*
+ * the expr tree must be built before adding targetlist/qualist
+ */
+void univPlanQualListAddExpr(UnivPlanC *up);
+void univPlanInitplanAddExpr(UnivPlanC *up);
+void univPlanTargetListAddTargetEntry(UnivPlanC *up, bool resJunk);
+void univPlanConnectorAddHashExpr(UnivPlanC *up);
+void univPlanConnectorSetRangeVsegMap(UnivPlanC *up, int *map, bool magmaTable);
+// set magma range num
+void univPlanSetRangeNum(UnivPlanC *up, int rangeNum);
+
+// construct Connector
+typedef enum ConnectorType {
+  UnivPlanShuffle,
+  UnivPlanBroadcast,
+  UnivPlanConverge
+} ConnectorType;
+int32_t univPlanConnectorNewInstance(UnivPlanC *up, int32_t);
+void univPlanConnectorSetType(UnivPlanC *up, ConnectorType type);
+void univPlanConnectorSetStageNo(UnivPlanC *up, int32_t stageNo);
+void univPlanConnectorSetColIdx(UnivPlanC *up, int64_t numCols,
+                                const int32_t *colIdx);
+void univPlanConnectorSetSortFuncId(UnivPlanC *up, int64_t numCols,
+                                    const int32_t *sortFuncId);
+
+// construct ExtScan
+int32_t univPlanExtScanNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanExtScanSetRelId(UnivPlanC *up, uint32_t relId);
+void univPlanExtScanSetColumnsToRead(UnivPlanC *up, int64_t numCols,
+                                     const int32_t *columnsToRead);
+// construct magma index info
+void univPlanExtScanSetIndex(UnivPlanC *up, bool index);
+void univPlanExtScanSetScanType(UnivPlanC *up, int type);
+void univPlanExtScanDirection(UnivPlanC *up, int direction);
+void univPlanExtScanSetIndexName(UnivPlanC *up, const char *indexName);
+void univPlanIndexQualListAddExpr(UnivPlanC *up);
+
+/*void univPlanExtScanAddTaskWithFileSplits(UnivPlanC *up, uint32_t
+   fileSplitNum, int64_t *lbLen, int64_t *ubLen, const char **lowerBound, const
+   char **upperBound);
+*/
+// construct SeqScan
+int32_t univPlanSeqScanNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanSeqScanSetRelId(UnivPlanC *up, uint32_t relId);
+void univPlanSeqScanSetReadStatsOnly(UnivPlanC *up, bool readStatsOnly);
+void univPlanSeqScanSetColumnsToRead(UnivPlanC *up, int64_t numCols,
+                                     const int32_t *columnsToRead);
+void univPlanSeqScanAddTaskWithFileSplits(bool isMagma, UnivPlanC *up,
+                                          uint32_t fileSplitNum,
+                                          const char **fileName, int64_t *start,
+                                          int64_t *len, int32_t *rangeid,
+                                          int32_t *rgid);
+
+// construct Agg
+int32_t univPlanAggNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanAggSetNumGroupsAndGroupColIndexes(UnivPlanC *up, int64_t numGroups,
+                                               int64_t numCols,
+                                               const int32_t *grpColIdx);
+
+// construct Sort
+int32_t univPlanSortNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanSortSetColIdx(UnivPlanC *up, int64_t numCols,
+                           const int32_t *colIdx);
+void univPlanSortSetSortFuncId(UnivPlanC *up, int64_t numCols,
+                               const int32_t *sortFuncId);
+void univPlanSortAddLimitOffset(UnivPlanC *up);
+void univPlanSortAddLimitCount(UnivPlanC *up);
+
+// construct Limit
+int32_t univPlanLimitNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanLimitAddLimitOffset(UnivPlanC *up);
+void univPlanLimitAddLimitCount(UnivPlanC *up);
+
+// construct append
+int32_t univPlanAppendNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanAppendAddAppendPlan(UnivPlanC *up);
+
+typedef enum UnivPlanCJoinType {
+  UNIVPLAN_JOIN_INNER,
+  UNIVPLAN_JOIN_LEFT,
+  UNIVPLAN_JOIN_RIGHT,
+  UNIVPLAN_JOIN_FULL,
+  UNIVPLAN_JOIN_IN,
+  UNIVPLAN_JOIN_LASJ = 8,
+  UNIVPLAN_JOIN_LASJ_NOTIN = 9
+} UnivPlanCJoinType;
+// construct nestloop
+int32_t univPlanNestLoopNewInstance(UnivPlanC *up, int32_t pid);
+bool univPlanNestLoopSetType(UnivPlanC *up, UnivPlanCJoinType type);
+void univPlanNestLoopAddJoinQual(UnivPlanC *up);
+// construct hashjoin
+int32_t univPlanHashJoinNewInstance(UnivPlanC *up, int32_t pid);
+bool univPlanHashJoinSetType(UnivPlanC *up, UnivPlanCJoinType type);
+void univPlanHashJoinAddJoinQual(UnivPlanC *up);
+void univPlanHashJoinAddHashClause(UnivPlanC *up);
+void univPlanHashJoinAddHashQualClause(UnivPlanC *up);
+// construct mergejoin
+int32_t univPlanMergeJoinNewInstance(UnivPlanC *up, int32_t pid);
+bool univPlanMergeJoinSetType(UnivPlanC *up, UnivPlanCJoinType type);
+void univPlanMergeJoinAddJoinQual(UnivPlanC *up);
+void univPlanMergeJoinAddMergeClause(UnivPlanC *up);
+
+// construct hash
+int32_t univPlanHashNewInstance(UnivPlanC *up, int32_t pid);
+
+typedef enum UnivPlanCShareType {
+  UNIVPLAN_SHARE_NOTSHARED,
+  UNIVPLAN_SHARE_MATERIAL,
+  UNIVPLAN_SHARE_MATERIAL_XSLICE,
+  UNIVPLAN_SHARE_SORT,
+  UNIVPLAN_SHARE_SORT_XSLICE
+} UnivPlanCShareType;
+// construct material
+int32_t univPlanMaterialNewInstance(UnivPlanC *up, int32_t pid);
+bool univPlanMaterialSetAttr(UnivPlanC *up, UnivPlanCShareType type,
+                             bool cdbStrict, int32_t shareId,
+                             int32_t driverSlice, int32_t nsharer,
+                             int32_t xslice);
+
+// construct shareinputscan
+int32_t univPlanShareInputScanNewInstance(UnivPlanC *up, int32_t pid);
+bool univPlanShareInputScanSetAttr(UnivPlanC *up, UnivPlanCShareType type,
+                                   int32_t shareId, int32_t driverSlice);
+
+// construct result
+int32_t univPlanResultNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanResultAddResConstantQual(UnivPlanC *up);
+
+// construct subqueryscan
+int32_t univPlanSubqueryScanNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanSubqueryScanAddSubPlan(UnivPlanC *up);
+
+// construct Unique
+int32_t univPlanUniqueNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanUniqueSetNumGroupsAndUniqColIdxs(UnivPlanC *up, int64_t numCols,
+                                              const int32_t *uniqColIdxs);
+
+// construct Insert
+int32_t univPlanInsertNewInstance(UnivPlanC *up, int32_t pid);
+void univPlanInsertSetRelId(UnivPlanC *up, uint32_t relId);
+
+void univPlanAddToPlanNode(UnivPlanC *up, bool isLeft);
+
+void univPlanFixVarType(UnivPlanC *up);
+
+void univPlanStagize(UnivPlanC *up);
+
+void univPlanAddGuc(UnivPlanC *up, const char *name, const char *value);
+
+const char *univPlanSerialize(UnivPlanC *up, int32_t *size, bool compress);
+
+UnivPlanCatchedError *univPlanGetLastError(UnivPlanC *up);
+
+// call before add expr node
+void univPlanNewExpr(UnivPlanC *up);
+
+// following functions return the id of the new expr node in the expr tree
+// which will be used as the pid for adding its argument in the expr tree
+int32_t univPlanExprAddConst(UnivPlanC *up, int32_t pid, int32_t type,
+                             bool isNull, const char *buffer, int64_t typeMod);
+int32_t univPlanExprAddVar(UnivPlanC *up, int32_t pid, uint32_t varNo,
+                           int32_t varAttNo, int32_t typeId, int64_t typeMod);
+int32_t univPlanExprAddOpExpr(UnivPlanC *up, int32_t pid, int32_t funcId);
+int32_t univPlanExprAddFuncExpr(UnivPlanC *up, int32_t pid, int32_t funcId);
+int32_t univPlanAggrefAddPartialStage(UnivPlanC *up, int32_t pid,
+                                      int32_t funcId);
+int32_t univPlanAggrefAddIntermediateStage(UnivPlanC *up, int32_t pid,
+                                           int32_t funcId);
+int32_t univPlanAggrefAddFinalStage(UnivPlanC *up, int32_t pid, int32_t funcId);
+int32_t univPlanAggrefAddOneStage(UnivPlanC *up, int32_t pid, int32_t funcId);
+int32_t univPlanAggrefAddProxyVar(UnivPlanC *up, int32_t pid, int32_t varAttNo,
+                                  int32_t funcId, int64_t typeMod);
+typedef enum { UNIVPLAN_PARAM_EXTERN, UNIVPLAN_PARAM_EXEC } UnivplanParamKind;
+int32_t univPlanExprAddParam(UnivPlanC *up, int32_t pid,
+                             UnivplanParamKind paramKind, int32_t paramId,
+                             int32_t typeId, int64_t typeMod);
+typedef enum {
+  UNIVPLAN_EXISTS_SUBLINK = 0,
+  UNIVPLAN_ALL_SUBLINK = 1,
+  UNIVPLAN_ANY_SUBLINK = 2,
+  UNIVPLAN_ROWCOMPARE_SUBLINK = 3,
+  UNIVPLAN_EXPR_SUBLINK = 4,
+  UNIVPLAN_ARRAY_SUBLINK = 5,
+  UNIVPLAN_NOT_EXISTS_SUBLINK = 6
+} UnivplanSubLinkType;
+int32_t univPlanExprAddSubPlan(UnivPlanC *up, int32_t pid,
+                               UnivplanSubLinkType sublinkType, int32_t planId,
+                               int32_t stageNo, int32_t typeId, int64_t typeMod,
+                               bool useHashTable, bool initPlan);
+void univPlanExprAddSubPlanTestexpr(UnivPlanC *up, int32_t subplanId);
+
+void univPlanAddTokenEntry(UnivPlanC *up, FileSystemCredentialCPtr tokenEntry);
+void univPlanAddSnapshot(UnivPlanC *up, char *snapshot, int32_t snapshot_len);
+void univPlanSubPlanAddSetParam(UnivPlanC *up, int32_t subplanId, int32_t num,
+                                int32_t *setParam);
+void univPlanSubPlanAddParParam(UnivPlanC *up, int32_t subplanId, int32_t num,
+                                int32_t *parParam);
+void univPlanSubPlanAddTestexprParam(UnivPlanC *up, int32_t subplanId,
+                                     int32_t num, int32_t *testexprParam);
+
+typedef enum {
+  UNIVPLAN_BOOLEXPRTYPE_AND_EXPR,
+  UNIVPLAN_BOOLEXPRTYPE_OR_EXPR,
+  UNIVPLAN_BOOLEXPRTYPE_NOT_EXPR
+} UnivplanBoolExprType;
+int32_t univPlanExprAddBoolExpr(UnivPlanC *up, int32_t pid,
+                                UnivplanBoolExprType boolExprType);
+typedef enum {
+  UNIVPLAN_NULLTESTTYPE_IS_NULL,
+  UNIVPLAN_NULLTESTTYPE_IS_NOT_NULL
+} UnivplanNullTestType;
+int32_t univPlanExprAddNullTestExpr(UnivPlanC *up, int32_t pid,
+                                    UnivplanNullTestType nullTestType);
+
+typedef enum {
+  UNIVPLAN_BOOLEANTESTTYPE_IS_TRUE,
+  UNIVPLAN_BOOLEANTESTTYPE_IS_NOT_TRUE,
+  UNIVPLAN_BOOLEANTESTTYPE_IS_FALSE,
+  UNIVPLAN_BOOLEANTESTTYPE_IS_NOT_FALSE,
+  UNIVPLAN_BOOLEANTESTTYPE_IS_UNKNOWN,
+  UNIVPLAN_BOOLEANTESTTYPE_IS_NOT_UNKNOWN
+} UnivplanBooleanTestType;
+int32_t univPlanExprAddBoolTestExpr(UnivPlanC *up, int32_t pid,
+                                    UnivplanBooleanTestType boolTestType);
+
+int32_t univPlanExprAddCaseExpr(UnivPlanC *up, int32_t pid, int32_t casetype);
+void univPlanExprAddCaseExprDefresult(UnivPlanC *up, int32_t caseexpr_id);
+int32_t univPlanExprAddCaseWhen(UnivPlanC *up, int32_t pid);
+void univPlanExprAddCaseWhenExpr(UnivPlanC *up, int32_t casewhen_id);
+void univPlanExprAddCaseWhenResult(UnivPlanC *up, int32_t casewhen_id);
+
+int32_t univPlanExprAddScalarArrayOpExpr(UnivPlanC *up, int32_t pid,
+                                         int32_t funcId, bool useOr);
+
+int32_t univPlanExprAddCoalesceExpr(UnivPlanC *up, int32_t pid,
+                                    int32_t coalesceType,
+                                    int32_t coalesceTypeMod);
+
+int32_t univPlanExprAddNullIfExpr(UnivPlanC *up, int32_t pid, int32_t funcId,
+                                  int32_t retType, int32_t typeMod);
+
+int32_t univPlanExprAddDistinctExpr(UnivPlanC *up, int32_t pid, int32_t funcId);
+
+// debug
+const char *univPlanGetJsonFormatedPlan(UnivPlanC *up);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_CWRAPPER_UNIVPLAN_C_H_
diff --git a/depends/univplan/src/univplan/minmax/minmax-predicates.cc b/depends/univplan/src/univplan/minmax/minmax-predicates.cc
new file mode 100644
index 0000000..84bbc79
--- /dev/null
+++ b/depends/univplan/src/univplan/minmax/minmax-predicates.cc
@@ -0,0 +1,596 @@
+/*
+ * 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.
+ */
+
+#include "univplan/minmax/minmax-predicates.h"
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "dbcommon/log/logger.h"
+#include "dbcommon/type/type-util.h"
+#include "dbcommon/utils/string-util.h"
+
+#include "univplan/common/plannode-walker.h"
+
+namespace univplan {
+
+ListPredicate::ListPredicate(const univplan::UnivPlanExprPolyList* exprs,
+                             const MinMaxPredicatesAbstract* pred)
+    : PredicateOper(pred) {
+  for (int i = 0; i < exprs->size(); ++i) {
+    children.push_back(
+        MinMaxPredicatesAbstract::buildPredicateOper(&exprs->Get(i), pred));
+  }
+}
+
+bool ListPredicate::canDrop() {
+  // behave as And
+  for (PredicateOper::uptr& child : children) {
+    if (child->canDrop()) return true;
+  }
+  return false;
+}
+
+BooleanPredicate::BooleanPredicate(const MinMaxPredicatesAbstract* pred,
+                                   const univplan::UnivPlanBoolExpr* op)
+    : PredicateOper(pred) {
+  for (int i = 0; i < op->args_size(); ++i) {
+    children.push_back(
+        MinMaxPredicatesAbstract::buildPredicateOper(&op->args(i), pred));
+  }
+}
+
+bool VarPredicate::canDrop() {
+  if (PREDPAGE(pred)->hasAllNull(var->varattno())) return true;
+  PredicateStats s = PREDPAGE(pred)->getMinMax(var->varattno());
+  if (s.hasMinMax && !dbcommon::DatumGetValue<bool>(s.maxValue.value))
+    return true;
+  return false;
+}
+
+bool IsTrueBooleanTestPredicate::canDrop() {
+  if (PREDPAGE(pred)->hasAllNull(var->varattno())) return true;
+  PredicateStats s = PREDPAGE(pred)->getMinMax(var->varattno());
+  if (s.hasMinMax && !dbcommon::DatumGetValue<bool>(s.maxValue.value))
+    return true;
+  return false;
+}
+
+bool IsNotTrueBooleanTestPredicate::canDrop() {
+  if (!PREDPAGE(pred)->hasNull(var->varattno())) {
+    PredicateStats s = PREDPAGE(pred)->getMinMax(var->varattno());
+    if (s.hasMinMax && dbcommon::DatumGetValue<bool>(s.minValue.value))
+      return true;
+  }
+  return false;
+}
+
+bool IsFalseBooleanTestPredicate::canDrop() {
+  if (PREDPAGE(pred)->hasAllNull(var->varattno())) return true;
+  PredicateStats s = PREDPAGE(pred)->getMinMax(var->varattno());
+  if (s.hasMinMax && dbcommon::DatumGetValue<bool>(s.minValue.value))
+    return true;
+  return false;
+}
+
+bool IsNotFalseBooleanTestPredicate::canDrop() {
+  if (!PREDPAGE(pred)->hasNull(var->varattno())) {
+    PredicateStats s = PREDPAGE(pred)->getMinMax(var->varattno());
+    if (s.hasMinMax && !dbcommon::DatumGetValue<bool>(s.maxValue.value))
+      return true;
+  }
+  return false;
+}
+
+bool IsUnknownBooleanTestPredicate::canDrop() {
+  if (!PREDPAGE(pred)->hasNull(var->varattno())) return true;
+  return false;
+}
+
+bool IsNotUnknownBooleanTestPredicate::canDrop() {
+  if (PREDPAGE(pred)->hasAllNull(var->varattno())) return true;
+  return false;
+}
+
+bool AndPredicate::canDrop() {
+  // as long as one branch is OK to drop, we can drop it.
+  for (PredicateOper::uptr& child : children) {
+    if (child->canDrop()) return true;
+  }
+  return false;
+}
+
+bool OrPredicate::canDrop() {
+  // as long as one branch is NOT ok to drop, we can NOT drop it.
+  for (PredicateOper::uptr& child : children) {
+    if (!child->canDrop()) return false;
+  }
+  return true;
+}
+
+bool IsNullPredicate::canDrop() {
+  // as long as one var has null, we can't drop it
+  for (auto index : varAttrNo) {
+    if (PREDPAGE(pred)->hasNull(index)) return false;
+  }
+  return true;
+}
+
+bool IsNotNullPredicate::canDrop() {
+  // if one var has all null, we can drop it
+  for (auto index : varAttrNo) {
+    if (PREDPAGE(pred)->hasAllNull(index)) return true;
+  }
+  return false;
+}
+
+dbcommon::TupleBatch::uptr CompPredicate::buildTupleBatch(int32_t argIndex) {
+  dbcommon::TupleBatch::uptr batch(
+      new dbcommon::TupleBatch(*PREDPAGE(pred)->getTupleDesc(), true));
+  dbcommon::TupleBatchWriter& writer = batch->getTupleBatchWriter();
+  for (uint32_t i = 0; i < batch->getNumOfColumns(); ++i) {
+    if (varAttNo[argIndex].size() == 1 && i == varAttNo[argIndex][0] - 1) {
+      PredicateStats s =
+          PREDPAGE(pred)->getMinMax(i + 1, &minTimestamp, &maxTimestamp);
+      if (!s.hasMinMax) return nullptr;
+      writer[i]->append(&s.minValue);
+      writer[i]->append(&s.maxValue);
+    }
+  }
+  batch->incNumOfRows(2);
+  return std::move(batch);
+}
+
+PredicateStats::uptr CompPredicate::calcLeft() { return doCalc(0); }
+
+PredicateStats::uptr CompPredicate::calcRight() { return doCalc(1); }
+
+PredicateStats::uptr CompPredicate::doCalc(int32_t argIndex) {
+  PredicateStats::uptr stats(new PredicateStats);
+
+  if (varAttNo[argIndex].size() == 1 &&
+      PREDPAGE(pred)->hasAllNull(varAttNo[argIndex][0])) {
+    stats->hasAllNull = true;
+    return std::move(stats);
+  }
+
+  tbVec_.push_back(buildTupleBatch(argIndex));
+  if (!tbVec_[argIndex]) return nullptr;
+  ExprContext context;
+  context.scanBatch = tbVec_[argIndex].get();
+  dbcommon::Object* obj = dbcommon::DatumGetValue<dbcommon::Object*>(
+      args[argIndex]->calc(&context));
+  dbcommon::Vector* vec = dynamic_cast<dbcommon::Vector*>(obj);
+  if (!vec) {
+    dbcommon::Scalar* scalar = dynamic_cast<dbcommon::Scalar*>(obj);
+    stats->maxValue = *scalar;
+    stats->minValue = *scalar;
+  } else {
+    dbcommon::Scalar s1;
+    dbcommon::Scalar s2;
+    vec->readPlainScalar(0, &s1);
+    vec->readPlainScalar(1, &s2);
+    int32_t comRes =
+        compareTo(s1, typeKinds[argIndex], s2, typeKinds[argIndex]);
+    stats->maxValue = comRes >= 0 ? s1 : s2;
+    stats->minValue = comRes <= 0 ? s1 : s2;
+  }
+  return std::move(stats);
+}
+
+bool CompPredicate::isValidToPredicate() const {
+  // for arg1 op arg2, currently we only allow at most one variable
+  // exists in each side, and the two vars can't be the same one
+  if (varAttNo[0].size() > 1 || varAttNo[1].size() > 1) return false;
+
+  if (varAttNo[0].size() == 1 && varAttNo[1].size() == 1 &&
+      varAttNo[0][0] == varAttNo[1][0])
+    return false;
+
+  return true;
+}
+
+std::string CompPredicate::covertPredicateTypeStr(std::string typeName) {
+  if (typeName == "date")
+    return "int32";
+  else if (typeName == "time")
+    return "int64";
+  else if (typeName == "bpchar" || typeName == "varchar")
+    return "string";
+  return typeName;
+}
+
+int32_t CompPredicate::compareTo(const dbcommon::Scalar& s1,
+                                 dbcommon::TypeKind t1,
+                                 const dbcommon::Scalar& s2,
+                                 dbcommon::TypeKind t2) {
+  std::string type1 =
+      covertPredicateTypeStr(dbcommon::TypeUtil::getTypeNameById(t1));
+  std::string type2 =
+      covertPredicateTypeStr(dbcommon::TypeUtil::getTypeNameById(t2));
+  std::string funcName = type1 + "_less_than_" + type2;
+  const dbcommon::FuncEntry* funcEntry =
+      dbcommon::Func::instance()->getFuncEntryByName(funcName);
+  if (!funcEntry)
+    LOG_ERROR(ERRCODE_INTERNAL_ERROR, "func name %s not found in func system",
+              funcName.c_str());
+  std::unique_ptr<dbcommon::Object> retval(new dbcommon::Scalar);
+  std::unique_ptr<dbcommon::Invoker> invoker(
+      new dbcommon::Invoker(funcEntry->funcId));
+  invoker->resetPrarmeter();
+  invoker->addParam(dbcommon::CreateDatum<dbcommon::Object*>(retval.get()));
+  invoker->addParam(dbcommon::CreateDatum<const dbcommon::Object*>(&s1));
+  invoker->addParam(dbcommon::CreateDatum<const dbcommon::Object*>(&s2));
+  invoker->invoke();
+  if (dbcommon::DatumGetValue<bool>(
+          dynamic_cast<dbcommon::Scalar*>(retval.get())->value))
+    return -1;
+
+  funcName = type1 + "_equal_" + type2;
+  funcEntry = dbcommon::Func::instance()->getFuncEntryByName(funcName);
+  if (!funcEntry)
+    LOG_ERROR(ERRCODE_INTERNAL_ERROR, "func name %s not found in func system",
+              funcName.c_str());
+  invoker.reset(new dbcommon::Invoker(funcEntry->funcId));
+  invoker->resetPrarmeter();
+  invoker->addParam(dbcommon::CreateDatum<dbcommon::Object*>(retval.get()));
+  invoker->addParam(dbcommon::CreateDatum<const dbcommon::Object*>(&s1));
+  invoker->addParam(dbcommon::CreateDatum<const dbcommon::Object*>(&s2));
+  invoker->invoke();
+  if (dbcommon::DatumGetValue<bool>(
+          dynamic_cast<dbcommon::Scalar*>(retval.get())->value))
+    return 0;
+
+  return 1;
+}
+
+bool EqualPredicate::canDrop() {
+  if (!isValidToPredicate()) return false;
+
+  PredicateStats::uptr leftStat = this->calcLeft();
+  PredicateStats::uptr rightStat = this->calcRight();
+
+  // left or right has no statistics
+  if (leftStat == nullptr || rightStat == nullptr) return false;
+
+  // if either side is ALL null, can drop
+  if (leftStat->hasAllNull || rightStat->hasAllNull) return true;
+
+  // can drop when left's max < right's min, or right's max < left's min
+  if (compareTo(leftStat->maxValue, typeKinds[0], rightStat->minValue,
+                typeKinds[1]) < 0 ||
+      compareTo(rightStat->maxValue, typeKinds[1], leftStat->minValue,
+                typeKinds[0]) < 0)
+    return true;
+
+  // now check bloom filter
+  int32_t columnId;
+  PredicateStats* stat;
+  dbcommon::TypeKind type;
+  if (expr->args(0).has_var() && expr->args(1).has_val()) {
+    columnId = expr->args(0).var().varattno();
+    stat = rightStat.get();
+    type = typeKinds[1];
+  } else if (expr->args(1).has_var() && expr->args(0).has_val()) {
+    columnId = expr->args(1).var().varattno();
+    stat = leftStat.get();
+    type = typeKinds[0];
+  } else {
+    return false;
+  }
+
+  if (PREDPAGE(pred)->canDropByBloomFilter(columnId, stat, type)) return true;
+
+  return false;
+}
+
+bool NEPredicate::canDrop() {
+  if (!isValidToPredicate()) return false;
+
+  PredicateStats::uptr leftStat = this->calcLeft();
+  PredicateStats::uptr rightStat = this->calcRight();
+
+  // left or right has no statistics
+  if (leftStat == nullptr || rightStat == nullptr) return false;
+
+  // if either side is ALL null, can drop
+  if (leftStat->hasAllNull || rightStat->hasAllNull) return true;
+
+  // can drop when there is only one unique value.
+  if (compareTo(leftStat->minValue, typeKinds[0], leftStat->maxValue,
+                typeKinds[0]) == 0 &&
+      compareTo(rightStat->minValue, typeKinds[1], rightStat->maxValue,
+                typeKinds[1]) == 0 &&
+      compareTo(leftStat->maxValue, typeKinds[0], rightStat->maxValue,
+                typeKinds[1]) == 0)
+    return true;
+  else
+    return false;
+}
+
+bool GTPredicate::canDrop() {
+  if (!isValidToPredicate()) return false;
+
+  PredicateStats::uptr leftStat = this->calcLeft();
+  PredicateStats::uptr rightStat = this->calcRight();
+
+  // left or right has no statistics
+  if (leftStat == nullptr || rightStat == nullptr) return false;
+
+  // if either side is ALL null, can drop
+  if (leftStat->hasAllNull || rightStat->hasAllNull) return true;
+
+  // can drop when left's max <= right's min.
+  if (compareTo(leftStat->maxValue, typeKinds[0], rightStat->minValue,
+                typeKinds[1]) <= 0)
+    return true;
+  else
+    return false;
+}
+
+bool GEPredicate::canDrop() {
+  if (!isValidToPredicate()) return false;
+
+  PredicateStats::uptr leftStat = this->calcLeft();
+  PredicateStats::uptr rightStat = this->calcRight();
+
+  // left or right has no statistics
+  if (leftStat == nullptr || rightStat == nullptr) return false;
+
+  // if either side is ALL null, can drop
+  if (leftStat->hasAllNull || rightStat->hasAllNull) return true;
+
+  // can drop when left's max < right's min.
+  if (compareTo(leftStat->maxValue, typeKinds[0], rightStat->minValue,
+                typeKinds[1]) < 0) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool LTPredicate::canDrop() {
+  if (!isValidToPredicate()) return false;
+
+  PredicateStats::uptr leftStat = this->calcLeft();
+  PredicateStats::uptr rightStat = this->calcRight();
+
+  // left or right has no statistics
+  if (leftStat == nullptr || rightStat == nullptr) return false;
+
+  // if either side is ALL null, can drop
+  if (leftStat->hasAllNull || rightStat->hasAllNull) return true;
+
+  // can drop when right's max <= left's min.
+  if (compareTo(rightStat->maxValue, typeKinds[1], leftStat->minValue,
+                typeKinds[0]) <= 0)
+    return true;
+  else
+    return false;
+}
+
+bool LEPredicate::canDrop() {
+  if (!isValidToPredicate()) return false;
+
+  PredicateStats::uptr leftStat = this->calcLeft();
+  PredicateStats::uptr rightStat = this->calcRight();
+
+  // left or right has no statistics
+  if (leftStat == nullptr || rightStat == nullptr) return false;
+
+  // if either side is ALL null, can drop
+  if (leftStat->hasAllNull || rightStat->hasAllNull) return true;
+
+  // can drop when right's max < left's min.
+  if (compareTo(rightStat->maxValue, typeKinds[1], leftStat->minValue,
+                typeKinds[0]) < 0)
+    return true;
+  else
+    return false;
+}
+
+PredicateOper::uptr MinMaxPredicatesAbstract::buildPredicateOper(
+    const univplan::UnivPlanExprPolyList* exprs,
+    const MinMaxPredicatesAbstract* owner) {
+  return PredicateOper::uptr(new ListPredicate(exprs, owner));
+}
+
+PredicateOper::uptr MinMaxPredicatesAbstract::buildPredicateOper(
+    const univplan::UnivPlanExprPoly* expr,
+    const MinMaxPredicatesAbstract* owner) {
+  switch (expr->type()) {
+    case univplan::UNIVPLAN_EXPR_OPEXPR: {
+      const univplan::UnivPlanOpExpr& op = expr->opexpr();
+      CompPredicate::uptr ret;
+      std::string funcName =
+          dbcommon::Func::instance()
+              ->getFuncEntryById(static_cast<dbcommon::FuncKind>(op.funcid()))
+              ->funcName;
+      if (dbcommon::StringUtil::countReplicates(funcName, "less_than") == 1)
+        ret.reset(new LTPredicate(owner, &op));
+      else if (dbcommon::StringUtil::countReplicates(funcName, "less_eq") == 1)
+        ret.reset(new LEPredicate(owner, &op));
+      else if (dbcommon::StringUtil::countReplicates(funcName, "not_equal") ==
+               1)
+        ret.reset(new NEPredicate(owner, &op));
+      else if (dbcommon::StringUtil::countReplicates(funcName, "equal") == 1)
+        ret.reset(new EqualPredicate(owner, &op));
+      else if (dbcommon::StringUtil::countReplicates(funcName,
+                                                     "greater_than") == 1)
+        ret.reset(new GTPredicate(owner, &op));
+      else if (dbcommon::StringUtil::countReplicates(funcName, "greater_eq") ==
+               1)
+        ret.reset(new GEPredicate(owner, &op));
+      else
+        goto end;
+      for (int i = 0; i < op.args_size(); ++i)
+        ret->addArg(InitExpr(&op.args(i)));
+      return std::move(ret);
+    }
+    case univplan::UNIVPLAN_EXPR_BOOLEXPR: {
+      const univplan::UnivPlanBoolExpr& op = expr->boolexpr();
+      BooleanPredicate::uptr ret;
+      if (op.type() == univplan::BOOLEXPRTYPE::BOOLEXPRTYPE_AND_EXPR)
+        ret.reset(new AndPredicate(owner, &op));
+      else if (op.type() == univplan::BOOLEXPRTYPE::BOOLEXPRTYPE_OR_EXPR)
+        ret.reset(new OrPredicate(owner, &op));
+      else
+        goto end;
+      return std::move(ret);
+    }
+    case univplan::UNIVPLAN_EXPR_NULLTEST: {
+      const univplan::UnivPlanNullTest& op = expr->nulltest();
+      NullTestPredicate::uptr ret;
+      if (op.type() == univplan::NULLTESTTYPE::NULLTESTTYPE_IS_NULL) {
+        ret.reset(new IsNullPredicate(owner, &op));
+      } else {
+        assert(op.type() == univplan::NULLTESTTYPE::NULLTESTTYPE_IS_NOT_NULL);
+        ret.reset(new IsNotNullPredicate(owner, &op));
+      }
+      return std::move(ret);
+    }
+    case univplan::UNIVPLAN_EXPR_VAR: {
+      const univplan::UnivPlanVar& var = expr->var();
+      VarPredicate::uptr ret(new VarPredicate(owner, &var));
+      return std::move(ret);
+    }
+    case univplan::UNIVPLAN_EXPR_BOOLEANTEST: {
+      const univplan::UnivPlanBooleanTest& op = expr->booltest();
+      BooleanTestPredicate::uptr ret;
+      if (op.type() == univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_TRUE) {
+        ret.reset(new IsTrueBooleanTestPredicate(owner, &op));
+      } else if (op.type() ==
+                 univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_NOT_TRUE) {
+        ret.reset(new IsNotTrueBooleanTestPredicate(owner, &op));
+      } else if (op.type() == univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_FALSE) {
+        ret.reset(new IsFalseBooleanTestPredicate(owner, &op));
+      } else if (op.type() ==
+                 univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_NOT_FALSE) {
+        ret.reset(new IsNotFalseBooleanTestPredicate(owner, &op));
+      } else if (op.type() == univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_UNKNOWN) {
+        ret.reset(new IsUnknownBooleanTestPredicate(owner, &op));
+      } else {
+        assert(op.type() ==
+               univplan::BOOLTESTTYPE::BOOLTESTTYPE_IS_NOT_UNKNOWN);
+        ret.reset(new IsNotUnknownBooleanTestPredicate(owner, &op));
+      }
+      return std::move(ret);
+    }
+    default: {
+      // do nothing
+      break;
+    }
+  }
+end:
+  LOG_INFO("Predicate warning: expr is not predicated: %s",
+           expr->DebugString().c_str());
+  PredicateOper::uptr ret(new PredicateOper(owner));
+  return std::move(ret);
+}
+
+bool MinMaxPredicatesPage::canDrop() {
+  if (nullptr == exprs) {
+    return false;
+  }
+
+  try {
+    predicate = MinMaxPredicatesAbstract::buildPredicateOper(exprs, this);
+    return predicate->canDrop();
+  } catch (...) {
+    return false;
+  }
+}
+
+COBlockTaskList::COBlockTaskList() {}
+
+void COBlockTaskList::intersect(const COBlockTaskList& source) {
+  auto selfIter = taskList.begin();
+  auto srcIter = source.taskList.begin();
+  if (selfIter != taskList.end()) {
+    selfIter->second->resetIntersection();
+    return;  // no task to process
+  }
+  while (srcIter != source.taskList.end() && selfIter != taskList.end()) {
+    COBlockTask* selfTask = selfIter->second.get();
+    COBlockTask* srcTask = srcIter->second.get();
+    if (selfTask->taskBeginRowId > srcTask->getTaskEndRowId()) {
+      srcIter++;  // try next source task
+    } else if (srcTask->taskBeginRowId > selfTask->getTaskEndRowId()) {
+      selfIter++;
+      if (selfIter != taskList.end()) {
+        selfIter->second->resetIntersection();
+      }
+    } else {
+      selfIter->second->intersect(*srcTask);
+      srcIter++;  // try next source task
+    }
+  }
+}
+
+void COBlockTaskList::unionAll(const COBlockTaskList& source) {
+  auto selfIter = taskList.begin();
+  auto srcIter = source.taskList.begin();
+  if (selfIter != taskList.end()) {
+    selfIter->second->resetUnionAll();
+    return;  // no task to process
+  }
+  while (srcIter != source.taskList.end() && selfIter != taskList.end()) {
+    COBlockTask* selfTask = selfIter->second.get();
+    COBlockTask* srcTask = srcIter->second.get();
+    if (selfTask->beginRowId > srcTask->getTaskEndRowId()) {
+      srcIter++;  // try next source task
+    } else if (srcTask->taskBeginRowId > selfTask->getEndRowId()) {
+      selfIter++;
+      if (selfIter != taskList.end()) {
+        selfIter->second->resetUnionAll();
+      }
+    } else {
+      selfIter->second->unionAll(*srcTask);
+      srcIter++;  // try next source task
+    }
+  }
+}
+
+void MinMaxPredicatesCO::preparePredicates() {
+  predicate.reset(new ListPredicate(exprs, this));
+}
+
+void MinMaxPredicatesCO::calculate(
+    std::unordered_map<int, COBlockTaskList::uptr>* res) {
+  // prepare res
+  for (int i = 0; i < this->td->getNumOfColumns(); ++i) {
+    (*res)[i + 1].reset(new COBlockTaskList());
+  }
+
+  while (true) {
+    predicatesForPages.reset();
+    preparePredicatesForStrip();
+    if (predicatesForPages == nullptr) {
+      break;  // no more to work with
+    }
+    if (!predicatesForPages->canDrop()) {
+      for (auto iter = res->begin(); iter != res->end(); ++iter) {
+        iter->second->add(std::move(buildCOTaskFromCurrentStrip(iter->first)));
+      }
+    }
+  }
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/minmax/minmax-predicates.h b/depends/univplan/src/univplan/minmax/minmax-predicates.h
new file mode 100644
index 0000000..976b9c6
--- /dev/null
+++ b/depends/univplan/src/univplan/minmax/minmax-predicates.h
@@ -0,0 +1,604 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_MINMAX_MINMAX_PREDICATES_H_
+#define UNIVPLAN_SRC_UNIVPLAN_MINMAX_MINMAX_PREDICATES_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "dbcommon/common/tuple-batch.h"
+#include "dbcommon/common/tuple-desc.h"
+#include "dbcommon/nodes/datum.h"
+
+#include "univplan/common/expression.h"
+#include "univplan/common/statistics.h"
+#include "univplan/common/univplan-type.h"
+#include "univplan/common/var-util.h"
+
+namespace univplan {
+
+class PredicateStats {
+ public:
+  PredicateStats() {}
+  virtual ~PredicateStats() {}
+  typedef std::unique_ptr<PredicateStats> uptr;
+
+  dbcommon::Scalar maxValue;
+  dbcommon::Scalar minValue;
+  bool hasAllNull = false;
+  bool hasMinMax = false;
+};
+
+class MinMaxPredicatesAbstract;
+class MinMaxPredicatesPage;
+class MinMaxPredicatesCO;
+
+#define PREDPAGE(pred) (dynamic_cast<const MinMaxPredicatesPage*>(pred))
+#define PREDCO(pred) (dynamic_cast<const MinMaxPredicatesCO*>(pred))
+
+class PredicateOper {
+ public:
+  explicit PredicateOper(const MinMaxPredicatesAbstract* pred) : pred(pred) {}
+  virtual ~PredicateOper() {}
+
+  typedef std::unique_ptr<PredicateOper> uptr;
+
+  virtual bool canDrop() { return false; }
+
+ protected:
+  const MinMaxPredicatesAbstract* pred = nullptr;
+};
+
+class ListPredicate : public PredicateOper {
+ public:
+  ListPredicate(const univplan::UnivPlanExprPolyList* exprs,
+                const MinMaxPredicatesAbstract* pred);
+  ~ListPredicate() {}
+
+  typedef std::unique_ptr<ListPredicate> uptr;
+
+  bool canDrop() override;
+
+ private:
+  std::vector<PredicateOper::uptr> children;
+};
+
+class BooleanPredicate : public PredicateOper {
+ public:
+  BooleanPredicate(const MinMaxPredicatesAbstract* pred,
+                   const univplan::UnivPlanBoolExpr* op);
+  ~BooleanPredicate() {}
+
+  typedef std::unique_ptr<BooleanPredicate> uptr;
+
+ protected:
+  std::vector<PredicateOper::uptr> children;
+};
+
+class VarPredicate : public PredicateOper {
+ public:
+  VarPredicate(const MinMaxPredicatesAbstract* pred,
+               const univplan::UnivPlanVar* var)
+      : PredicateOper(pred), var(var) {}
+  ~VarPredicate() {}
+
+  typedef std::unique_ptr<VarPredicate> uptr;
+
+  bool canDrop() override;
+
+ private:
+  const univplan::UnivPlanVar* var;
+};
+
+class BooleanTestPredicate : public PredicateOper {
+ public:
+  BooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                       const univplan::UnivPlanBooleanTest* op)
+      : PredicateOper(pred), var(&op->arg().var()) {}
+  ~BooleanTestPredicate() {}
+
+  typedef std::unique_ptr<BooleanTestPredicate> uptr;
+
+ protected:
+  const univplan::UnivPlanVar* var;
+};
+
+class IsTrueBooleanTestPredicate : public BooleanTestPredicate {
+ public:
+  IsTrueBooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                             const univplan::UnivPlanBooleanTest* op)
+      : BooleanTestPredicate(pred, op) {}
+  ~IsTrueBooleanTestPredicate() {}
+
+  typedef std::unique_ptr<IsTrueBooleanTestPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class IsNotTrueBooleanTestPredicate : public BooleanTestPredicate {
+ public:
+  IsNotTrueBooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                                const univplan::UnivPlanBooleanTest* op)
+      : BooleanTestPredicate(pred, op) {}
+  ~IsNotTrueBooleanTestPredicate() {}
+
+  typedef std::unique_ptr<IsNotTrueBooleanTestPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class IsFalseBooleanTestPredicate : public BooleanTestPredicate {
+ public:
+  IsFalseBooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                              const univplan::UnivPlanBooleanTest* op)
+      : BooleanTestPredicate(pred, op) {}
+  ~IsFalseBooleanTestPredicate() {}
+
+  typedef std::unique_ptr<IsFalseBooleanTestPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class IsNotFalseBooleanTestPredicate : public BooleanTestPredicate {
+ public:
+  IsNotFalseBooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                                 const univplan::UnivPlanBooleanTest* op)
+      : BooleanTestPredicate(pred, op) {}
+  ~IsNotFalseBooleanTestPredicate() {}
+
+  typedef std::unique_ptr<IsNotFalseBooleanTestPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class IsUnknownBooleanTestPredicate : public BooleanTestPredicate {
+ public:
+  IsUnknownBooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                                const univplan::UnivPlanBooleanTest* op)
+      : BooleanTestPredicate(pred, op) {}
+  ~IsUnknownBooleanTestPredicate() {}
+
+  typedef std::unique_ptr<IsUnknownBooleanTestPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class IsNotUnknownBooleanTestPredicate : public BooleanTestPredicate {
+ public:
+  IsNotUnknownBooleanTestPredicate(const MinMaxPredicatesAbstract* pred,
+                                   const univplan::UnivPlanBooleanTest* op)
+      : BooleanTestPredicate(pred, op) {}
+  ~IsNotUnknownBooleanTestPredicate() {}
+
+  typedef std::unique_ptr<IsNotUnknownBooleanTestPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class AndPredicate : public BooleanPredicate {
+ public:
+  AndPredicate(const MinMaxPredicatesAbstract* pred,
+               const univplan::UnivPlanBoolExpr* op)
+      : BooleanPredicate(pred, op) {}
+  ~AndPredicate() {}
+
+  typedef std::unique_ptr<AndPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class OrPredicate : public BooleanPredicate {
+ public:
+  OrPredicate(const MinMaxPredicatesAbstract* pred,
+              const univplan::UnivPlanBoolExpr* op)
+      : BooleanPredicate(pred, op) {}
+  ~OrPredicate() {}
+
+  typedef std::unique_ptr<OrPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class NullTestPredicate : public PredicateOper {
+ public:
+  NullTestPredicate(const MinMaxPredicatesAbstract* pred,
+                    const univplan::UnivPlanNullTest* op)
+      : PredicateOper(pred) {
+    if (!op->arg().has_var())
+      LOG_ERROR(ERRCODE_FEATURE_NOT_SUPPORTED,
+                "NullTestPredicate only work for simple expression");
+    univplan::VarUtil varUtil;
+    varAttrNo = varUtil.collectVarAttNo(
+        const_cast<univplan::UnivPlanExprPoly*>(&op->arg()));
+  }
+  ~NullTestPredicate() {}
+
+  typedef std::unique_ptr<NullTestPredicate> uptr;
+
+ protected:
+  std::vector<int32_t> varAttrNo;
+};
+
+class IsNullPredicate : public NullTestPredicate {
+ public:
+  IsNullPredicate(const MinMaxPredicatesAbstract* pred,
+                  const univplan::UnivPlanNullTest* op)
+      : NullTestPredicate(pred, op) {}
+  ~IsNullPredicate() {}
+
+  typedef std::unique_ptr<IsNullPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class IsNotNullPredicate : public NullTestPredicate {
+ public:
+  IsNotNullPredicate(const MinMaxPredicatesAbstract* pred,
+                     const univplan::UnivPlanNullTest* op)
+      : NullTestPredicate(pred, op) {}
+  ~IsNotNullPredicate() {}
+
+  typedef std::unique_ptr<IsNotNullPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class CompPredicate : public PredicateOper {
+ public:
+  CompPredicate(const MinMaxPredicatesAbstract* pred,
+                const univplan::UnivPlanOpExpr* op)
+      : PredicateOper(pred), expr(op) {
+    assert(op->args_size() == 2);
+    typeKinds.push_back(univplan::PlanNodeUtil::exprType(op->args(0)));
+    typeKinds.push_back(univplan::PlanNodeUtil::exprType(op->args(1)));
+    univplan::VarUtil varUtil;
+    varAttNo.push_back(varUtil.collectVarAttNo(
+        const_cast<univplan::UnivPlanExprPoly*>(&op->args(0))));
+    varAttNo.push_back(varUtil.collectVarAttNo(
+        const_cast<univplan::UnivPlanExprPoly*>(&op->args(1))));
+  }
+  ~CompPredicate() {}
+
+  typedef std::unique_ptr<CompPredicate> uptr;
+
+  void addArg(ExprState::uptr arg) { args.push_back(std::move(arg)); }
+
+  bool isValidToPredicate() const;
+
+ protected:
+  PredicateStats::uptr calcLeft();
+  PredicateStats::uptr calcRight();
+  int32_t compareTo(const dbcommon::Scalar& s1, dbcommon::TypeKind t1,
+                    const dbcommon::Scalar& s2, dbcommon::TypeKind t2);
+
+ private:
+  dbcommon::TupleBatch::uptr buildTupleBatch(int32_t argIndex);
+  PredicateStats::uptr doCalc(int32_t argIndex);
+  std::string covertPredicateTypeStr(std::string typeName);
+
+ protected:
+  std::vector<dbcommon::TypeKind> typeKinds;
+  const univplan::UnivPlanOpExpr* expr;
+
+ private:
+  std::vector<ExprState::uptr> args;
+  std::vector<std::vector<int32_t>> varAttNo;
+  dbcommon::Timestamp minTimestamp;
+  dbcommon::Timestamp maxTimestamp;
+  std::vector<dbcommon::TupleBatch::uptr> tbVec_;
+};
+
+class EqualPredicate : public CompPredicate {
+ public:
+  EqualPredicate(const MinMaxPredicatesAbstract* pred,
+                 const univplan::UnivPlanOpExpr* op)
+      : CompPredicate(pred, op) {}
+  ~EqualPredicate() {}
+
+  typedef std::unique_ptr<EqualPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class NEPredicate : public CompPredicate {
+ public:
+  NEPredicate(const MinMaxPredicatesAbstract* pred,
+              const univplan::UnivPlanOpExpr* op)
+      : CompPredicate(pred, op) {}
+  ~NEPredicate() {}
+
+  typedef std::unique_ptr<NEPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class GTPredicate : public CompPredicate {
+ public:
+  GTPredicate(const MinMaxPredicatesAbstract* pred,
+              const univplan::UnivPlanOpExpr* op)
+      : CompPredicate(pred, op) {}
+  ~GTPredicate() {}
+
+  typedef std::unique_ptr<GTPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class GEPredicate : public CompPredicate {
+ public:
+  GEPredicate(const MinMaxPredicatesAbstract* pred,
+              const univplan::UnivPlanOpExpr* op)
+      : CompPredicate(pred, op) {}
+  ~GEPredicate() {}
+
+  typedef std::unique_ptr<GEPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class LTPredicate : public CompPredicate {
+ public:
+  LTPredicate(const MinMaxPredicatesAbstract* pred,
+              const univplan::UnivPlanOpExpr* op)
+      : CompPredicate(pred, op) {}
+  ~LTPredicate() {}
+
+  typedef std::unique_ptr<LTPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class LEPredicate : public CompPredicate {
+ public:
+  LEPredicate(const MinMaxPredicatesAbstract* pred,
+              const univplan::UnivPlanOpExpr* op)
+      : CompPredicate(pred, op) {}
+  ~LEPredicate() {}
+
+  typedef std::unique_ptr<LEPredicate> uptr;
+
+  bool canDrop() override;
+};
+
+class MinMaxPredicatesAbstract {
+ public:
+  MinMaxPredicatesAbstract(const univplan::UnivPlanExprPolyList* predicateExprs,
+                           const dbcommon::TupleDesc* tupleDesc)
+      : exprs(predicateExprs), td(tupleDesc) {}
+  virtual ~MinMaxPredicatesAbstract() {}
+
+  // facility for building general purpose predicate tree
+  static PredicateOper::uptr buildPredicateOper(
+      const univplan::UnivPlanExprPolyList* exprs,
+      const MinMaxPredicatesAbstract* owner);
+
+  static PredicateOper::uptr buildPredicateOper(
+      const univplan::UnivPlanExprPoly* expr,
+      const MinMaxPredicatesAbstract* owner);
+
+  const dbcommon::TupleDesc* getTupleDesc() const { return td; }
+
+ protected:
+  const univplan::UnivPlanExprPolyList* exprs;  // original pushed filter
+  const dbcommon::TupleDesc* td;                // full tuple desc
+  PredicateOper::uptr predicate;                // generated predicate
+};
+
+class MinMaxPredicatesPage : public MinMaxPredicatesAbstract {
+ public:
+  MinMaxPredicatesPage(const Statistics* s,
+                       const univplan::UnivPlanExprPolyList* predicateExprs,
+                       const dbcommon::TupleDesc* tupleDesc)
+      : MinMaxPredicatesAbstract(predicateExprs, tupleDesc), stripeStats(s) {}
+  virtual ~MinMaxPredicatesPage() {}
+
+  typedef std::unique_ptr<MinMaxPredicatesPage> uptr;
+
+  virtual bool canDrop();  // page level min-max calculation interface
+
+ public:
+  virtual bool hasNull(int32_t colId) const = 0;
+  virtual bool hasAllNull(int32_t colId) const = 0;
+  virtual bool canDropByBloomFilter(int32_t colId, PredicateStats* stat,
+                                    dbcommon::TypeKind type) const = 0;
+  virtual PredicateStats getMinMax(int32_t colId) const = 0;
+  virtual PredicateStats getMinMax(int32_t colId,
+                                   dbcommon::Timestamp* minTimestamp,
+                                   dbcommon::Timestamp* maxTimestamp) const = 0;
+
+ protected:
+  const Statistics* stripeStats;
+};
+
+class COBlockTask {
+ public:
+  COBlockTask(uint64_t offset, uint64_t beginRowId, uint64_t rowCount)
+      : offset(offset),
+        beginRowId(beginRowId),
+        rowCount(rowCount),
+        taskBeginRowId(beginRowId),
+        taskRowCount(rowCount),
+        lastTaskBeginRowId(0),
+        lastTaskRowCount(0),
+        intersected(false) {}
+
+  COBlockTask(uint64_t offset, uint64_t beginRowId, uint64_t rowCount,
+              uint64_t taskBeginRowId, uint64_t taskRowCount)
+      : offset(offset),
+        beginRowId(beginRowId),
+        rowCount(rowCount),
+        taskBeginRowId(taskBeginRowId),
+        taskRowCount(taskRowCount),
+        lastTaskBeginRowId(0),
+        lastTaskRowCount(0),
+        intersected(false) {
+    LOG_DEBUG("CO block task %llu %llu %llu %llu %llu", offset, beginRowId,
+              rowCount, taskBeginRowId, taskRowCount);
+  }
+
+  typedef std::unique_ptr<COBlockTask> uptr;
+
+ public:
+  inline uint64_t getEndRowId() const { return beginRowId + rowCount - 1; }
+  inline uint64_t getTaskEndRowId() const {
+    return taskBeginRowId + taskRowCount - 1;
+  }
+  inline uint64_t getLastTaskEndRowId() const {
+    return lastTaskBeginRowId + lastTaskRowCount - 1;
+  }
+
+  inline void resetIntersection() {
+    intersected = false;
+    lastTaskBeginRowId = taskBeginRowId;
+    lastTaskRowCount = taskRowCount;
+    taskRowCount = 0;
+  }
+
+  inline void resetUnionAll() {
+    lastTaskBeginRowId = taskBeginRowId;
+    lastTaskRowCount = taskRowCount;
+  }
+
+  inline void intersect(const COBlockTask& srcTask) {
+    if (srcTask.getTaskEndRowId() < lastTaskBeginRowId ||
+        srcTask.taskBeginRowId > this->getLastTaskEndRowId()) {
+      if (!intersected) {
+        if (srcTask.getEndRowId() < this->lastTaskBeginRowId) {
+          // does not touch existing job, so restore original version
+          taskBeginRowId = lastTaskBeginRowId;
+          taskRowCount = lastTaskRowCount;
+          intersected = true;
+        } else if (srcTask.getEndRowId() <= this->getLastTaskEndRowId()) {
+          // restore partial job
+          taskBeginRowId = srcTask.getEndRowId() + 1;
+          taskRowCount = this->getLastTaskEndRowId() - taskBeginRowId + 1;
+          intersected = true;
+        }
+        return;
+      }
+    }
+
+    if (!intersected) {
+      // fix task begin row id, the source task may have smaller task begin row
+      // id, thus check to make sure this task has valid task begin row id. end
+      // row id has the same problem as well.
+      taskBeginRowId = srcTask.taskBeginRowId < taskBeginRowId
+                           ? taskBeginRowId
+                           : srcTask.taskBeginRowId;
+      uint64_t tmpTaskEndRowId = srcTask.getTaskEndRowId();
+      tmpTaskEndRowId = tmpTaskEndRowId > this->getLastTaskEndRowId()
+                            ? this->getLastTaskEndRowId()
+                            : tmpTaskEndRowId;
+      taskRowCount = tmpTaskEndRowId - taskBeginRowId + 1;
+      intersected = true;
+    } else {
+      // dont check begin row id again, as we expect srce tasks are processed
+      // in ascending order, and we use last task row id range to perform
+      // intersection
+      uint64_t tmpTaskEndRowId = srcTask.getTaskEndRowId();
+      tmpTaskEndRowId = tmpTaskEndRowId > this->getLastTaskEndRowId()
+                            ? this->getLastTaskEndRowId()
+                            : tmpTaskEndRowId;
+      taskRowCount = tmpTaskEndRowId - taskBeginRowId + 1;
+    }
+  }
+
+  inline void unionAll(const COBlockTask& srcTask) {
+    if (srcTask.getTaskEndRowId() < beginRowId ||
+        srcTask.taskBeginRowId > this->getEndRowId()) {
+      // skip this part, as no overlap in row id range
+      return;
+    }
+    // we always check its possible new begin rowid and it should not go out of
+    // current task range
+    taskBeginRowId = taskBeginRowId > srcTask.taskBeginRowId
+                         ? srcTask.taskBeginRowId
+                         : taskBeginRowId;
+    taskBeginRowId = taskBeginRowId < beginRowId ? beginRowId : taskBeginRowId;
+    // then is its end rowid
+    uint64_t tmpTaskEndRowId = srcTask.getTaskEndRowId();
+    tmpTaskEndRowId = tmpTaskEndRowId > this->getTaskEndRowId()
+                          ? tmpTaskEndRowId
+                          : this->getTaskEndRowId();
+    tmpTaskEndRowId = tmpTaskEndRowId > this->getEndRowId()
+                          ? this->getEndRowId()
+                          : tmpTaskEndRowId;
+    taskRowCount = tmpTaskEndRowId - taskBeginRowId + 1;
+  }
+
+ public:
+  uint64_t offset;
+  uint64_t beginRowId;
+  uint64_t rowCount;
+  uint64_t taskBeginRowId;
+  uint64_t taskRowCount;
+  uint64_t lastTaskBeginRowId;
+  uint64_t lastTaskRowCount;
+
+ private:
+  bool intersected;
+};
+
+class COBlockTaskList {
+ public:
+  COBlockTaskList();
+
+  void add(COBlockTask::uptr task) {
+    uint64_t taskBeginRowId = task->taskBeginRowId;
+    taskList[taskBeginRowId] = std::move(task);
+  }
+  void intersect(const COBlockTaskList& source);
+  void unionAll(const COBlockTaskList& source);
+
+  typedef std::unique_ptr<COBlockTaskList> uptr;
+
+ public:
+  std::map<uint64_t, COBlockTask::uptr> taskList;
+};
+
+class MinMaxPredicatesCO : public MinMaxPredicatesAbstract {
+ public:
+  MinMaxPredicatesCO(const univplan::UnivPlanExprPolyList* predicateExprs,
+                     const dbcommon::TupleDesc* tupleDesc)
+      : MinMaxPredicatesAbstract(predicateExprs, tupleDesc) {}
+  virtual ~MinMaxPredicatesCO() {}
+
+  // methods implemented by sub-class to provide customized data preparation
+  virtual void preparePredicatesForStrip() = 0;
+  virtual COBlockTask::uptr buildCOTaskFromCurrentStrip(int colId) = 0;
+
+  // methods of whole calculation framework
+  virtual void preparePredicates();
+  virtual void calculate(std::unordered_map<int, COBlockTaskList::uptr>* res);
+
+ protected:
+  // holding one strip's statistics
+  std::unique_ptr<univplan::Statistics> stripStatistics;
+  // holding one page level predicates to do actual calculation
+  std::unique_ptr<MinMaxPredicatesPage> predicatesForPages;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_MINMAX_MINMAX_PREDICATES_H_
diff --git a/depends/univplan/src/univplan/proto/universal-plan-catalog.proto b/depends/univplan/src/univplan/proto/universal-plan-catalog.proto
new file mode 100644
index 0000000..eab5e16
--- /dev/null
+++ b/depends/univplan/src/univplan/proto/universal-plan-catalog.proto
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+ 
+syntax = "proto2";
+
+package univplan;
+
+option cc_generic_services = true;
+option java_generic_services = true;
+
+message UnivPlanColumn {
+	required string		columnName = 1;
+	required int32 		typeId = 2;
+	optional int32    scale1 = 3;
+	optional int32    scale2 = 4;
+	optional bool     isNullable = 5;
+	optional bytes    defaultVal = 6;
+	required int64    typeMod = 7 [default = -1];
+}
+
+enum UNIVPLANFORMATTYPE {
+  TEXT_FORMAT        = 1;
+  CSV_FORMAT         = 2;
+  ORC_FORMAT         = 3;
+  MAGMA_FORMAT       = 4;
+  MAGMA_LOCAL_FORMAT = 5;
+  INVALID_FORMAT     = 6;
+}
+
+//
+// Magma:
+//
+//  In case a Magma external table, location saves target table external
+//  name defined in Magma, in format :
+//      magma:///dbname/schemaname/tablename
+//  for the columns, MAGMA has its own catalog having complete column definition,
+//  thus, it is only required to fill basic column names and rough type content
+//
+
+message UnivPlanTable {
+	required int64					    tableId = 1;
+	required UNIVPLANFORMATTYPE	format = 2;
+	required string					    location = 3;
+	required bytes					    tableOptionsInJson = 4;
+	repeated UnivPlanColumn 		columns = 5;
+}
diff --git a/depends/univplan/src/univplan/proto/universal-plan-expr.proto b/depends/univplan/src/univplan/proto/universal-plan-expr.proto
new file mode 100644
index 0000000..bbd6b24
--- /dev/null
+++ b/depends/univplan/src/univplan/proto/universal-plan-expr.proto
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+ 
+syntax = "proto2";
+
+package univplan;
+
+option cc_generic_services = true;
+option java_generic_services = true;
+
+enum UNIVPLANEXPRTYPE {
+  UNIVPLAN_EXPR_AGGREF = 1;
+  UNIVPLAN_EXPR_BOOLEXPR = 2;
+  UNIVPLAN_EXPR_CONST = 3;
+  UNIVPLAN_EXPR_FUNCEXPR = 4;
+  UNIVPLAN_EXPR_NULLTEST = 5;
+  UNIVPLAN_EXPR_OPEXPR = 6;
+  UNIVPLAN_EXPR_TARGETENTRY = 7;
+  UNIVPLAN_EXPR_VAR = 8;
+  UNIVPLAN_EXPR_BOOLEANTEST = 9;
+  UNIVPLAN_EXPR_CASEEXPR = 10;
+  UNIVPLAN_EXPR_CASEWHEN = 11;
+  UNIVPLAN_EXPR_SUBPLAN = 12;
+  UNIVPLAN_EXPR_PARAM = 13;
+  UNIVPLAN_EXPR_SCALARARRAYOPEXPR = 14;
+  UNIVPLAN_EXPR_COALESCEEXPR = 15;
+  UNIVPLAN_EXPR_NULLIFEXPR = 16;
+  UNIVPLAN_EXPR_DISTINCTEXPR = 17;
+}
+
+message UnivPlanOpExpr {
+  required int32              funcId = 1;
+  required int32              retType = 2;
+  repeated UnivPlanExprPoly   args = 3;
+}
+
+message UnivPlanFuncExpr {
+  required int32              funcId = 1;
+  required int32              retType = 2;
+  repeated UnivPlanExprPoly   args = 3;
+}
+
+enum NULLTESTTYPE {
+  NULLTESTTYPE_IS_NULL = 0;
+  NULLTESTTYPE_IS_NOT_NULL = 1;
+}
+
+message UnivPlanNullTest {
+  required UnivPlanExprPoly   arg = 1;
+  required NULLTESTTYPE       type = 2;
+}
+
+enum BOOLTESTTYPE {
+  BOOLTESTTYPE_IS_TRUE = 0;
+  BOOLTESTTYPE_IS_NOT_TRUE = 1;
+  BOOLTESTTYPE_IS_FALSE = 2;
+  BOOLTESTTYPE_IS_NOT_FALSE = 3;
+  BOOLTESTTYPE_IS_UNKNOWN = 4;
+  BOOLTESTTYPE_IS_NOT_UNKNOWN = 5;
+}
+
+message UnivPlanBooleanTest {
+  required UnivPlanExprPoly   arg = 1;
+  required BOOLTESTTYPE       type = 2;
+}
+
+enum BOOLEXPRTYPE {
+  BOOLEXPRTYPE_AND_EXPR = 0;
+  BOOLEXPRTYPE_OR_EXPR = 1;
+  BOOLEXPRTYPE_NOT_EXPR = 2;
+}
+
+message UnivPlanBoolExpr {
+  repeated UnivPlanExprPoly   args = 1;
+  required BOOLEXPRTYPE       type = 2;
+}
+
+message UnivPlanConst {
+  required int32              type = 1;
+  required bool               isNull = 2;
+  optional string             value = 3;
+  optional int64              typeMod = 4 [default = -1];
+}
+
+message UnivPlanVar {
+  required uint32             varNo = 1;
+  required int32              varAttNo = 2;
+  required int32              typeId = 3;
+  optional int64              typeMod = 4 [default = -1];
+}
+
+message UnivPlanAggref {
+  required int32              transFuncId = 1;
+  required int32              retType = 2;
+  required bool               transInitVal = 3;
+  optional int32              finalFuncId = 4;
+  repeated UnivPlanExprPoly   args = 5;
+  required int32              funcId = 6;
+}
+
+message UnivPlanTargetEntry {
+  required UnivPlanExprPoly   expression = 1;
+  required bool               resJunk = 2;
+  optional string             resName = 3;
+}
+
+message UnivPlanCaseExpr {
+  required int32              casetype = 1;
+  repeated UnivPlanExprPoly   args = 2;
+  required UnivPlanExprPoly   defresult = 3;
+}
+
+message UnivPlanCaseWhen {
+  required UnivPlanExprPoly   expr = 1;
+  required UnivPlanExprPoly   result = 2;
+}
+
+enum SUBLINKTYPE {
+  EXISTS_SUBLINK = 0;
+  ALL_SUBLINK = 1;
+  ANY_SUBLINK = 2;
+  ROWCOMPARE_SUBLINK = 3;
+  EXPR_SUBLINK = 4;
+  ARRAY_SUBLINK = 5;
+  NOT_EXISTS_SUBLINK = 6;
+}
+
+message UnivPlanSubPlan {
+  required SUBLINKTYPE subLinkType = 1;
+  required int32 planId = 2;
+  required int32 typeId = 3;
+  optional int64 typeMod = 4 [default = -1];
+  required bool useHashTable = 5;
+  required bool initPlan = 6;
+  repeated int32 setParam = 7;                // input param for param_exec
+  repeated int32 parentParam = 8;             // input param for subplan
+  repeated UnivPlanExprPoly args = 9;         // inputs for subplan
+  repeated int32 testexprParam = 10;          // input param for test expr
+  optional UnivPlanExprPoly testexpr = 11;    // test expr for ALL/ANY
+}
+
+enum PARAMKIND {
+  PARAM_EXTERN = 0;
+  PARAM_EXEC = 1;
+}
+
+message UnivPlanParam {
+  required PARAMKIND paramKind = 1;
+  required int32 paramId = 2;
+  required int32 typeId = 3;
+  optional int64 typeMod = 4 [default = -1];
+}
+
+message UnivPlanScalarArrayOpExpr {
+  required int32              funcId = 1;
+  required bool               useOr = 2;
+  repeated UnivPlanExprPoly   args = 3;
+}
+
+message UnivPlanCoalesceExpr {
+  required int32              coalesceType = 1;
+  required int32              coalesceTypeMod = 2;
+  repeated UnivPlanExprPoly   args = 3;
+}
+
+message UnivPlanNullIfExpr {
+  required int32              funcId = 1;
+  required int32              retType = 2;
+  required int32              typeMod = 3;
+  repeated UnivPlanExprPoly   args = 4;
+}
+
+message UnivPlanDistinctExpr {
+  required int32				 funcId = 1;
+  required int32				 retType = 2;
+  repeated UnivPlanExprPoly   args = 3;
+}
+
+message UnivPlanExprPoly {
+  required UNIVPLANEXPRTYPE   type = 1;
+  oneof value {
+    UnivPlanAggref            aggref = 2;
+    UnivPlanBoolExpr          boolexpr = 3;
+    UnivPlanConst             val = 4;
+    UnivPlanFuncExpr          funcexpr = 5;
+    UnivPlanNullTest          nulltest = 6;
+    UnivPlanOpExpr            opexpr = 7;
+    UnivPlanTargetEntry       targetentry = 8;
+    UnivPlanVar               var = 9;
+    UnivPlanBooleanTest       booltest = 10;
+    UnivPlanCaseExpr          caseexpr = 11;
+    UnivPlanCaseWhen          casewhen = 12;
+    UnivPlanSubPlan           subplan = 13;
+    UnivPlanParam             param = 14;
+    UnivPlanScalarArrayOpExpr scalarArrayOpexpr = 15;
+    UnivPlanCoalesceExpr      coalesceExpr = 16;
+    UnivPlanNullIfExpr        nullIfExpr = 17;
+    UnivPlanDistinctExpr      distinctExpr = 18;
+  }
+}
diff --git a/depends/univplan/src/univplan/proto/universal-plan.proto b/depends/univplan/src/univplan/proto/universal-plan.proto
new file mode 100644
index 0000000..fb1cf95
--- /dev/null
+++ b/depends/univplan/src/univplan/proto/universal-plan.proto
@@ -0,0 +1,415 @@
+/*
+ * 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.
+ */
+ 
+syntax = "proto2";
+
+package univplan;
+
+option cc_generic_services = true;
+option java_generic_services = true;
+
+import "univplan/proto/universal-plan-catalog.proto";
+import "univplan/proto/universal-plan-expr.proto";
+
+enum UNIVPLANNODETYPE {
+  UNIVPLAN_UNSET = 1;
+
+  UNIVPLAN_CONNECTOR = 2;
+  UNIVPLAN_SINK = 3;
+  UNIVPLAN_CONVERGE = 4;
+  UNIVPLAN_BROADCAST = 5;
+  UNIVPLAN_SHUFFLE = 6;
+  UNIVPLAN_AGG = 7;
+  UNIVPLAN_SCAN_SEQ = 8;
+  UNIVPLAN_SORT = 9;
+  UNIVPLAN_LIMIT = 10;
+  UNIVPLAN_APPEND = 11;
+  UNIVPLAN_NESTLOOP = 12;
+  UNIVPLAN_HASHJOIN = 13;
+  UNIVPLAN_MERGEJOIN = 14;
+  UNIVPLAN_MATERIAL = 15;
+  UNIVPLAN_RESULT = 16;
+  UNIVPLAN_HASH = 17;
+  UNIVPLAN_SUBQUERYSCAN = 18;
+  UNIVPLAN_UNIQUE = 19;
+  UNIVPLAN_INSERT = 20;
+  UNIVPLAN_SHAREINPUTSCAN = 21;
+
+  UNIVPLAN_EXT_GS_SCAN = 100;
+  UNIVPLAN_EXT_GS_FILTER = 101;
+  UNIVPLAN_EXT_GS_PROJ = 102;
+}
+
+message UnivPlanPlanNodePoly {
+  required UNIVPLANNODETYPE type = 1;
+  oneof value {
+    UnivPlanConnector connector = 2;
+    UnivPlanSink sink = 3;
+    UnivPlanConverge converge = 4;
+    UnivPlanBroadcast broadcast = 5;
+    UnivPlanShuffle shuffle = 6;
+    UnivPlanAgg agg = 7;
+    UnivPlanScanSeq scanSeq = 8;
+    UnivPlanSort sort = 9;
+    UnivPlanLimit limit = 10;
+    UnivPlanAppend append = 11;
+    UnivPlanNestLoop nestLoop = 12;
+    UnivPlanMergeJoin mergeJoin = 13;
+    UnivPlanHashJoin hashJoin = 14;
+    UnivPlanMaterial material = 15;
+    UnivPlanResult result = 16;
+    UnivPlanHash hash = 17;
+    UnivPlanSubqueryScan subqueryscan = 18;
+    UnivPlanUnique unique = 19;
+    UnivPlanInsert insert = 20;
+    UnivPlanShareInputScan shareinputscan = 21;
+
+    UnivPlanExtGSScan extGSScan = 100;
+    UnivPlanExtGSFilter extGSFilter = 101;
+    UnivPlanExtGSProj extGSProject = 102;
+  }
+}
+
+// The overall super node for plan nodes
+message UnivPlanPlanNode {
+  repeated UnivPlanExprPoly targetList = 1;     // Target list to be calculated
+  repeated UnivPlanExprPoly qualList = 2;       // qual conditions
+  optional UnivPlanPlanNodePoly leftPlan = 3;   // left plan tree
+  optional UnivPlanPlanNodePoly rightPlan = 4;  // right plan tree
+  repeated UnivPlanExprPoly initPlan = 5;       // un-correlated expr subselects
+  optional double planRows = 6;                 // estimated number of rows
+  optional int32  planRowWidth = 7;             // estimated row width in bytes
+  optional int64  operatorMemKB = 8;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SCAN NODES BEGIN
+///////////////////////////////////////////////////////////////////////////////
+
+// The scan task
+message UnivPlanScanTask {
+  // repeated UnivPlanScanFileSplit splits = 1;
+  optional bytes serializedSplits = 1;
+}
+
+message UnivPlanScanSeq {
+  required UnivPlanPlanNode super = 1;
+  required uint32 relId = 2;
+  optional bool readStatsOnly = 3;
+  repeated UnivPlanScanTask tasks = 4;
+  repeated int32 columnsToRead = 5;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SCAN NODES END
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// SINK NODES BEGIN
+///////////////////////////////////////////////////////////////////////////////
+
+enum UNIVPLANCONNECTORTYPE {
+  CONNECTORTYPE_INVALID = 0;
+  CONNECTORTYPE_SHUFFLE = 1;
+  CONNECTORTYPE_BROADCAST = 2;
+  CONNECTORTYPE_CONVERGE = 3;
+}
+
+message UnivPlanConnector {
+  required UnivPlanPlanNode super = 1;
+  required UNIVPLANCONNECTORTYPE type = 2;
+  optional int32 stageno = 3 [default = -1];
+  repeated UnivPlanExprPoly hashExpr = 4;
+  repeated int32 colIdx = 5;
+  repeated int32 sortFuncId = 6;
+  optional bool  magmaTable = 7 [default = false];
+  repeated int32 magmaMap = 8;
+  optional int32 rangeNum = 9 [default = 0];  // magma range num
+}
+
+// Sink
+message UnivPlanSink {
+  required UnivPlanPlanNode super = 1;
+  required UNIVPLANCONNECTORTYPE connectorType = 2;
+  required int32 sourceStageNo = 3;
+  required int32 currentStageNo = 4;
+  repeated int32 colIdx = 5;
+  repeated int32 sortFuncId = 6;
+}
+
+// Converge
+message UnivPlanConverge {
+  required UnivPlanPlanNode super = 1;
+  required int32 targetStageNo = 2;
+  required int32 currentStageNo = 3;
+}
+
+// Broadcast
+message UnivPlanBroadcast {
+  required UnivPlanPlanNode super = 1;
+  required int32 targetStageNo = 2;
+  required int32 currentStageNo = 3;
+}
+
+// Shuffle
+message UnivPlanShuffle {
+  required UnivPlanPlanNode super = 1;
+  required int32 targetStageNo = 2;
+  required int32 currentStageNo = 3;
+  repeated UnivPlanExprPoly hashExpr = 4;
+  optional bool  magmaTable = 5 [default = false];
+  repeated int32 magmaMap = 6;
+  optional int32 rangeNum = 9 [default = 0];  // magma range num
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// SINK NODES END
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// JOIN NODES BEGIN
+///////////////////////////////////////////////////////////////////////////////
+
+enum UNIVPLANJOINTYPE {
+  JOIN_INNER = 0;  // matching tuple pairs only
+  JOIN_LEFT = 1;   // pairs + unmatched LHS tuples
+  JOIN_RIGHT = 2;  // pairs + unmatched RHS tuples
+  JOIN_FULL = 3;   // pairs + unmatched LHS + unmatched RHS
+  JOIN_IN = 4;
+  JOIN_LASJ = 8;
+  JOIN_LASJ_NOTIN = 9;
+  JOIN_NOT_SUPPORTED = 10;
+}
+
+// NestLoop
+message UnivPlanNestLoop {
+  required UnivPlanPlanNode super = 1;
+  required UNIVPLANJOINTYPE type = 2;
+  repeated UnivPlanExprPoly joinQual = 3;
+}
+
+// HashJoin
+message UnivPlanHashJoin {
+  required UnivPlanPlanNode super = 1;
+  required UNIVPLANJOINTYPE type = 2;
+  repeated UnivPlanExprPoly joinQual = 3;
+  repeated UnivPlanExprPoly hashClauses = 4;
+  repeated UnivPlanExprPoly hashQualClauses = 5;
+}
+
+// MergeJoin
+message UnivPlanMergeJoin {
+  required UnivPlanPlanNode super = 1;
+  required UNIVPLANJOINTYPE type = 2;
+  repeated UnivPlanExprPoly joinQual = 3;
+  repeated UnivPlanExprPoly mergeClauses = 4;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// JOIN NODES END
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// OTHER PLAN NODES BEGIN
+///////////////////////////////////////////////////////////////////////////////
+
+message UnivPlanAgg {
+  required UnivPlanPlanNode super = 1;
+  required int64 numGroups = 2;
+  repeated int32 groupColIndexes = 3;
+}
+
+message UnivPlanLimit {
+  required UnivPlanPlanNode super = 1;
+  optional UnivPlanExprPoly limitOffset = 2;
+  optional UnivPlanExprPoly limitCount = 3;
+}
+
+message UnivPlanAppend {
+  required UnivPlanPlanNode super = 1;
+  repeated UnivPlanPlanNodePoly appendPlans = 2;
+}
+
+message UnivPlanSort {
+  required UnivPlanPlanNode super = 1;
+  repeated int32 colIdx = 2;
+  repeated int32 sortFuncId = 3;
+  optional UnivPlanExprPoly limitOffset = 4;
+  optional UnivPlanExprPoly limitCount = 5;
+}
+
+enum UNIVPLANSHARETYPE {
+  SHARE_NOTSHARED = 0;
+  SHARE_MATERIAL = 1;
+  SHARE_MATERIAL_XSLICE = 2;
+  SHARE_SORT = 3;
+  SHARE_SORT_XSLICE = 4;
+  SHARE_NOT_SUPPORTED = 5;
+}
+
+message UnivPlanMaterial {
+  required UnivPlanPlanNode super = 1;
+  required UNIVPLANSHARETYPE share_type = 2;
+  required bool cdbStrict = 3;
+  optional int32 shared_id = 4;
+  optional int32 driver_slice = 5;  // slice id that will execute this material
+  optional int32 nsharer = 6;       // number of sharer
+  optional int32 nsharer_xslice = 7;  // number of sharer cross slice
+}
+
+message UnivPlanShareInputScan {
+    required UnivPlanPlanNode super = 1;
+    required UNIVPLANSHARETYPE shareType = 2;
+    required int32 sharedId = 3;
+    required int32 driverSlice = 4;
+}
+
+message UnivPlanResult {
+  required UnivPlanPlanNode super = 1;
+  repeated UnivPlanExprPoly resconstantqual = 2;
+}
+
+message UnivPlanHash {
+  required UnivPlanPlanNode super = 1;
+}
+
+message UnivPlanSubqueryScan {
+  required UnivPlanPlanNode super = 1;
+  optional UnivPlanPlanNodePoly subPlan = 2;
+}
+
+message UnivPlanUnique {
+  required UnivPlanPlanNode    super = 1;
+  repeated int32               uniqColIdxs = 2;
+}
+
+message UnivPlanInsert {
+  required UnivPlanPlanNode    super = 1;
+  required uint32 relId = 2;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OTHER PLAN NODES END
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// MAGMA NODES BEGIN
+///////////////////////////////////////////////////////////////////////////////
+enum ExternalScanType {
+  TableScan = 0;
+  MagmaIndexScan = 1;
+  MagmaIndexOnlyScan = 2;
+  MagmaBitmapScan = 3;
+}
+
+enum ExternalScanDirection {
+  BackwardScanDirection = -1;
+  NoMovementScanDirection = 0;
+  ForwardScanDirection = 1;
+}
+
+message UnivPlanExtGSScan {
+  required UnivPlanPlanNode super = 1;
+  required uint32 relId = 2;       // table def index
+  repeated int32 keyColIndex = 3;  // this two field for schema mapping
+  repeated int32 valColIndex = 4;
+  repeated int32 columnsToRead = 5;
+  optional UnivPlanExprPoly filter = 6;          // filter
+  repeated UnivPlanExprPoly filterByKeyCol = 7;  // filter for each col
+  repeated UnivPlanScanTask tasks = 8;           // use univplan scan task
+  // index scan info for magma
+  optional bool indexscan = 9 [default = false];
+  optional ExternalScanType type = 10;
+  optional ExternalScanDirection direction = 11;
+  optional string indexName = 12;
+  repeated UnivPlanExprPoly indexQual = 13;
+  optional bool readStatsOnly = 14 [default = false];
+}
+
+message UnivPlanExtGSFilter {
+  required UnivPlanPlanNode super = 1;
+  optional UnivPlanExprPoly filter = 2;  // filter for all columns
+}
+
+message UnivPlanExtGSProj {
+  required UnivPlanPlanNode super = 1;
+  repeated int32 columnIndexes = 2;  // required columns
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// MAGMA NODES END
+///////////////////////////////////////////////////////////////////////////////
+
+message UnivPlanRangeTblEntry {
+  required UnivPlanTable table = 1;
+}
+
+message UnivPlanListener {
+  required string address = 1;
+  required int32 port = 2;
+}
+
+message UnivPlanReceiver {
+  repeated UnivPlanListener listener = 1;
+}
+
+message UnivPlanParamInfo {
+  required int32 type = 1;
+  required bool isNull = 2;
+  optional string value = 3;
+}
+
+message UnivPlanTokenKey {
+  required string protocol = 1;
+  required string ip = 2;
+  required int32 port = 3;
+}
+
+message UnivPlanTokenEntry {
+  required UnivPlanTokenKey key = 1;
+  required string token = 2;
+}
+
+enum UNIVPLANCMDTYPE {
+  CMD_UNKNOWN = 0;
+  CMD_SELECT = 1;
+  CMD_UPDATE = 2;
+  CMD_INSERT = 3;
+  CMD_DELETE = 4;
+  CMD_UTILITY = 5;
+  CMD_NOTHING = 6;
+}
+
+// Overall container of one plan to execute
+message UnivPlanPlan {
+  required UnivPlanPlanNodePoly plan = 1;
+  repeated UnivPlanPlan childStages = 2;
+  repeated UnivPlanRangeTblEntry rangeTables = 3;
+  repeated UnivPlanReceiver receivers = 4;
+  optional int32 stageNo = 5;
+  optional bool doInstrument = 6;
+  repeated UnivPlanPlanNodePoly subplans = 7;
+  repeated UnivPlanParamInfo paramInfos = 8;
+  optional int32 nCrossLevelParams = 9 [default = 0];
+  repeated UnivPlanTokenEntry tokenMap = 10;
+  optional bytes snapshot = 11;
+  map<string, string> guc = 12;
+  map<string, string> commonValue = 13;
+  optional UNIVPLANCMDTYPE cmdType = 14 [default = CMD_SELECT];
+}
diff --git a/depends/univplan/src/univplan/testutil/univplan-proto-util.cc b/depends/univplan/src/univplan/testutil/univplan-proto-util.cc
new file mode 100644
index 0000000..6bb0a76
--- /dev/null
+++ b/depends/univplan/src/univplan/testutil/univplan-proto-util.cc
@@ -0,0 +1,519 @@
+/*
+ * 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.
+ */
+
+#include "univplan/testutil/univplan-proto-util.h"
+
+#include <memory>
+#include <string>
+
+#include "dbcommon/function/func-kind.cg.h"
+
+namespace univplan {
+
+UnivPlanProtoUtility::UnivPlanProtoUtility() {
+  this->univplan = univPlanNewInstance();
+}
+
+UnivPlanProtoUtility::~UnivPlanProtoUtility() {
+  univPlanFreeInstance(&univplan);
+}
+
+void UnivPlanProtoUtility::constructVarOpConstExpr(
+    int32_t pid, int32_t opFuncId, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType, dbcommon::TypeKind constType,
+    const char *buffer) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddOpExpr(univplan, pid, opFuncId);
+  univPlanExprAddVar(univplan, root, varNo, varAttNo, varType, -1);
+  univPlanExprAddConst(univplan, root, constType, false, buffer, -1);
+}
+
+void UnivPlanProtoUtility::constructConstOpVarExpr(
+    int32_t pid, int32_t opFuncId, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType, dbcommon::TypeKind constType,
+    const char *buffer) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddOpExpr(univplan, pid, opFuncId);
+  univPlanExprAddConst(univplan, root, constType, false, buffer, -1);
+  univPlanExprAddVar(univplan, root, varNo, varAttNo, varType, -1);
+}
+
+void UnivPlanProtoUtility::constructVarOpVarExpr(
+    int32_t pid, int32_t opFuncId, int32_t varNo1, int32_t varAttNo1,
+    dbcommon::TypeKind varType1, int32_t varNo2, int32_t varAttNo2,
+    dbcommon::TypeKind varType2) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddOpExpr(univplan, pid, opFuncId);
+  univPlanExprAddVar(univplan, root, varNo1, varAttNo1, varType1, -1);
+  univPlanExprAddVar(univplan, root, varNo2, varAttNo2, varType2, -1);
+}
+
+void UnivPlanProtoUtility::constructConstOpConstExpr(
+    int32_t pid, int32_t opFuncId, dbcommon::TypeKind constType1,
+    const char *buffer1, dbcommon::TypeKind constType2, const char *buffer2) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddOpExpr(univplan, pid, opFuncId);
+  univPlanExprAddConst(univplan, root, constType1, false, buffer1, -1);
+  univPlanExprAddConst(univplan, root, constType2, false, buffer2, -1);
+}
+
+void UnivPlanProtoUtility::constructFuncOpVarExpr(int32_t pid, int32_t opFuncId,
+                                                  int32_t varNo,
+                                                  int32_t varAttNo,
+                                                  dbcommon::TypeKind varType,
+                                                  int32_t mappingFuncId) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddOpExpr(univplan, pid, opFuncId);
+  univPlanExprAddFuncExpr(univplan, root, mappingFuncId);
+  univPlanExprAddVar(univplan, root, varNo, varAttNo, varType, -1);
+}
+
+void UnivPlanProtoUtility::constructBoolExpr(
+    int32_t pid, int32_t opFuncId1, int32_t varNo1, int32_t varAttNo1,
+    dbcommon::TypeKind varType1, dbcommon::TypeKind constType1,
+    const char *buffer1, int32_t opFuncId2, int32_t varNo2, int32_t varAttNo2,
+    dbcommon::TypeKind varType2, dbcommon::TypeKind constType2,
+    const char *buffer2, int8_t boolType) {
+  univPlanNewExpr(univplan);
+  int32_t root =
+      univPlanExprAddBoolExpr(univplan, pid, UnivplanBoolExprType(boolType));
+
+  int32_t root1 = univPlanExprAddOpExpr(univplan, root, opFuncId1);
+  univPlanExprAddVar(univplan, root1, varNo1, varAttNo1, varType1, -1);
+  univPlanExprAddConst(univplan, root1, constType1, false, buffer1, -1);
+
+  int32_t root2 = univPlanExprAddOpExpr(univplan, root, opFuncId2);
+  univPlanExprAddVar(univplan, root2, varNo2, varAttNo2, varType2, -1);
+  univPlanExprAddConst(univplan, root2, constType2, false, buffer2, -1);
+}
+
+void UnivPlanProtoUtility::constructNullTestExpr(int32_t pid,
+                                                 int8_t nulltesttype,
+                                                 int32_t varNo,
+                                                 int32_t varAttNo,
+                                                 dbcommon::TypeKind varType) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddNullTestExpr(
+      univplan, pid, UnivplanNullTestType(nulltesttype));
+  univPlanExprAddVar(univplan, root, varNo, varAttNo, varType, -1);
+}
+
+void UnivPlanProtoUtility::constructVarOpConstThenOpVarOpConstExpr(
+    int32_t pid, int32_t opFuncId, int32_t opFuncId1, int32_t varNo1,
+    int32_t varAttNo1, dbcommon::TypeKind varType1,
+    dbcommon::TypeKind constType1, const char *buffer1, int32_t opFuncId2,
+    int32_t varNo2, int32_t varAttNo2, dbcommon::TypeKind varType2,
+    dbcommon::TypeKind constType2, const char *buffer2) {
+  univPlanNewExpr(univplan);
+  int32_t root = univPlanExprAddOpExpr(univplan, pid, opFuncId);
+
+  int32_t root1 = univPlanExprAddOpExpr(univplan, root, opFuncId1);
+  univPlanExprAddVar(univplan, root1, varNo1, varAttNo1, varType1, -1);
+  univPlanExprAddConst(univplan, root1, constType1, false, buffer1, -1);
+
+  int32_t root2 = univPlanExprAddOpExpr(univplan, root, opFuncId2);
+  univPlanExprAddVar(univplan, root2, varNo2, varAttNo2, varType2, -1);
+  univPlanExprAddConst(univplan, root2, constType2, false, buffer2, -1);
+}
+
+void UnivPlanProtoUtility::constructVarTargetEntry(int32_t pid, int32_t varNo,
+                                                   int32_t varAttNo,
+                                                   dbcommon::TypeKind type) {
+  univPlanNewExpr(univplan);
+  univPlanExprAddVar(univplan, pid, varNo, varAttNo, type, -1);
+  univPlanTargetListAddTargetEntry(univplan, false);
+}
+
+void UnivPlanProtoUtility::constructConstTargetEntry(
+    int32_t pid, dbcommon::TypeKind constType, const char *buffer) {
+  univPlanNewExpr(univplan);
+  if (constType == dbcommon::TypeKind::ANYID)
+    univPlanExprAddConst(univplan, -1, dbcommon::TypeKind::TINYINTID, true,
+                         buffer, -1);
+  else
+    univPlanExprAddConst(univplan, -1, constType, false, buffer, -1);
+  univPlanTargetListAddTargetEntry(univplan, false);
+}
+
+void UnivPlanProtoUtility::constructConstOpConstTargetEntry(
+    int32_t pid, int32_t opFuncId, dbcommon::TypeKind constType1,
+    const char *buffer1, dbcommon::TypeKind constType2, const char *buffer2) {
+  univPlanNewExpr(univplan);
+  constructConstOpConstExpr(pid, opFuncId, constType1, buffer1, constType2,
+                            buffer2);
+  univPlanTargetListAddTargetEntry(univplan, false);
+}
+
+void UnivPlanProtoUtility::constructAggRefTargetEntry(
+    int32_t pid, int8_t aggstage, int32_t mappingFuncId, int32_t varNo,
+    int32_t varAttNo, dbcommon::TypeKind type) {
+  univPlanNewExpr(univplan);
+  int32_t uid;
+  if (aggstage == 1)  // AGGSTAGE_PARTIAL
+    uid = univPlanAggrefAddPartialStage(univplan, -1, mappingFuncId);
+  else if (aggstage == 3)  // AGGSTAGE_FINAL
+    uid = univPlanAggrefAddFinalStage(univplan, -1, mappingFuncId);
+  univPlanAggrefAddProxyVar(univplan, uid, varAttNo, mappingFuncId, -1);
+  univPlanTargetListAddTargetEntry(univplan, false);
+}
+
+void UnivPlanProtoUtility::constructNullTestTargetEntry(
+    int32_t pid, int8_t nulltesttype, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType) {
+  constructNullTestExpr(pid, nulltesttype, varNo, varAttNo, varType);
+  univPlanTargetListAddTargetEntry(univplan, false);
+}
+
+void UnivPlanProtoUtility::constructVarOpConstTargetEntry(
+    int32_t pid, int32_t opFuncId, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType, dbcommon::TypeKind constType,
+    const char *buffer) {
+  constructVarOpConstExpr(pid, opFuncId, varNo, varAttNo, varType, constType,
+                          buffer);
+  univPlanTargetListAddTargetEntry(univplan, false);
+}
+
+void UnivPlanProtoUtility::constructVarOpConstQualList(
+    int32_t pid, int32_t opFuncId, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType, dbcommon::TypeKind constType,
+    const char *buffer) {
+  constructVarOpConstExpr(pid, opFuncId, varNo, varAttNo, varType, constType,
+                          buffer);
+  univPlanQualListAddExpr(univplan);
+}
+
+void UnivPlanProtoUtility::constructConstOpVarQualList(
+    int32_t pid, int32_t opFuncId, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType, dbcommon::TypeKind constType,
+    const char *buffer) {
+  constructConstOpVarExpr(pid, opFuncId, varNo, varAttNo, varType, constType,
+                          buffer);
+  univPlanQualListAddExpr(univplan);
+}
+
+void UnivPlanProtoUtility::constructVarOpVarQualList(
+    int32_t pid, int32_t opFuncId, int32_t varNo1, int32_t varAttNo1,
+    dbcommon::TypeKind varType1, int32_t varNo2, int32_t varAttNo2,
+    dbcommon::TypeKind varType2) {
+  constructVarOpVarExpr(pid, opFuncId, varNo1, varAttNo1, varType1, varNo2,
+                        varAttNo2, varType2);
+  univPlanQualListAddExpr(univplan);
+}
+
+void UnivPlanProtoUtility::constructFuncOpVarQualList(
+    int32_t pid, int32_t opFuncId, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType, int32_t mappingFuncId) {
+  constructFuncOpVarExpr(pid, opFuncId, varNo, varAttNo, varType,
+                         mappingFuncId);
+  univPlanQualListAddExpr(univplan);
+}
+
+void UnivPlanProtoUtility::constructBoolQualList(
+    int32_t pid, int32_t opFuncId1, int32_t varNo1, int32_t varAttNo1,
+    dbcommon::TypeKind varType1, dbcommon::TypeKind constType1,
+    const char *buffer1, int32_t opFuncId2, int32_t varNo2, int32_t varAttNo2,
+    dbcommon::TypeKind varType2, dbcommon::TypeKind constType2,
+    const char *buffer2, int8_t boolType) {
+  constructBoolExpr(pid, opFuncId1, varNo1, varAttNo1, varType1, constType1,
+                    buffer1, opFuncId2, varNo2, varAttNo2, varType2, constType2,
+                    buffer2, boolType);
+  univPlanQualListAddExpr(univplan);
+}
+
+void UnivPlanProtoUtility::constructNullTestQualList(
+    int32_t pid, int8_t nulltesttype, int32_t varNo, int32_t varAttNo,
+    dbcommon::TypeKind varType) {
+  constructNullTestExpr(pid, nulltesttype, varNo, varAttNo, varType);
+  univPlanQualListAddExpr(univplan);
+}
+
+void UnivPlanProtoUtility::constructVarOpConstThenOpVarOpConstQualList(
+    int32_t pid, int32_t opFuncId, int32_t opFuncId1, int32_t varNo1,
+    int32_t varAttNo1, dbcommon::TypeKind varType1,
+    dbcommon::TypeKind constType1, const char *buffer1, int32_t opFuncId2,
+    int32_t varNo2, int32_t varAttNo2, dbcommon::TypeKind varType2,
+    dbcommon::TypeKind constType2, const char *buffer2) {
+  constructVarOpConstThenOpVarOpConstExpr(
+      pid, opFuncId, opFuncId1, varNo1, varAttNo1, varType1, constType1,
+      buffer1, opFuncId2, varNo2, varAttNo2, varType2, constType2, buffer2);
+  univPlanQualListAddExpr(univplan);
+}
+
+int32_t UnivPlanProtoUtility::constructSeqScan(int32_t pid, bool withQualList,
+                                               int8_t targetEntryType) {
+  int32_t uid = ::univPlanSeqScanNewInstance(univplan, pid);
+
+  constructVarTargetEntry(-1, 1, 1, dbcommon::TypeKind::INTID);
+  if (targetEntryType == 0) {
+    constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+  } else if (targetEntryType == 1) {
+    constructVarTargetEntry(-1, 1, 2, dbcommon::TypeKind::INTID);
+    constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+  } else if (targetEntryType == 2) {
+    constructNullTestTargetEntry(-1, 1, 1, 3, dbcommon::TypeKind::STRINGID);
+  } else if (targetEntryType == 3) {
+    constructVarOpConstTargetEntry(-1, dbcommon::INT_ADD_INT, 1, 1,
+                                   dbcommon::TypeKind::INTID,
+                                   dbcommon::TypeKind::INTID, "1");
+  } else if (targetEntryType == 4) {
+    constructVarOpConstTargetEntry(-1, dbcommon::STRING_EQUAL_STRING, 1, 3,
+                                   dbcommon::TypeKind::STRINGID,
+                                   dbcommon::TypeKind::STRINGID, "1");
+  } else if (targetEntryType == 5) {
+    constructConstTargetEntry(-1, dbcommon::TypeKind::BIGINTID, "1");
+  } else if (targetEntryType == 6) {
+    constructConstTargetEntry(-1, dbcommon::TypeKind::ANYID, "");
+  } else if (targetEntryType == 7) {
+    constructVarOpConstTargetEntry(-1, dbcommon::INT_ADD_INT, 1, 1,
+                                   dbcommon::TypeKind::INTID,
+                                   dbcommon::TypeKind::INTID, "1");
+    constructVarOpConstTargetEntry(-1, dbcommon::STRING_EQUAL_STRING, 1, 3,
+                                   dbcommon::TypeKind::STRINGID,
+                                   dbcommon::TypeKind::STRINGID, "1");
+  } else if (targetEntryType == 8) {
+    constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+    constructVarTargetEntry(-1, 1, 2, dbcommon::TypeKind::INTID);
+  }
+
+  if (withQualList) {
+    constructVarOpConstQualList(-1, dbcommon::INT_GREATER_THAN_INT, 1, 2,
+                                dbcommon::TypeKind::INTID,
+                                dbcommon::TypeKind::INTID, "1");
+    constructVarOpVarQualList(-1, dbcommon::BIGINT_EQUAL_INT, 1, 1,
+                              dbcommon::TypeKind::INTID, 1, 2,
+                              dbcommon::TypeKind::INTID);
+    constructFuncOpVarQualList(-1, dbcommon::DOUBLE_LESS_THAN_DOUBLE, 1, 1,
+                               dbcommon::TypeKind::INTID,
+                               dbcommon::TINYINT_LESS_THAN_TINYINT);
+    constructBoolQualList(
+        -1, dbcommon::BIGINT_GREATER_THAN_INT, 1, 1, dbcommon::TypeKind::INTID,
+        dbcommon::TypeKind::INTID, "1", dbcommon::INT_LESS_THAN_INT, 1, 2,
+        dbcommon::TypeKind::INTID, dbcommon::TypeKind::INTID, "10");
+  }
+
+  univPlanSeqScanSetRelId(univplan, 1);
+
+  int32_t numCols = 2;
+  int32_t *columnsToRead =
+      reinterpret_cast<int32_t *>(malloc(numCols * sizeof(int32_t)));
+  columnsToRead[0] = 0;
+  columnsToRead[1] = 1;
+  univPlanSeqScanSetColumnsToRead(univplan, numCols, columnsToRead);
+  free(columnsToRead);
+
+  int64_t start[2] = {1, 2};
+  int64_t len[2] = {200, 100};
+  std::string fileName_buf[2] = {"/a", "/b"};
+  std::unique_ptr<const char *[]> fileName(new const char *[2]);
+  fileName[0] = fileName_buf[0].c_str();
+  fileName[1] = fileName_buf[1].c_str();
+  univPlanSeqScanAddTaskWithFileSplits(false, univplan, 1, fileName.get(),
+                                       start, len, NULL, NULL);
+  univPlanSeqScanAddTaskWithFileSplits(false, univplan, 2, fileName.get(),
+                                       start, len, NULL, NULL);
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+int32_t UnivPlanProtoUtility::constructConnector(int32_t pid, int8_t ctype,
+                                                 bool allTargetEntry) {
+  int32_t uid = univPlanConnectorNewInstance(univplan, pid);
+  univPlanConnectorSetType(univplan, ConnectorType(ctype));
+
+  constructVarTargetEntry(-1, 1, 1, dbcommon::TypeKind::INTID);
+  if (allTargetEntry)
+    constructVarTargetEntry(-1, 1, 2, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+
+  if (ctype == 0) {
+    univPlanNewExpr(univplan);
+    univPlanExprAddVar(univplan, -1, 1, 0, dbcommon::TypeKind::INTID, -1);
+    univPlanConnectorAddHashExpr(univplan);
+  }
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+int32_t UnivPlanProtoUtility::constructSortConnector(int32_t pid,
+                                                     int8_t ctype) {
+  int32_t uid = univPlanConnectorNewInstance(univplan, pid);
+  univPlanConnectorSetType(univplan, ConnectorType(ctype));
+
+  constructVarTargetEntry(-1, 1, 1, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+
+  int64_t numCols = 2;
+  int32_t *colIdx =
+      reinterpret_cast<int32_t *>(malloc(numCols * sizeof(int32_t)));
+  int32_t *mappingSortFuncId =
+      reinterpret_cast<int32_t *>(malloc(numCols * sizeof(int32_t)));
+  colIdx[0] = 1;
+  colIdx[1] = 2;
+  mappingSortFuncId[0] = dbcommon::BIGINT_LESS_THAN_BIGINT;
+  mappingSortFuncId[1] = dbcommon::INT_LESS_THAN_INT;
+  univPlanConnectorSetColIdx(univplan, numCols, colIdx);
+  univPlanConnectorSetSortFuncId(univplan, numCols, mappingSortFuncId);
+  free(colIdx);
+  free(mappingSortFuncId);
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+int32_t UnivPlanProtoUtility::constructLimitBelow(int32_t pid, int64_t count,
+                                                  int64_t offset) {
+  int32_t uid = univPlanLimitNewInstance(univplan, pid);
+
+  constructVarTargetEntry(-1, 1, 1, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 2, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+
+  univPlanSetPlanNodeInfo(univplan, static_cast<double>(count + offset), 0, 0);
+
+  std::string countTmp = std::to_string(count);
+  char const *countBuf = countTmp.c_str();
+  std::string offsetTmp = std::to_string(offset);
+  char const *offsetBuf = offsetTmp.c_str();
+  if (count != -1) {
+    constructConstOpConstExpr(-1, dbcommon::BIGINT_ADD_BIGINT,
+                              dbcommon::BIGINTID, countBuf, dbcommon::BIGINTID,
+                              offsetBuf);
+    univPlanLimitAddLimitCount(univplan);
+  }
+
+  if (offset != -1) {
+    univPlanNewExpr(univplan);
+    univPlanExprAddConst(univplan, -1, dbcommon::BIGINTID, false, "0", -1);
+    univPlanLimitAddLimitOffset(univplan);
+  }
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+int32_t UnivPlanProtoUtility::constructLimitTop(int32_t pid, int64_t count,
+                                                int64_t offset) {
+  int32_t uid = univPlanLimitNewInstance(univplan, pid);
+
+  constructVarTargetEntry(-1, 1, 1, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 2, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+
+  univPlanSetPlanNodeInfo(univplan, static_cast<double>(count + offset), 0, 0);
+
+  if (count != -1) {
+    univPlanNewExpr(univplan);
+    std::string countTmp = std::to_string(count);
+    char const *countBuf = countTmp.c_str();
+    univPlanExprAddConst(univplan, -1, dbcommon::BIGINTID, false, countBuf, -1);
+    univPlanLimitAddLimitCount(univplan);
+  }
+
+  if (offset != -1) {
+    univPlanNewExpr(univplan);
+    std::string offsetTmp = std::to_string(offset);
+    char const *offsetBuf = offsetTmp.c_str();
+    univPlanExprAddConst(univplan, -1, dbcommon::BIGINTID, false, offsetBuf,
+                         -1);
+    univPlanLimitAddLimitOffset(univplan);
+  }
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+int32_t UnivPlanProtoUtility::constructAgg(int32_t pid, int8_t aggstage) {
+  int32_t uid = univPlanAggNewInstance(univplan, pid);
+
+  constructAggRefTargetEntry(-1, aggstage, dbcommon::COUNT, 1, 1,
+                             dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 2, dbcommon::TypeKind::INTID);
+  constructVarTargetEntry(-1, 1, 3, dbcommon::TypeKind::STRINGID);
+
+  int64_t numCols = 2;
+  int32_t *grpColIdx =
+      reinterpret_cast<int32_t *>(malloc(numCols * sizeof(int32_t)));
+  grpColIdx[0] = 1;
+  grpColIdx[1] = 3;
+  univPlanAggSetNumGroupsAndGroupColIndexes(univplan, 1000, numCols, grpColIdx);
+  free(grpColIdx);
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+int32_t UnivPlanProtoUtility::constructSort(int32_t pid) {
+  int32_t uid = univPlanSortNewInstance(univplan, pid);
+
+  int64_t numCols = 2;
+  int32_t *colIdx =
+      reinterpret_cast<int32_t *>(malloc(numCols * sizeof(int32_t)));
+  int32_t *mappingSortFuncId =
+      reinterpret_cast<int32_t *>(malloc(numCols * sizeof(int32_t)));
+  colIdx[0] = 1;
+  colIdx[1] = 2;
+  mappingSortFuncId[0] = dbcommon::BIGINT_LESS_THAN_BIGINT;
+  mappingSortFuncId[1] = dbcommon::INT_LESS_THAN_INT;
+  univPlanSortSetColIdx(univplan, numCols, colIdx);
+  univPlanSortSetSortFuncId(univplan, numCols, mappingSortFuncId);
+  free(colIdx);
+  free(mappingSortFuncId);
+
+  univPlanAddToPlanNode(univplan, true);
+  return uid;
+}
+
+void UnivPlanProtoUtility::constructRangeTable() {
+  univPlanRangeTblEntryAddDummy(univplan);
+
+  char location[] = "/tmp";
+  char fmtOptsJson[] = "option1 string in json";
+  int32_t columnNum = 2;
+  int32_t columnDataType[2] = {dbcommon::TypeKind::INTID,
+                               dbcommon::TypeKind::STRINGID};
+  int64_t columnDataTypeMod[2] = {-1, -1};
+  std::string columnName_buf[2] = {"p1", "p2"};
+  std::unique_ptr<const char *[]> columnName(new const char *[2]);
+  columnName[0] = columnName_buf[0].c_str();
+  columnName[1] = columnName_buf[1].c_str();
+
+  univPlanRangeTblEntryAddTable(univplan, 2, UnivPlanOrcFormat, location,
+                                fmtOptsJson, columnNum, columnName.get(),
+                                columnDataType, columnDataTypeMod);
+}
+
+void UnivPlanProtoUtility::constructReceiver() {
+  std::string addr_buf1[1] = {"mdw"};
+  std::unique_ptr<const char *[]> addr1(new const char *[1]);
+  addr1[0] = addr_buf1[0].c_str();
+  int32_t port1[2] = {101};
+
+  std::string addr_buf2[2] = {"smdw", "smdw"};
+  std::unique_ptr<const char *[]> addr2(new const char *[2]);
+  addr2[0] = addr_buf2[0].c_str();
+  addr2[1] = addr_buf2[1].c_str();
+  int32_t port2[2] = {201, 203};
+
+  univPlanReceiverAddListeners(univplan, 1, addr1.get(), port1);
+  univPlanReceiverAddListeners(univplan, 2, addr2.get(), port2);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/testutil/univplan-proto-util.h b/depends/univplan/src/univplan/testutil/univplan-proto-util.h
new file mode 100644
index 0000000..4e63e30
--- /dev/null
+++ b/depends/univplan/src/univplan/testutil/univplan-proto-util.h
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_TESTUTIL_UNIVPLAN_PROTO_UTIL_H_
+#define UNIVPLAN_SRC_UNIVPLAN_TESTUTIL_UNIVPLAN_PROTO_UTIL_H_
+
+#include <string>
+#include <utility>
+
+#include "univplan/cwrapper/univplan-c.h"
+
+#include "dbcommon/log/logger.h"
+#include "dbcommon/type/type-kind.h"
+
+#include "univplan/common/stagize.h"
+#include "univplan/common/var-util.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-tree.h"
+#include "univplan/univplanbuilder/univplanbuilder-plan.h"
+#include "univplan/univplanbuilder/univplanbuilder-table.h"
+#include "univplan/univplanbuilder/univplanbuilder.h"
+
+struct UnivPlanC {
+  univplan::UnivPlanBuilder::uptr upb;
+  univplan::UnivPlanBuilderNode::uptr curNode;
+  univplan::UnivPlanBuilderExprTree::uptr curExpr;
+  std::string seriaizedPlan;
+  UnivPlanCatchedError error;
+  std::string debugString;
+  uint16_t totalStageNo;
+};
+
+typedef enum NodeTag {
+  T_Invalid = 0,
+  T_BoolExpr = 313,
+} NodeTag;
+
+typedef struct Expr {
+  NodeTag type;
+} Expr;
+
+typedef enum BoolExprType { AND_EXPR, OR_EXPR, NOT_EXPR } BoolExprType;
+
+struct ListCell {
+  union {
+    void *ptr_value;
+    int int_value;
+    unsigned int oid_value;
+  } data;
+  ListCell *next;
+};
+
+typedef struct List {
+  NodeTag type; /* T_List, T_IntList, or T_OidList */
+  int length;
+  ListCell *head;
+  ListCell *tail;
+} List;
+
+typedef struct BoolExpr {
+  Expr xpr;
+  BoolExprType boolop;
+  List *args;   /* arguments to this expression */
+  int location; /* token location, or -1 if unknown */
+} BoolExpr;
+
+typedef enum NullTestType { IS_NULL, IS_NOT_NULL } NullTestType;
+
+typedef struct NullTest {
+  Expr xpr;
+  Expr *arg;                 /* input expression */
+  NullTestType nulltesttype; /* IS NULL, IS NOT NULL */
+} NullTest;
+
+namespace univplan {
+class UnivPlanProtoUtility {
+ public:
+  UnivPlanProtoUtility();
+  ~UnivPlanProtoUtility();
+
+  // like p op const
+  void constructVarOpConstExpr(int32_t pid, int32_t opFuncId, int32_t varNo,
+                               int32_t varAttNo, dbcommon::TypeKind varType,
+                               dbcommon::TypeKind constType,
+                               const char *buffer);
+
+  // like const op p
+  void constructConstOpVarExpr(int32_t pid, int32_t opFuncId, int32_t varNo,
+                               int32_t varAttNo, dbcommon::TypeKind varType,
+                               dbcommon::TypeKind constType,
+                               const char *buffer);
+  // like p op q
+  void constructVarOpVarExpr(int32_t pid, int32_t opFuncId, int32_t varNo1,
+                             int32_t varAttNo1, dbcommon::TypeKind varType1,
+                             int32_t varNo2, int32_t varAttNo2,
+                             dbcommon::TypeKind varType2);
+
+  // like const op const
+  void constructConstOpConstExpr(int32_t pid, int32_t opFuncId,
+                                 dbcommon::TypeKind constType1,
+                                 const char *buffer1,
+                                 dbcommon::TypeKind constType2,
+                                 const char *buffer2);
+
+  // like func op p
+  void constructFuncOpVarExpr(int32_t pid, int32_t opFuncId, int32_t varNo,
+                              int32_t varAttNo, dbcommon::TypeKind varType,
+                              int32_t mappingFuncId);
+
+  // like p op const or q op const
+  void constructBoolExpr(int32_t pid, int32_t opFuncId1, int32_t varNo1,
+                         int32_t varAttNo1, dbcommon::TypeKind varType1,
+                         dbcommon::TypeKind constType1, const char *buffer1,
+                         int32_t opFuncId2, int32_t varNo2, int32_t varAttNo2,
+                         dbcommon::TypeKind varType2,
+                         dbcommon::TypeKind constType2, const char *buffer2,
+                         int8_t boolType = 1);
+
+  // like p is (not) null
+  void constructNullTestExpr(int32_t pid, int8_t nulltesttype, int32_t varNo,
+                             int32_t varAttNo, dbcommon::TypeKind varType);
+
+  // like (p op const) op (q op const)
+  void constructVarOpConstThenOpVarOpConstExpr(
+      int32_t pid, int32_t opFuncId, int32_t opFuncId1, int32_t varNo1,
+      int32_t varAttNo1, dbcommon::TypeKind varType1,
+      dbcommon::TypeKind constType1, const char *buffer1, int32_t opFuncId2,
+      int32_t varNo2, int32_t varAttNo2, dbcommon::TypeKind varType2,
+      dbcommon::TypeKind constType2, const char *buffer2);
+
+  // TargetEntry with var
+  void constructVarTargetEntry(int32_t pid, int32_t varNo, int32_t varAttNo,
+                               dbcommon::TypeKind type);
+
+  // TargetEntry with const
+  void constructConstTargetEntry(int32_t pid, dbcommon::TypeKind constType,
+                                 const char *buffer);
+
+  // TargetEntry with const op const
+  void constructConstOpConstTargetEntry(int32_t pid, int32_t opFuncId,
+                                        dbcommon::TypeKind constType1,
+                                        const char *buffer1,
+                                        dbcommon::TypeKind constType2,
+                                        const char *buffer2);
+
+  // TargetEntry with Aggref
+  void constructAggRefTargetEntry(int32_t pid, int8_t aggstage, int32_t varNo,
+                                  int32_t mappingFuncId, int32_t varAttNo,
+                                  dbcommon::TypeKind type);
+
+  // TargetEntry with NullTest
+  void constructNullTestTargetEntry(int32_t pid, int8_t nulltesttype,
+                                    int32_t varNo, int32_t varAttNo,
+                                    dbcommon::TypeKind varType);
+
+  // TargetEntry with p op const
+  void constructVarOpConstTargetEntry(int32_t pid, int32_t opFuncId,
+                                      int32_t varNo, int32_t varAttNo,
+                                      dbcommon::TypeKind varType,
+                                      dbcommon::TypeKind constType,
+                                      const char *buffer);
+
+  // QualList like p op const
+  void constructVarOpConstQualList(int32_t pid, int32_t opFuncId, int32_t varNo,
+                                   int32_t varAttNo, dbcommon::TypeKind varType,
+                                   dbcommon::TypeKind constType,
+                                   const char *buffer);
+
+  // QualList like const op p
+  void constructConstOpVarQualList(int32_t pid, int32_t opFuncId, int32_t varNo,
+                                   int32_t varAttNo, dbcommon::TypeKind varType,
+                                   dbcommon::TypeKind constType,
+                                   const char *buffer);
+
+  // QualList like p op q
+  void constructVarOpVarQualList(int32_t pid, int32_t opFuncId, int32_t varNo1,
+                                 int32_t varAttNo1, dbcommon::TypeKind varType1,
+                                 int32_t varNo2, int32_t varAttNo2,
+                                 dbcommon::TypeKind varType2);
+
+  // QualList like func op p
+  void constructFuncOpVarQualList(int32_t pid, int32_t opFuncId, int32_t varNo,
+                                  int32_t varAttNo, dbcommon::TypeKind varType,
+                                  int32_t mappingFuncId);
+
+  // QualList like p op const or q op const
+  void constructBoolQualList(int32_t pid, int32_t opFuncId1, int32_t varNo1,
+                             int32_t varAttNo1, dbcommon::TypeKind varType1,
+                             dbcommon::TypeKind constType1, const char *buffer1,
+                             int32_t opFuncId2, int32_t varNo2,
+                             int32_t varAttNo2, dbcommon::TypeKind varType2,
+                             dbcommon::TypeKind constType2, const char *buffer2,
+                             int8_t boolType = 1);
+
+  // QualList with NullTest
+  void constructNullTestQualList(int32_t pid, int8_t nulltesttype,
+                                 int32_t varNo, int32_t varAttNo,
+                                 dbcommon::TypeKind varType);
+
+  // QualList like p op const or q op const
+  void constructVarOpConstThenOpVarOpConstQualList(
+      int32_t pid, int32_t opFuncId, int32_t opFuncId1, int32_t varNo1,
+      int32_t varAttNo1, dbcommon::TypeKind varType1,
+      dbcommon::TypeKind constType1, const char *buffer1, int32_t opFuncId2,
+      int32_t varNo2, int32_t varAttNo2, dbcommon::TypeKind varType2,
+      dbcommon::TypeKind constType2, const char *buffer2);
+
+  int32_t constructSeqScan(int32_t pid, bool wihQualList,
+                           int8_t targetEntryType);
+
+  int32_t constructConnector(int32_t pid, int8_t ctype, bool allTargetEntry);
+
+  int32_t constructSortConnector(int32_t pid, int8_t ctype);
+
+  void constructRangeTable();
+
+  void constructReceiver();
+
+  // when count/limit = -1, means there is no count/limit
+  int32_t constructLimitBelow(int32_t pid, int64_t count, int64_t offset);
+
+  // when count/limit = -1, means there is no count/limit
+  int32_t constructLimitTop(int32_t pid, int64_t count, int64_t offset);
+
+  int32_t constructAgg(int32_t pid, int8_t aggstage);
+
+  int32_t constructSort(int32_t pid);
+
+  void univPlanFixVarType() { ::univPlanFixVarType(univplan); }
+
+  void univPlanStagize() { ::univPlanStagize(univplan); }
+
+  const char *univPlanSerialize(int32_t *size) {
+    return ::univPlanSerialize(univplan, size, true);
+  }
+
+  const char *univPlanGetJsonFormatedPlan() {
+    return ::univPlanGetJsonFormatedPlan(univplan);
+  }
+
+  void univPlanSetDoInstrument(bool doInstrument) {
+    ::univPlanSetDoInstrument(univplan, doInstrument);
+  }
+
+  int32_t univPlanSeqScanNewInstance(int32_t pid) {
+    return ::univPlanSeqScanNewInstance(univplan, pid);
+  }
+
+  void univPlanAddToPlanNodeTest(bool isleft) {
+    ::univPlanAddToPlanNode(univplan, isleft);
+  }
+
+  UnivPlanC *getUnivPlan() { return std::move(univplan); }
+
+ private:
+  UnivPlanC *univplan = nullptr;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_TESTUTIL_UNIVPLAN_PROTO_UTIL_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-agg.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-agg.cc
new file mode 100644
index 0000000..ea0e4bc
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-agg.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-agg.h"
+
+namespace univplan {
+
+UnivPlanBuilderAgg::UnivPlanBuilderAgg() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanAgg());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderAgg::~UnivPlanBuilderAgg() {}
+
+UnivPlanBuilderPlanNodePoly::uptr UnivPlanBuilderAgg::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_AGG, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_agg(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderAgg::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_AGG);
+  ref->CopyFrom(node.agg());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderAgg::setNumGroups(int64_t n) { ref->set_numgroups(n); }
+
+void UnivPlanBuilderAgg::setGroupColIndexes(
+    const std::vector<int32_t> &nArray) {
+  for (int i = 0; i < nArray.size(); ++i) {
+    ref->add_groupcolindexes(nArray[i]);
+  }
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-agg.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-agg.h
new file mode 100644
index 0000000..4434f40
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-agg.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_AGG_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_AGG_H_
+
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderAgg : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderAgg();
+  virtual ~UnivPlanBuilderAgg();
+
+  typedef std::unique_ptr<UnivPlanBuilderAgg> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setNumGroups(int64_t n);
+  void setGroupColIndexes(const std::vector<int32_t> &nArray);
+
+ private:
+  UnivPlanAgg *ref;
+  std::unique_ptr<UnivPlanAgg> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_AGG_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-append.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-append.cc
new file mode 100644
index 0000000..efe4aaf
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-append.cc
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-append.h"
+
+namespace univplan {
+
+UnivPlanBuilderAppend::UnivPlanBuilderAppend() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanAppend());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderAppend::~UnivPlanBuilderAppend() {}
+
+UnivPlanBuilderPlanNodePoly::uptr UnivPlanBuilderAppend::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_APPEND, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_append(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderAppend::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_APPEND);
+  ref->CopyFrom(node.append());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderAppend::addAppendPlan(const UnivPlanPlanNodePoly &node) {
+  UnivPlanPlanNodePoly *poly = ref->add_appendplans();
+  poly->CopyFrom(node);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-append.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-append.h
new file mode 100644
index 0000000..9642647
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-append.h
@@ -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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_APPEND_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_APPEND_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderAppend : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderAppend();
+  virtual ~UnivPlanBuilderAppend();
+
+  typedef std::unique_ptr<UnivPlanBuilderAppend> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void addAppendPlan(const UnivPlanPlanNodePoly &node);
+
+ private:
+  UnivPlanAppend *ref;
+  std::unique_ptr<UnivPlanAppend> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_APPEND_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-column.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-column.h
new file mode 100644
index 0000000..0152e52
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-column.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_COLUMN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_COLUMN_H_
+
+#include <string>
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class UnivPlanBuilderColumn {
+ public:
+  explicit UnivPlanBuilderColumn(UnivPlanColumn *column) : ref(column) {}
+
+  virtual ~UnivPlanBuilderColumn() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderColumn> uptr;
+
+  void setColumnName(const std::string &columnName) {
+    ref->set_columnname(columnName);
+  }
+
+  void setTypeId(int32_t typeId) { ref->set_typeid_(typeId); }
+  void setTypeMod(int64_t typeMod) { ref->set_typemod(typeMod); }
+
+  void setScale1(int32_t scale) { ref->set_scale1(scale); }
+  void setScale2(int32_t scale) { ref->set_scale2(scale); }
+  void setIsNullable(bool nullable) { ref->set_isnullable(nullable); }
+
+ private:
+  UnivPlanColumn *ref = nullptr;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_COLUMN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-connector.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-connector.h
new file mode 100644
index 0000000..c48ae56
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-connector.h
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_CONNECTOR_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_CONNECTOR_H_
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderConnector : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderConnector() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanConnector);
+    ref = planNode.get();
+
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderConnector() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderConnector> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_CONNECTOR, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_connector(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_CONNECTOR);
+    ref->CopyFrom(node.connector());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  void setConnectorType(UNIVPLANCONNECTORTYPE type) { ref->set_type(type); }
+
+  void setStageNo(int stageNo) { ref->set_stageno(stageNo); }
+
+  void setMagmaTable(bool stageNo) { ref->set_magmatable(stageNo); }
+
+  void setMagmaMap(int *map) {
+    int rangeNum = ref->rangenum();
+    for (int i = 0; i < rangeNum; ++i) {
+      ref->add_magmamap(map[i]);
+    }
+  }
+
+  void setRangeNum(int rangeNum) { ref->set_rangenum(rangeNum); }
+
+  void setColIdx(const std::vector<int32_t> &nArray) {
+    for (int i = 0; i < nArray.size(); ++i) {
+      ref->add_colidx(nArray[i]);
+    }
+  }
+
+  void setSortFuncId(const std::vector<int32_t> &nArray) {
+    for (int i = 0; i < nArray.size(); ++i) {
+      ref->add_sortfuncid(nArray[i]);
+    }
+  }
+
+  void addHashExpr(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_hashexpr()->AddAllocated(exprTree->ownExprPoly().release());
+  }
+
+ private:
+  UnivPlanConnector *ref;
+  std::unique_ptr<UnivPlanConnector> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_CONNECTOR_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-node.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-node.h
new file mode 100644
index 0000000..8196919
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-node.h
@@ -0,0 +1,736 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_NODE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_NODE_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-poly.h"
+
+namespace univplan {
+
+class UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderExprNode> uptr;
+
+  UnivPlanBuilderExprNode() {}
+
+  UnivPlanBuilderExprNode(int32_t pid, int32_t uid) : pid(pid), uid(uid) {}
+
+  virtual ~UnivPlanBuilderExprNode() {}
+
+  // Create a UnivPlanBuilderExprPoly instance and transfer out the ownership.
+  virtual std::unique_ptr<UnivPlanBuilderExprPoly> getExprPolyBuilder() = 0;
+
+  virtual UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) {
+    LOG_ERROR(ERRCODE_INTERNAL_ERROR, "add args not implemented");
+  }
+
+  int32_t pid = -1;
+  int32_t uid = -1;
+};
+
+class UnivPlanBuilderVar final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderVar> uptr;
+
+  UnivPlanBuilderVar() : node_(new UnivPlanVar), ref_(node_.get()) {}
+
+  UnivPlanBuilderVar(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanVar),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_VAR));
+    exprPoly->getExprPoly()->set_allocated_var(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderVar &setVarNo(uint32_t varNo) {
+    ref_->set_varno(varNo);
+    return *this;
+  }
+
+  UnivPlanBuilderVar &setVarAttNo(int32_t varAttNo) {
+    ref_->set_varattno(varAttNo);
+    return *this;
+  }
+
+  UnivPlanBuilderVar &setTypeId(int32_t typeId) {
+    ref_->set_typeid_(typeId);
+    return *this;
+  }
+
+  UnivPlanBuilderVar &setTypeMod(int64_t typeMod) {
+    ref_->set_typemod(typeMod);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanVar> node_;
+  UnivPlanVar *ref_;
+};
+
+class UnivPlanBuilderConst final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderConst> uptr;
+
+  UnivPlanBuilderConst() : node_(new UnivPlanConst), ref_(node_.get()) {}
+
+  UnivPlanBuilderConst(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanConst),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_CONST));
+    exprPoly->getExprPoly()->set_allocated_val(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderConst &setType(int32_t type) {
+    ref_->set_type(type);
+    return *this;
+  }
+
+  UnivPlanBuilderConst &setTypeMod(int64_t typeMod) {
+    ref_->set_typemod(typeMod);
+    return *this;
+  }
+
+  UnivPlanBuilderConst &setIsNull(bool isNull) {
+    ref_->set_isnull(isNull);
+    return *this;
+  }
+
+  UnivPlanBuilderConst &setValue(const std::string &value) {
+    ref_->set_value(value);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanConst> node_;
+  UnivPlanConst *ref_;
+};
+
+class UnivPlanBuilderOpExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderOpExpr> uptr;
+
+  UnivPlanBuilderOpExpr() : node_(new UnivPlanOpExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderOpExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanOpExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_OPEXPR));
+    exprPoly->getExprPoly()->set_allocated_opexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  UnivPlanBuilderOpExpr &setRetType(int32_t retType) {
+    ref_->set_rettype(retType);
+    return *this;
+  }
+
+  UnivPlanBuilderOpExpr &setFuncId(int32_t funcId) {
+    ref_->set_funcid(funcId);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanOpExpr> node_;
+  UnivPlanOpExpr *ref_;
+};
+
+class UnivPlanBuilderFuncExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderFuncExpr> uptr;
+
+  UnivPlanBuilderFuncExpr() : node_(new UnivPlanFuncExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderFuncExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanFuncExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_FUNCEXPR));
+    exprPoly->getExprPoly()->set_allocated_funcexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderFuncExpr &setFuncId(int32_t funcId) {
+    ref_->set_funcid(funcId);
+    return *this;
+  }
+
+  UnivPlanBuilderFuncExpr &setRetType(int32_t retType) {
+    ref_->set_rettype(retType);
+    return *this;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanFuncExpr> node_;
+  UnivPlanFuncExpr *ref_;
+};
+
+class UnivPlanBuilderAggref final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderAggref> uptr;
+
+  UnivPlanBuilderAggref() : node_(new UnivPlanAggref), ref_(node_.get()) {}
+
+  UnivPlanBuilderAggref(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanAggref),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_AGGREF));
+    exprPoly->getExprPoly()->set_allocated_aggref(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderAggref &setFuncId(int32_t funcId) {
+    ref_->set_funcid(funcId);
+
+    return *this;
+  }
+
+  UnivPlanBuilderAggref &setTransFuncId(int32_t funcId) {
+    ref_->set_transfuncid(funcId);
+    return *this;
+  }
+
+  UnivPlanBuilderAggref &setFinalFuncId(int32_t funcId) {
+    ref_->set_finalfuncid(funcId);
+    return *this;
+  }
+
+  UnivPlanBuilderAggref &setRetType(int32_t retType) {
+    ref_->set_rettype(retType);
+    return *this;
+  }
+
+  UnivPlanBuilderAggref &setTransInitVal(bool transInitVal) {
+    ref_->set_transinitval(transInitVal);
+    return *this;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanAggref> node_;
+  UnivPlanAggref *ref_;
+};
+
+class UnivPlanBuilderBoolExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderBoolExpr> uptr;
+
+  UnivPlanBuilderBoolExpr() : node_(new UnivPlanBoolExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderBoolExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanBoolExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_BOOLEXPR));
+    exprPoly->getExprPoly()->set_allocated_boolexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderBoolExpr &setType(BOOLEXPRTYPE type) {
+    ref_->set_type(type);
+    return *this;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanBoolExpr> node_;
+  UnivPlanBoolExpr *ref_;
+};
+
+class UnivPlanBuilderNullTest final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderNullTest> uptr;
+
+  UnivPlanBuilderNullTest() : node_(new UnivPlanNullTest), ref_(node_.get()) {}
+
+  UnivPlanBuilderNullTest(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanNullTest),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_NULLTEST));
+    exprPoly->getExprPoly()->set_allocated_nulltest(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderNullTest &setType(NULLTESTTYPE type) {
+    ref_->set_type(type);
+    return *this;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->set_allocated_arg(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanNullTest> node_;
+  UnivPlanNullTest *ref_;
+};
+
+class UnivPlanBuilderBooleanTest final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderBooleanTest> uptr;
+
+  UnivPlanBuilderBooleanTest()
+      : node_(new UnivPlanBooleanTest), ref_(node_.get()) {}
+
+  UnivPlanBuilderBooleanTest(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanBooleanTest),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_BOOLEANTEST));
+    exprPoly->getExprPoly()->set_allocated_booltest(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderBooleanTest &setType(BOOLTESTTYPE type) {
+    ref_->set_type(type);
+    return *this;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->set_allocated_arg(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanBooleanTest> node_;
+  UnivPlanBooleanTest *ref_;
+};
+
+class UnivPlanBuilderCaseExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderCaseExpr> uptr;
+
+  UnivPlanBuilderCaseExpr() : node_(new UnivPlanCaseExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderCaseExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanCaseExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_CASEEXPR));
+    exprPoly->getExprPoly()->set_allocated_caseexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderCaseExpr &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    if (addingDefresult_) return setDefresult(std::move(arg));
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  UnivPlanBuilderCaseExpr &setCasetype(int32_t casetype) {
+    ref_->set_casetype(casetype);
+    return *this;
+  }
+
+  void setAddingDefresult() { addingDefresult_ = true; }
+
+  UnivPlanBuilderCaseExpr &setDefresult(UnivPlanBuilderExprPoly::uptr arg) {
+    ref_->set_allocated_defresult(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanCaseExpr> node_;
+  UnivPlanCaseExpr *ref_;
+  bool addingDefresult_ = false;
+};
+
+class UnivPlanBuilderCaseWhen final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderCaseWhen> uptr;
+
+  UnivPlanBuilderCaseWhen() : node_(new UnivPlanCaseWhen), ref_(node_.get()) {}
+
+  UnivPlanBuilderCaseWhen(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanCaseWhen),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_CASEWHEN));
+    exprPoly->getExprPoly()->set_allocated_casewhen(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderCaseWhen &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    if (addingResult_) return setResult(std::move(arg));
+    ref_->set_allocated_expr(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  void setAddingResult() { addingResult_ = true; }
+
+  UnivPlanBuilderCaseWhen &setResult(UnivPlanBuilderExprPoly::uptr arg) {
+    ref_->set_allocated_result(arg->ownExprPoly().release());
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanCaseWhen> node_;
+  UnivPlanCaseWhen *ref_;
+  bool addingResult_ = false;
+};
+
+class UnivPlanBuilderSubPlan final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderSubPlan> uptr;
+
+  UnivPlanBuilderSubPlan() : node_(new UnivPlanSubPlan), ref_(node_.get()) {}
+
+  UnivPlanBuilderSubPlan(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanSubPlan),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_SUBPLAN));
+    exprPoly->getExprPoly()->set_allocated_subplan(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderSubPlan &setSubLinkType(SUBLINKTYPE subLinkType) {
+    ref_->set_sublinktype(subLinkType);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &setPlanId(int32_t planId) {
+    ref_->set_planid(planId);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &setTypeId(int32_t typeId) {
+    ref_->set_typeid_(typeId);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &setTypeMod(int64_t typeMod) {
+    ref_->set_typemod(typeMod);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &setUseHashTable(bool useHashTable) {
+    ref_->set_usehashtable(useHashTable);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &setInitPlan(bool initPlan) {
+    ref_->set_initplan(initPlan);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &addSetParam(int32_t setParam) {
+    ref_->add_setparam(setParam);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &addParParam(int32_t parParam) {
+    ref_->add_parentparam(parParam);
+    return *this;
+  }
+
+  UnivPlanBuilderSubPlan &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    if (addingTestexpr_) {
+      ref_->set_allocated_testexpr(arg->ownExprPoly().release());
+    } else {
+      ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    }
+    return *this;
+  }
+
+  void setAddingTestexpr() { addingTestexpr_ = true; }
+
+  UnivPlanBuilderSubPlan &addTestexprParam(int32_t testexprParam) {
+    ref_->add_testexprparam(testexprParam);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanSubPlan> node_;
+  UnivPlanSubPlan *ref_;
+  bool addingTestexpr_ = false;
+};
+
+class UnivPlanBuilderParam final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderParam> uptr;
+
+  UnivPlanBuilderParam() : node_(new UnivPlanParam), ref_(node_.get()) {}
+
+  UnivPlanBuilderParam(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanParam),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_PARAM));
+    exprPoly->getExprPoly()->set_allocated_param(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderParam &setParamKind(PARAMKIND paramKind) {
+    ref_->set_paramkind(paramKind);
+    return *this;
+  }
+
+  UnivPlanBuilderParam &setParamId(int32_t paramId) {
+    ref_->set_paramid(paramId);
+    return *this;
+  }
+
+  UnivPlanBuilderParam &setTypeId(int32_t typeId) {
+    ref_->set_typeid_(typeId);
+    return *this;
+  }
+
+  UnivPlanBuilderParam &setTypeMod(int64_t typeMod) {
+    ref_->set_typemod(typeMod);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanParam> node_;
+  UnivPlanParam *ref_;
+};
+
+class UnivPlanBuilderScalarArrayOpExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderOpExpr> uptr;
+
+  UnivPlanBuilderScalarArrayOpExpr()
+      : node_(new UnivPlanScalarArrayOpExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderScalarArrayOpExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanScalarArrayOpExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_SCALARARRAYOPEXPR));
+    exprPoly->getExprPoly()->set_allocated_scalararrayopexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  UnivPlanBuilderScalarArrayOpExpr &setUseOr(bool useOr) {
+    ref_->set_useor(useOr);
+    return *this;
+  }
+
+  UnivPlanBuilderScalarArrayOpExpr &setFuncId(int32_t funcId) {
+    ref_->set_funcid(funcId);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanScalarArrayOpExpr> node_;
+  UnivPlanScalarArrayOpExpr *ref_;
+};
+
+class UnivPlanBuilderCoalesceExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderCoalesceExpr> uptr;
+
+  UnivPlanBuilderCoalesceExpr()
+      : node_(new UnivPlanCoalesceExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderCoalesceExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanCoalesceExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_COALESCEEXPR));
+    exprPoly->getExprPoly()->set_allocated_coalesceexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderCoalesceExpr &addArgs(
+      UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  UnivPlanBuilderCoalesceExpr &setCoalesceType(int32_t coalesceType) {
+    ref_->set_coalescetype(coalesceType);
+    return *this;
+  }
+
+  UnivPlanBuilderCoalesceExpr &setCoalesceTypeMod(int32_t coalesceTypeMod) {
+    ref_->set_coalescetypemod(coalesceTypeMod);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanCoalesceExpr> node_;
+  UnivPlanCoalesceExpr *ref_;
+};
+
+class UnivPlanBuilderNullIfExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderNullIfExpr> uptr;
+
+  UnivPlanBuilderNullIfExpr()
+      : node_(new UnivPlanNullIfExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderNullIfExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanNullIfExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_NULLIFEXPR));
+    exprPoly->getExprPoly()->set_allocated_nullifexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderNullIfExpr &addArgs(
+      UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  UnivPlanBuilderNullIfExpr &setRetType(int32_t retType) {
+    ref_->set_rettype(retType);
+    return *this;
+  }
+
+  UnivPlanBuilderNullIfExpr &setTypeMod(int32_t typeMod) {
+    ref_->set_typemod(typeMod);
+    return *this;
+  }
+
+  UnivPlanBuilderNullIfExpr &setFuncId(int32_t funcId) {
+    ref_->set_funcid(funcId);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanNullIfExpr> node_;
+  UnivPlanNullIfExpr *ref_;
+};
+
+class UnivPlanBuilderDistinctExpr final : public UnivPlanBuilderExprNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderDistinctExpr> uptr;
+
+  UnivPlanBuilderDistinctExpr()
+      : node_(new UnivPlanDistinctExpr), ref_(node_.get()) {}
+
+  UnivPlanBuilderDistinctExpr(int32_t pid, int32_t uid)
+      : UnivPlanBuilderExprNode(pid, uid),
+        node_(new UnivPlanDistinctExpr),
+        ref_(node_.get()) {}
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() override {
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_DISTINCTEXPR));
+    exprPoly->getExprPoly()->set_allocated_distinctexpr(node_.release());
+    return exprPoly;
+  }
+
+  UnivPlanBuilderExprNode &addArgs(UnivPlanBuilderExprPoly::uptr arg) override {
+    ref_->mutable_args()->AddAllocated(arg->ownExprPoly().release());
+    return *this;
+  }
+
+  UnivPlanBuilderDistinctExpr &setRetType(int32_t retType) {
+    ref_->set_rettype(retType);
+    return *this;
+  }
+
+  UnivPlanBuilderDistinctExpr &setFuncId(int32_t funcId) {
+    ref_->set_funcid(funcId);
+    return *this;
+  }
+
+ private:
+  std::unique_ptr<UnivPlanDistinctExpr> node_;
+  UnivPlanDistinctExpr *ref_;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_NODE_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-poly.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-poly.h
new file mode 100644
index 0000000..f783dc8
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-poly.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_POLY_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_POLY_H_
+
+#include "dbcommon/log/logger.h"
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class UnivPlanBuilderExprPoly {
+ public:
+  explicit UnivPlanBuilderExprPoly(UNIVPLANEXPRTYPE type) {
+    exprPoly.reset(new UnivPlanExprPoly());
+    ref = exprPoly.get();
+    ref->set_type(type);
+  }
+
+  virtual ~UnivPlanBuilderExprPoly() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderExprPoly> uptr;
+
+  UnivPlanExprPoly *getExprPoly() { return ref; }
+
+  std::unique_ptr<UnivPlanExprPoly> ownExprPoly() {
+    return std::move(exprPoly);
+  }
+
+ private:
+  UnivPlanExprPoly *ref;
+  std::unique_ptr<UnivPlanExprPoly> exprPoly;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_POLY_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-tree.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-tree.h
new file mode 100644
index 0000000..9c59186
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-expr-tree.h
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_TREE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_TREE_H_
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "univplan/common/univplan-type.h"
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-node.h"
+
+namespace univplan {
+class UnivPlanBuilderExprTree {
+ public:
+  UnivPlanBuilderExprTree() {}
+  typedef std::unique_ptr<UnivPlanBuilderExprTree> uptr;
+
+  std::unique_ptr<UnivPlanExprPoly> ownExprPoly() { return std::move(root); }
+  void setRoot(UnivPlanBuilderExprPoly::uptr exprPoly) {
+    root = std::move(exprPoly->ownExprPoly());
+  }
+
+  int32_t getUid() { return nodeCounter++; }
+
+  // Create a new UnivPlanBuilderExprNode
+  // @param pid Parent ID for the new UnivPlanBuilderExprNode
+  template <typename UnivPlanBuilderExprNode>
+  std::unique_ptr<UnivPlanBuilderExprNode> ExprNodeFactory(int32_t pid) {
+    return std::unique_ptr<UnivPlanBuilderExprNode>(
+        new UnivPlanBuilderExprNode(pid, getUid()));
+  }
+
+  void addExprNode(UnivPlanBuilderExprNode::uptr bld) {
+    UnivPlanBuilderExprPoly::uptr pn = bld->getExprPolyBuilder();
+    int32_t uid = bld->uid;  // required field
+    int32_t pid = bld->pid;  // required field
+
+    // Check if this node has node id set already
+    if (uid < 0)
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "UnivPlanBuilderExprTree::addExprNode uid is negative");
+
+    // Check if the id is unique
+    if (idToExprNode.find(uid) != idToExprNode.end())
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "UnivPlanBuilderExprTree::addExprNode uid is duplicate");
+
+    // Use hash table to track this node quickly
+    idToExprNode[uid] = std::move(bld);
+
+    if (pid < 0) {
+      // If this node has no parent id, that means this node is a root, it is
+      // not allowed to have more than one root plan node.
+
+      // Set as a root plan node
+      setRoot(std::move(pn));
+
+    } else {
+      // If this node has a parent id, that means this node should be connected
+      // to its expected parent node.
+
+      // Find its parent node if its parent id is specified
+      if (idToExprNode.find(pid) == idToExprNode.end())
+        LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                  "UnivPlanBuilderExprTree::addExprNode unexpected parent id "
+                  "%d found",
+                  pid);
+
+      // add to parent node
+      idToExprNode[pid]->addArgs(std::move(pn));
+    }
+  }
+
+  UnivPlanBuilderExprNode* getExprNode(int32_t uid) {
+    return idToExprNode[uid].get();
+  }
+
+ private:
+  std::unique_ptr<UnivPlanExprPoly> root = nullptr;
+  std::map<int32_t, UnivPlanBuilderExprNode::uptr> idToExprNode;
+  int32_t nodeCounter = 0;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXPR_TREE_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-filter.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-filter.cc
new file mode 100644
index 0000000..734be3f
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-filter.cc
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-ext-gs-filter.h"
+
+namespace univplan {
+
+UnivPlanBuilderExtGSFilter::UnivPlanBuilderExtGSFilter()
+    : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanExtGSFilter());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderExtGSFilter::~UnivPlanBuilderExtGSFilter() {}
+
+void UnivPlanBuilderExtGSFilter::setExpr(
+    UnivPlanBuilderExprPoly::uptr exprPoly) {
+  ref->set_allocated_filter(exprPoly->ownExprPoly().release());
+}
+
+std::unique_ptr<UnivPlanBuilderPlanNodePoly>
+UnivPlanBuilderExtGSFilter::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(new UnivPlanBuilderPlanNodePoly(
+      UNIVPLAN_EXT_GS_FILTER, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_extgsfilter(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderExtGSFilter::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_EXT_GS_FILTER);
+  ref->CopyFrom(node.extgsfilter());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-filter.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-filter.h
new file mode 100644
index 0000000..63442ea
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-filter.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_FILTER_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_FILTER_H_
+
+#include <memory>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderExtGSFilter : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderExtGSFilter();
+  virtual ~UnivPlanBuilderExtGSFilter();
+
+  typedef std::unique_ptr<UnivPlanBuilderExtGSFilter> uptr;
+
+  std::unique_ptr<UnivPlanBuilderPlanNodePoly> ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setExpr(UnivPlanBuilderExprPoly::uptr exprPoly);
+
+ private:
+  UnivPlanExtGSFilter *ref;
+  std::unique_ptr<UnivPlanExtGSFilter> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_FILTER_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-proj.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-proj.cc
new file mode 100644
index 0000000..95baa59
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-proj.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-ext-gs-proj.h"
+
+namespace univplan {
+
+UnivPlanBuilderExtGSProject::UnivPlanBuilderExtGSProject()
+    : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanExtGSProj());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderExtGSProject::~UnivPlanBuilderExtGSProject() {}
+
+void UnivPlanBuilderExtGSProject::setColumnIndexes(
+    const std::vector<int32_t> &colIndexes) {
+  for (int i = 0; i < colIndexes.size(); ++i) {
+    ref->add_columnindexes(colIndexes[i]);
+  }
+}
+
+std::unique_ptr<UnivPlanBuilderPlanNodePoly>
+UnivPlanBuilderExtGSProject::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_EXT_GS_PROJ, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_extgsproject(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderExtGSProject::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_EXT_GS_PROJ);
+  ref->CopyFrom(node.extgsproject());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-proj.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-proj.h
new file mode 100644
index 0000000..4d7fb31
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-proj.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_PROJ_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_PROJ_H_
+
+#include <memory>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderExtGSProject : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderExtGSProject();
+  virtual ~UnivPlanBuilderExtGSProject();
+
+  typedef std::unique_ptr<UnivPlanBuilderExtGSProject> uptr;
+
+  std::unique_ptr<UnivPlanBuilderPlanNodePoly> ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setColumnIndexes(const std::vector<int32_t> &colIndexes);
+
+ private:
+  UnivPlanExtGSProj *ref;
+  std::unique_ptr<UnivPlanExtGSProj> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_PROJ_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-scan.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-scan.cc
new file mode 100644
index 0000000..1352fd5
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-scan.cc
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-ext-gs-scan.h"
+
+#include <utility>
+#include <vector>
+
+namespace univplan {
+
+UnivPlanBuilderExtGSScan::UnivPlanBuilderExtGSScan() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanExtGSScan());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderExtGSScan::~UnivPlanBuilderExtGSScan() {}
+
+std::unique_ptr<UnivPlanBuilderPlanNodePoly>
+UnivPlanBuilderExtGSScan::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_EXT_GS_SCAN, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_extgsscan(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderExtGSScan::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_EXT_GS_SCAN);
+  ref->CopyFrom(node.extgsscan());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderExtGSScan::setScanRelId(uint32_t id) { ref->set_relid(id); }
+
+void UnivPlanBuilderExtGSScan::setScanIndex(bool index = false) {
+  ref->set_indexscan(index);
+}
+
+void UnivPlanBuilderExtGSScan::setIndexScanType(ExternalScanType type) {
+  ref->set_type(univplan::ExternalScanType(type));
+}
+
+void UnivPlanBuilderExtGSScan::setDirectionType(
+    ExternalScanDirection direction) {
+  ref->set_direction(univplan::ExternalScanDirection(direction));
+}
+
+void UnivPlanBuilderExtGSScan::setIndexName(const char *indexName) {
+  ref->set_indexname(indexName);
+}
+
+void UnivPlanBuilderExtGSScan::addIndexQual(
+    UnivPlanBuilderExprTree::uptr exprTree) {
+  ref->mutable_indexqual()->AddAllocated(exprTree->ownExprPoly().release());
+}
+
+void UnivPlanBuilderExtGSScan::setColumnsToRead(
+    const std::vector<int32_t> &nArray) {
+  for (int i = 0; i < nArray.size(); ++i) {
+    ref->add_columnstoread(nArray[i]);
+  }
+}
+
+void UnivPlanBuilderExtGSScan::setKeyColumnIndexes(
+    const std::vector<int32_t> &nArray) {
+  for (int i = 0; i < nArray.size(); ++i) {
+    ref->add_keycolindex(nArray[i]);
+  }
+}
+
+void UnivPlanBuilderExtGSScan::setFilterExpr(
+    UnivPlanBuilderExprTree::uptr expr) {
+  ref->set_allocated_filter(expr->ownExprPoly().release());
+}
+
+std::unique_ptr<UnivPlanBuilderScanTask>
+UnivPlanBuilderExtGSScan::addScanTaskAndGetBuilder() {
+  UnivPlanScanTask *task = ref->add_tasks();
+  std::unique_ptr<UnivPlanBuilderScanTask> res(
+      new UnivPlanBuilderScanTask(task));
+  return std::move(res);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-scan.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-scan.h
new file mode 100644
index 0000000..5a3c54d
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-ext-gs-scan.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_SCAN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_SCAN_H_
+
+#include <memory>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+#include "univplan/univplanbuilder/univplanbuilder-scan-task.h"
+
+namespace univplan {
+
+class UnivPlanBuilderExtGSScan : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderExtGSScan();
+  virtual ~UnivPlanBuilderExtGSScan();
+
+  typedef std::unique_ptr<UnivPlanBuilderExtGSScan> uptr;
+
+  std::unique_ptr<UnivPlanBuilderPlanNodePoly> ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setScanRelId(uint32_t id);
+  void setColumnsToRead(const std::vector<int32_t> &nArray);
+  void setKeyColumnIndexes(const std::vector<int32_t> &nArray);
+  void setFilterExpr(UnivPlanBuilderExprTree::uptr expr);
+  std::unique_ptr<UnivPlanBuilderScanTask> addScanTaskAndGetBuilder();
+  // set magma index info
+  void setScanIndex(bool index);
+  void setIndexScanType(ExternalScanType type);
+  void setDirectionType(ExternalScanDirection direction);
+  void setIndexName(const char *indexName);
+  void addIndexQual(UnivPlanBuilderExprTree::uptr exprTree);
+
+ private:
+  UnivPlanExtGSScan *ref;
+  std::unique_ptr<UnivPlanExtGSScan> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_EXT_GS_SCAN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-hash.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-hash.h
new file mode 100644
index 0000000..05acb59
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-hash.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_HASH_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_HASH_H_
+
+#include <memory>
+#include <utility>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderHash : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderHash() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanHash());
+    ref = planNode.get();
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderHash() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderHash> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_HASH, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_hash(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_HASH);
+    ref->CopyFrom(node.hash());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+ private:
+  UnivPlanHash *ref;
+  std::unique_ptr<UnivPlanHash> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_HASH_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-hashjoin.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-hashjoin.h
new file mode 100644
index 0000000..a0d51f1
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-hashjoin.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_HASHJOIN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_HASHJOIN_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderHashJoin : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderHashJoin() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanHashJoin());
+    ref = planNode.get();
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderHashJoin() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderHashJoin> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_HASHJOIN, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_hashjoin(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_HASHJOIN);
+    ref->CopyFrom(node.hashjoin());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  void addJoinQual(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_joinqual()->AddAllocated(exprTree->ownExprPoly().release());
+  }
+
+  void addHashClause(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_hashclauses()->AddAllocated(exprTree->ownExprPoly().release());
+  }
+
+  void addHashQualClause(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_hashqualclauses()->AddAllocated(
+        exprTree->ownExprPoly().release());
+  }
+
+  void setJoinType(UNIVPLANJOINTYPE type) { ref->set_type(type); }
+
+ private:
+  UnivPlanHashJoin *ref;
+  std::unique_ptr<UnivPlanHashJoin> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_HASHJOIN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-insert.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-insert.cc
new file mode 100644
index 0000000..a43aba8
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-insert.cc
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-insert.h"
+
+namespace univplan {
+
+UnivPlanBuilderInsert::UnivPlanBuilderInsert() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanInsert());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderInsert::~UnivPlanBuilderInsert() {}
+
+UnivPlanBuilderPlanNodePoly::uptr UnivPlanBuilderInsert::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_INSERT, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_insert(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderInsert::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_INSERT);
+  ref->CopyFrom(node.insert());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderInsert::setInsertRelId(uint32_t id) { ref->set_relid(id); }
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-insert.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-insert.h
new file mode 100644
index 0000000..910891a
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-insert.h
@@ -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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_INSERT_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_INSERT_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderInsert : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderInsert();
+  virtual ~UnivPlanBuilderInsert();
+
+  typedef std::unique_ptr<UnivPlanBuilderInsert> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setInsertRelId(uint32_t id);
+
+ private:
+  UnivPlanInsert *ref;
+  std::unique_ptr<UnivPlanInsert> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_INSERT_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-limit.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-limit.cc
new file mode 100644
index 0000000..d65c119
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-limit.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-limit.h"
+
+namespace univplan {
+
+UnivPlanBuilderLimit::UnivPlanBuilderLimit() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanLimit());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderLimit::~UnivPlanBuilderLimit() {}
+
+UnivPlanBuilderPlanNodePoly::uptr UnivPlanBuilderLimit::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_LIMIT, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_limit(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderLimit::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_LIMIT);
+  ref->CopyFrom(node.limit());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderLimit::setLimitOffset(
+    UnivPlanBuilderExprTree::uptr offset) {
+  ref->set_allocated_limitoffset(offset->ownExprPoly().release());
+}
+
+void UnivPlanBuilderLimit::setLimitCount(UnivPlanBuilderExprTree::uptr count) {
+  ref->set_allocated_limitcount(count->ownExprPoly().release());
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-limit.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-limit.h
new file mode 100644
index 0000000..0f61f54
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-limit.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_LIMIT_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_LIMIT_H_
+
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderLimit : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderLimit();
+  virtual ~UnivPlanBuilderLimit();
+
+  typedef std::unique_ptr<UnivPlanBuilderLimit> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setLimitOffset(UnivPlanBuilderExprTree::uptr limitOffset);
+
+  void setLimitCount(UnivPlanBuilderExprTree::uptr limitCount);
+
+ private:
+  UnivPlanLimit *ref;
+  std::unique_ptr<UnivPlanLimit> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_LIMIT_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-listener.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-listener.h
new file mode 100644
index 0000000..ae8be2a
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-listener.h
@@ -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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_LISTENER_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_LISTENER_H_
+
+#include <string>
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class UnivPlanBuilderListener {
+ public:
+  explicit UnivPlanBuilderListener(UnivPlanListener *listener)
+      : ref(listener) {}
+
+  virtual ~UnivPlanBuilderListener() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderListener> uptr;
+
+  void setAddress(const std::string &addr) { ref->set_address(addr); }
+
+  void setPort(int32_t port) { ref->set_port(port); }
+
+ private:
+  UnivPlanListener *ref;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_LISTENER_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-material.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-material.h
new file mode 100644
index 0000000..bdb1dd2
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-material.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_MATERIAL_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_MATERIAL_H_
+
+#include <memory>
+#include <utility>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderMaterial : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderMaterial() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanMaterial());
+    ref = planNode.get();
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderMaterial() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderMaterial> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_MATERIAL, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_material(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly& node) override {
+    assert(node.type() == UNIVPLAN_MATERIAL);
+    ref->CopyFrom(node.material());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  UnivPlanBuilderMaterial& setShareType(UNIVPLANSHARETYPE type) {
+    ref->set_share_type(type);
+    return *this;
+  }
+
+  UnivPlanBuilderMaterial& setCdbStrict(bool cdbStrict) {
+    ref->set_cdbstrict(cdbStrict);
+    return *this;
+  }
+
+  UnivPlanBuilderMaterial& setShareId(int32_t shareId) {
+    ref->set_shared_id(shareId);
+    return *this;
+  }
+
+  UnivPlanBuilderMaterial& setDriverSlice(int32_t driverSlice) {
+    ref->set_driver_slice(driverSlice);
+    return *this;
+  }
+
+  UnivPlanBuilderMaterial& setNSharer(int32_t nsharer) {
+    ref->set_nsharer(nsharer);
+    return *this;
+  }
+
+  UnivPlanBuilderMaterial& setNSharerXSlice(int32_t xslice) {
+    ref->set_nsharer_xslice(xslice);
+    return *this;
+  }
+
+ private:
+  UnivPlanMaterial* ref;
+  std::unique_ptr<UnivPlanMaterial> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_MATERIAL_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-mergejoin.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-mergejoin.h
new file mode 100644
index 0000000..824e2de
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-mergejoin.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_MERGEJOIN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_MERGEJOIN_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderMergeJoin : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderMergeJoin() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanMergeJoin());
+    ref = planNode.get();
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderMergeJoin() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderMergeJoin> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_MERGEJOIN, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_mergejoin(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_MERGEJOIN);
+    ref->CopyFrom(node.mergejoin());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  void addJoinQual(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_joinqual()->AddAllocated(exprTree->ownExprPoly().release());
+  }
+
+  void addMergeClause(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_mergeclauses()->AddAllocated(
+        exprTree->ownExprPoly().release());
+  }
+
+  void setJoinType(UNIVPLANJOINTYPE type) { ref->set_type(type); }
+
+ private:
+  UnivPlanMergeJoin *ref;
+  std::unique_ptr<UnivPlanMergeJoin> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_MERGEJOIN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-nestloop.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-nestloop.h
new file mode 100644
index 0000000..193ce75
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-nestloop.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_NESTLOOP_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_NESTLOOP_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderNestLoop : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderNestLoop() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanNestLoop());
+    ref = planNode.get();
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderNestLoop() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderNestLoop> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_NESTLOOP, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_nestloop(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_NESTLOOP);
+    ref->CopyFrom(node.nestloop());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  void addJoinQual(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_joinqual()->AddAllocated(exprTree->ownExprPoly().release());
+  }
+
+  void setJoinType(UNIVPLANJOINTYPE type) { ref->set_type(type); }
+
+ private:
+  UnivPlanNestLoop *ref;
+  std::unique_ptr<UnivPlanNestLoop> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_NESTLOOP_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-node.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-node.cc
new file mode 100644
index 0000000..cd86ff5
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-node.cc
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+#include <utility>
+
+namespace univplan {
+
+void UnivPlanBuilderNode::setNodeLeftPlan(
+    UnivPlanBuilderPlanNodePoly::uptr pn) {
+  assert(baseRef != nullptr);
+  baseRef->set_allocated_leftplan(pn->ownPlanNodePoly().release());
+}
+
+void UnivPlanBuilderNode::setNodeRightPlan(
+    UnivPlanBuilderPlanNodePoly::uptr pn) {
+  assert(baseRef != nullptr);
+  baseRef->set_allocated_rightplan(pn->ownPlanNodePoly().release());
+}
+
+void UnivPlanBuilderNode::setNodePlanRows(double planRows) {
+  assert(baseRef != nullptr);
+  baseRef->set_planrows(planRows);
+}
+
+void UnivPlanBuilderNode::setNodePlanRowWidth(int32_t planRowWidth) {
+  assert(baseRef != nullptr);
+  baseRef->set_planrowwidth(planRowWidth);
+}
+
+void UnivPlanBuilderNode::setNodePlanOperatorMemKB(uint64_t operatorMemKB) {
+  baseRef->set_operatormemkb(operatorMemKB);
+}
+
+UnivPlanBuilderTargetEntry::uptr
+UnivPlanBuilderNode::addTargetEntryAndGetBuilder() {
+  assert(baseRef != nullptr);
+  UnivPlanExprPoly *exprTree = baseRef->add_targetlist();
+  exprTree->set_type(UNIVPLAN_EXPR_TARGETENTRY);
+  UnivPlanTargetEntry *targetEntry = exprTree->mutable_targetentry();
+  UnivPlanBuilderTargetEntry::uptr bld(
+      new UnivPlanBuilderTargetEntry(targetEntry));
+  return std::move(bld);
+}
+
+void UnivPlanBuilderNode::addQualList(UnivPlanBuilderExprTree::uptr exprTree) {
+  assert(baseRef != nullptr);
+  baseRef->mutable_quallist()->AddAllocated(exprTree->ownExprPoly().release());
+}
+
+void UnivPlanBuilderNode::addInitplan(UnivPlanBuilderExprTree::uptr exprTree) {
+  assert(baseRef != nullptr);
+  baseRef->mutable_initplan()->AddAllocated(exprTree->ownExprPoly().release());
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-node.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-node.h
new file mode 100644
index 0000000..437f7d0
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-node.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_NODE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_NODE_H_
+
+#include <memory>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-tree.h"
+#include "univplan/univplanbuilder/univplanbuilder-plan-node-poly.h"
+#include "univplan/univplanbuilder/univplanbuilder-target-entry.h"
+
+namespace univplan {
+
+class UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderNode() {
+    basePlanNode.reset(new UnivPlanPlanNode());
+    baseRef = basePlanNode.get();
+  }
+  virtual ~UnivPlanBuilderNode() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderNode> uptr;
+
+  void setNodeLeftPlan(UnivPlanBuilderPlanNodePoly::uptr pn);
+  void setNodeRightPlan(UnivPlanBuilderPlanNodePoly::uptr pn);
+
+  void setNodePlanRows(double planRows);
+
+  void setNodePlanRowWidth(int32_t planRowWidth);
+
+  void setNodePlanOperatorMemKB(uint64_t operatorMemKB);
+
+  virtual std::unique_ptr<UnivPlanBuilderPlanNodePoly> ownPlanNode() = 0;
+
+  // copy from the plannode content without left and right child
+  virtual void from(const UnivPlanPlanNodePoly &node) = 0;
+
+  UnivPlanBuilderTargetEntry::uptr addTargetEntryAndGetBuilder();
+  void addQualList(UnivPlanBuilderExprTree::uptr exprTree);
+  void addInitplan(UnivPlanBuilderExprTree::uptr exprTree);
+
+ public:
+  int32_t uid = -1;
+  int32_t pid = -1;
+
+ protected:
+  std::unique_ptr<UnivPlanPlanNode> basePlanNode;
+  UnivPlanPlanNode *baseRef = nullptr;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_NODE_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-paraminfo.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-paraminfo.h
new file mode 100644
index 0000000..99900c3
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-paraminfo.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PARAMINFO_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PARAMINFO_H_
+
+#include <string>
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class UnivPlanBuilderParamInfo {
+ public:
+  explicit UnivPlanBuilderParamInfo(UnivPlanParamInfo *paramInfo)
+      : ref_(paramInfo) {}
+
+  virtual ~UnivPlanBuilderParamInfo() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderParamInfo> uptr;
+
+  UnivPlanBuilderParamInfo &setType(int32_t type) {
+    ref_->set_type(type);
+    return *this;
+  }
+
+  UnivPlanBuilderParamInfo &setIsNull(bool isNull) {
+    ref_->set_isnull(isNull);
+    return *this;
+  }
+
+  UnivPlanBuilderParamInfo &setValue(const std::string &value) {
+    ref_->set_value(value);
+    return *this;
+  }
+
+ private:
+  UnivPlanParamInfo *ref_;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PARAMINFO_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan-node-poly.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan-node-poly.h
new file mode 100644
index 0000000..7d83518
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan-node-poly.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PLAN_NODE_POLY_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PLAN_NODE_POLY_H_
+
+#include <memory>
+#include <utility>
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class UnivPlanBuilderPlanNodePoly {
+ public:
+  UnivPlanBuilderPlanNodePoly(UNIVPLANNODETYPE type,
+                              const UnivPlanPlanNode &planNode)
+      : planNode(planNode) {
+    pn.reset(new UnivPlanPlanNodePoly());
+    ref = pn.get();
+    ref->set_type(type);
+  }
+
+  virtual ~UnivPlanBuilderPlanNodePoly() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderPlanNodePoly> uptr;
+
+  UnivPlanPlanNodePoly *getPlanNodePoly() { return ref; }
+
+  std::unique_ptr<UnivPlanPlanNodePoly> ownPlanNodePoly() {
+    return std::move(pn);
+  }
+
+  const UnivPlanPlanNode &getPlanNode() { return planNode; }
+
+ private:
+  UnivPlanPlanNodePoly *ref;
+  std::unique_ptr<UnivPlanPlanNodePoly> pn;
+  const UnivPlanPlanNode &planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PLAN_NODE_POLY_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan.cc
new file mode 100644
index 0000000..3d9e75e
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan.cc
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-plan.h"
+
+#include <utility>
+
+#include "dbcommon/log/logger.h"
+#include "univplan/univplanbuilder/univplanbuilder-plan-node-poly.h"
+
+namespace univplan {
+
+void UnivPlanBuilderPlan::setPlanNode(UnivPlanBuilderPlanNodePoly::uptr pn) {
+  ref->set_allocated_plan(pn->ownPlanNodePoly().release());
+}
+
+void UnivPlanBuilderPlan::setSubplanNode(UnivPlanBuilderPlanNodePoly::uptr pn) {
+  ref->mutable_subplans()->AddAllocated(pn->ownPlanNodePoly().release());
+}
+
+void UnivPlanBuilderPlan::setStageNo(int32_t stageNo) {
+  ref->set_stageno(stageNo);
+}
+
+void UnivPlanBuilderPlan::setTokenEntry(std::string protocol, std::string host,
+                                        int port, std::string token) {
+  UnivPlanTokenEntry *tokenEntry = ref->add_tokenmap();
+  tokenEntry->set_token(token);
+  auto key = tokenEntry->mutable_key();
+  key->set_protocol(protocol);
+  key->set_ip(host);
+  key->set_port(port);
+}
+
+void UnivPlanBuilderPlan::setSnapshot(const std::string &snapshot) {
+  ref->set_snapshot(snapshot);
+}
+
+void UnivPlanBuilderPlan::addGuc(const std::string &name,
+                                 const std::string &value) {
+  (*ref->mutable_guc())[name] = value;
+}
+
+void UnivPlanBuilderPlan::addCommonValue(const std::string &key,
+                                         const std::string &value,
+                                         std::string *newKey) {
+  // if key exists, add suffix to avoid overwriting exisiting values, newKey
+  // saves newly assigned key, if newKey is nullptr, original key is used
+  std::string tmpKey = key;
+  bool dup = false;
+  if (newKey != nullptr) {
+    uint64_t counter = 0;
+    while (true) {
+      *newKey = key + std::to_string(counter);
+      if (ref->commonvalue().find(*newKey) == ref->commonvalue().end()) {
+        tmpKey = *newKey;
+        break;  // found new key
+      } else if (ref->commonvalue().at(*newKey) == value) {
+        tmpKey = *newKey;
+        dup = true;
+        break;  // found duplicate value, no need to allocate new key
+      }
+      counter++;
+    }
+  }
+  if (!dup) {
+    (*ref->mutable_commonvalue())[tmpKey] = value;
+    LOG_DEBUG("set key %s into common values of plan size %lu", tmpKey.c_str(),
+              value.size());
+  } else {
+    LOG_DEBUG("found duplicate key %s", tmpKey.c_str());
+  }
+}
+
+void UnivPlanBuilderPlan::setDoInstrument(bool doInstrument) {
+  ref->set_doinstrument(doInstrument);
+}
+
+void UnivPlanBuilderPlan::setNCrossLevelParams(int32_t nCrossLevelParams) {
+  ref->set_ncrosslevelparams(nCrossLevelParams);
+}
+
+void UnivPlanBuilderPlan::setCmdType(UNIVPLANCMDTYPE cmdType) {
+  ref->set_cmdtype(cmdType);
+}
+
+// xxx only called from stagize, need sync with univplanplan's fields
+void UnivPlanBuilderPlan::from(const UnivPlanPlan &from) {
+  // plan not set
+  ref->set_stageno(from.stageno());
+  if (from.snapshot().empty() == false) ref->set_snapshot(from.snapshot());
+  ref->set_doinstrument(from.doinstrument());
+  ref->set_ncrosslevelparams(from.ncrosslevelparams());
+  ref->set_cmdtype(from.cmdtype());
+  for (int i = 0; i < from.rangetables_size(); i++)
+    ref->add_rangetables()->CopyFrom(from.rangetables(i));
+  for (int i = 0; i < from.tokenmap_size(); i++)
+    ref->add_tokenmap()->CopyFrom(from.tokenmap(i));
+  for (int i = 0; i < from.receivers_size(); i++)
+    ref->add_receivers()->CopyFrom(from.receivers(i));
+  for (int i = 0; i < from.paraminfos_size(); ++i)
+    ref->add_paraminfos()->CopyFrom(from.paraminfos(i));
+  for (auto ent : from.guc()) (*ref->mutable_guc())[ent.first] = ent.second;
+  for (auto ent : from.commonvalue())
+    (*ref->mutable_commonvalue())[ent.first] = ent.second;
+
+  // childStage not set
+}
+
+UnivPlanBuilderRangeTblEntry::uptr
+UnivPlanBuilderPlan::addRangeTblEntryAndGetBuilder() {
+  UnivPlanRangeTblEntry *entry = ref->add_rangetables();
+  UnivPlanBuilderRangeTblEntry::uptr bld(
+      new UnivPlanBuilderRangeTblEntry(entry));
+  return std::move(bld);
+}
+
+UnivPlanBuilderReceiver::uptr UnivPlanBuilderPlan::addReceiverAndGetBuilder() {
+  UnivPlanReceiver *receiver = ref->add_receivers();
+  UnivPlanBuilderReceiver::uptr bld(new UnivPlanBuilderReceiver(receiver));
+  return std::move(bld);
+}
+
+UnivPlanBuilderParamInfo::uptr
+UnivPlanBuilderPlan::addParamInfoAndGetBuilder() {
+  UnivPlanParamInfo *paramInfo = ref->add_paraminfos();
+  UnivPlanBuilderParamInfo::uptr bld(new UnivPlanBuilderParamInfo(paramInfo));
+  return std::move(bld);
+}
+
+UnivPlanBuilderPlan::uptr UnivPlanBuilderPlan::addChildStageAndGetBuilder() {
+  UnivPlanPlan *child = ref->add_childstages();
+  UnivPlanBuilderPlan::uptr bld(new UnivPlanBuilderPlan(child));
+  return std::move(bld);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan.h
new file mode 100644
index 0000000..a572f08
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-plan.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PLAN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PLAN_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+#include "univplan/univplanbuilder/univplanbuilder-paraminfo.h"
+#include "univplan/univplanbuilder/univplanbuilder-range-tbl-entry.h"
+#include "univplan/univplanbuilder/univplanbuilder-receiver.h"
+
+namespace univplan {
+
+class UnivPlanBuilderPlan {
+ public:
+  UnivPlanBuilderPlan() {
+    plan.reset(new UnivPlanPlan());
+    ref = plan.get();
+  }
+
+  explicit UnivPlanBuilderPlan(UnivPlanPlan *plan) : ref(plan) {}
+
+  virtual ~UnivPlanBuilderPlan() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderPlan> uptr;
+
+  UnivPlanPlan *getPlan() { return ref; }
+  UnivPlanPlan *ownPlan() { return plan.release(); }
+
+  // set root node of the plan
+  void setPlanNode(UnivPlanBuilderPlanNodePoly::uptr pn);
+  void setSubplanNode(UnivPlanBuilderPlanNodePoly::uptr pn);
+  bool isSettingSubplan() { return ref->has_plan(); }
+
+  void setStageNo(int32_t stageNo);
+  void setDoInstrument(bool doInstrument);
+  void setNCrossLevelParams(int32_t nCrossLevelParams);
+  void setCmdType(UNIVPLANCMDTYPE cmdType);
+
+  void from(const UnivPlanPlan &from);
+  void setTokenEntry(std::string protocol, std::string host, int port,
+                     std::string token);
+  void setSnapshot(const std::string &snapshot);
+
+  void addGuc(const std::string &name, const std::string &value);
+
+  void addCommonValue(const std::string &key, const std::string &value,
+                      std::string *newKey);
+
+  UnivPlanBuilderRangeTblEntry::uptr addRangeTblEntryAndGetBuilder();
+
+  UnivPlanBuilderReceiver::uptr addReceiverAndGetBuilder();
+
+  UnivPlanBuilderParamInfo::uptr addParamInfoAndGetBuilder();
+
+  UnivPlanBuilderPlan::uptr addChildStageAndGetBuilder();
+
+ private:
+  UnivPlanPlan *ref;
+  std::unique_ptr<UnivPlanPlan> plan;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_PLAN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-range-tbl-entry.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-range-tbl-entry.h
new file mode 100644
index 0000000..6d432ba
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-range-tbl-entry.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RANGE_TBL_ENTRY_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RANGE_TBL_ENTRY_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+class UnivPlanBuilderRangeTblEntry {
+ public:
+  UnivPlanBuilderRangeTblEntry() {
+    node.reset(new UnivPlanRangeTblEntry());
+    ref = node.get();
+  }
+
+  explicit UnivPlanBuilderRangeTblEntry(UnivPlanRangeTblEntry *rangeTblEntry)
+      : ref(rangeTblEntry) {}
+
+  virtual ~UnivPlanBuilderRangeTblEntry() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderRangeTblEntry> uptr;
+
+  void setTable(std::unique_ptr<UnivPlanTable> table) {
+    ref->set_allocated_table(table.release());
+  }
+
+ private:
+  UnivPlanRangeTblEntry *ref;
+  std::unique_ptr<UnivPlanRangeTblEntry> node;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RANGE_TBL_ENTRY_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-receiver.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-receiver.h
new file mode 100644
index 0000000..ebf26ef
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-receiver.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RECEIVER_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RECEIVER_H_
+
+#include <string>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-listener.h"
+
+namespace univplan {
+
+class UnivPlanBuilderReceiver {
+ public:
+  explicit UnivPlanBuilderReceiver(UnivPlanReceiver *receiver)
+      : ref(receiver) {}
+
+  virtual ~UnivPlanBuilderReceiver() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderReceiver> uptr;
+
+  UnivPlanBuilderListener::uptr addPlanListenerAndGetBuilder() {
+    UnivPlanListener *listener = ref->add_listener();
+    UnivPlanBuilderListener::uptr bld(new UnivPlanBuilderListener(listener));
+    return std::move(bld);
+  }
+
+ private:
+  UnivPlanReceiver *ref;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RECEIVER_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-result.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-result.h
new file mode 100644
index 0000000..adb05e6
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-result.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RESULT_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RESULT_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderResult : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderResult() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanResult());
+    ref = planNode.get();
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderResult() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderResult> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_RESULT, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_result(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_RESULT);
+    ref->CopyFrom(node.result());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  void addResConstantQual(UnivPlanBuilderExprTree::uptr exprTree) {
+    assert(ref != nullptr);
+    ref->mutable_resconstantqual()->AddAllocated(
+        exprTree->ownExprPoly().release());
+  }
+
+ private:
+  UnivPlanResult *ref;
+  std::unique_ptr<UnivPlanResult> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_RESULT_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-seq.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-seq.cc
new file mode 100644
index 0000000..4bb93c3
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-seq.cc
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-scan-seq.h"
+
+namespace univplan {
+
+UnivPlanBuilderScanSeq::UnivPlanBuilderScanSeq() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanScanSeq);
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderScanSeq::~UnivPlanBuilderScanSeq() {}
+
+std::unique_ptr<UnivPlanBuilderPlanNodePoly>
+UnivPlanBuilderScanSeq::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_SCAN_SEQ, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_scanseq(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderScanSeq::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_SCAN_SEQ);
+  ref->CopyFrom(node.scanseq());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderScanSeq::setScanRelId(uint32_t id) { ref->set_relid(id); }
+
+void UnivPlanBuilderScanSeq::setColumnsToRead(
+    const std::vector<int32_t> &nArray) {
+  for (int i = 0; i < nArray.size(); ++i) {
+    ref->add_columnstoread(nArray[i]);
+  }
+}
+
+void UnivPlanBuilderScanSeq::setReadStatsOnly(bool readStatsOnly) {
+  ref->set_readstatsonly(readStatsOnly);
+}
+
+UnivPlanBuilderScanTask::uptr
+UnivPlanBuilderScanSeq::addScanTaskAndGetBuilder() {
+  UnivPlanScanTask *task = ref->add_tasks();
+  std::unique_ptr<UnivPlanBuilderScanTask> res(
+      new UnivPlanBuilderScanTask(task));
+  return std::move(res);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-seq.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-seq.h
new file mode 100644
index 0000000..eee1cb7
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-seq.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SCAN_SEQ_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SCAN_SEQ_H_
+
+#include <string>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+#include "univplan/univplanbuilder/univplanbuilder-scan-task.h"
+
+namespace univplan {
+
+class UnivPlanBuilderScanSeq : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderScanSeq();
+  virtual ~UnivPlanBuilderScanSeq();
+
+  typedef std::unique_ptr<UnivPlanBuilderScanSeq> uptr;
+
+  std::unique_ptr<UnivPlanBuilderPlanNodePoly> ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setScanRelId(uint32_t id);
+  void setColumnsToRead(const std::vector<int32_t> &nArray);
+  void setReadStatsOnly(bool readStatsOnly);
+
+  UnivPlanBuilderScanTask::uptr addScanTaskAndGetBuilder();
+
+ private:
+  UnivPlanScanSeq *ref;
+  std::unique_ptr<UnivPlanScanSeq> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SCAN_SEQ_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-task.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-task.h
new file mode 100644
index 0000000..c4e4dab
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-scan-task.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SCAN_TASK_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SCAN_TASK_H_
+
+#include <string>
+
+#include "dbcommon/common/tuple-batch.h"
+
+#include "univplan/proto/universal-plan.pb.h"
+
+namespace univplan {
+
+#define TBSPLITS_COL_INDEX_FILENAME 0
+#define TBSPLITS_COL_INDEX_START 1
+#define TBSPLITS_COL_INDEX_LEN 2
+#define TBSPLITS_COL_INDEX_RANGEID 3
+#define TBSPLITS_COL_INDEX_RGID 4
+
+class UnivPlanBuilderScanTask {
+ public:
+  UnivPlanBuilderScanTask() { prepare(); }
+  explicit UnivPlanBuilderScanTask(UnivPlanScanTask *task) {
+    ref = task;
+    prepare();
+  }
+  virtual ~UnivPlanBuilderScanTask() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderScanTask> uptr;
+
+  void prepare(void) {
+    if (tbSplitsDesc_.getNumOfColumns() == 0) {
+      // initialize tuple desc only once, col names are not meaningful
+      tbSplitsDesc_.add("f", dbcommon::TypeKind::STRINGID);
+      tbSplitsDesc_.add("s", dbcommon::TypeKind::BIGINTID);
+      tbSplitsDesc_.add("l", dbcommon::TypeKind::BIGINTID);
+      tbSplitsDesc_.add("rid", dbcommon::TypeKind::BIGINTID);
+      tbSplitsDesc_.add("rgid", dbcommon::TypeKind::INTID);
+    }
+    tbSplits_.reset(new dbcommon::TupleBatch(tbSplitsDesc_, true));
+  }
+
+  void addScanFileSplit(const char *filename, int64_t start, int64_t len,
+                        int64_t rangeid, int32_t rgid) {
+    dbcommon::TupleBatchWriter &writers = tbSplits_->getTupleBatchWriter();
+    writers[TBSPLITS_COL_INDEX_FILENAME]->append(filename, strlen(filename),
+                                                 false);
+    writers[TBSPLITS_COL_INDEX_START]->append(reinterpret_cast<char *>(&start),
+                                              sizeof(int64_t), false);
+    writers[TBSPLITS_COL_INDEX_LEN]->append(reinterpret_cast<char *>(&len),
+                                            sizeof(int64_t), false);
+    writers[TBSPLITS_COL_INDEX_RANGEID]->append(
+        reinterpret_cast<char *>(&rangeid), sizeof(int64_t), false);
+    writers[TBSPLITS_COL_INDEX_RGID]->append(reinterpret_cast<char *>(&rgid),
+                                             sizeof(int32_t), false);
+    tbSplits_->incNumOfRows(1);
+  }
+
+  void generate(void) {
+    std::unique_ptr<std::string> serialized(new std::string());
+    if (tbSplits_->getNumOfRows() > 0) {
+      tbSplits_->serialize(serialized.get(), 0);
+      ref->set_allocated_serializedsplits(serialized.release());
+    }
+  }
+
+  dbcommon::TupleBatch::uptr releaseSplitsTb() { return std::move(tbSplits_); }
+
+ private:
+  UnivPlanScanTask *ref = nullptr;
+  dbcommon::TupleDesc tbSplitsDesc_;
+  dbcommon::TupleBatch::uptr tbSplits_;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SCAN_TASK_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-shareinput-scan.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-shareinput-scan.h
new file mode 100644
index 0000000..4c521ad
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-shareinput-scan.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SHAREINPUT_SCAN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SHAREINPUT_SCAN_H_
+
+#include <memory>
+#include <utility>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderShareInputScan : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderShareInputScan() : UnivPlanBuilderNode() {
+    planNode_.reset(new UnivPlanShareInputScan());
+    ref_ = planNode_.get();
+    ref_->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderShareInputScan() {}
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(new UnivPlanBuilderPlanNodePoly(
+        UNIVPLAN_SHAREINPUTSCAN, planNode_->super()));
+    pn->getPlanNodePoly()->set_allocated_shareinputscan(planNode_.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_SHAREINPUTSCAN);
+    ref_->CopyFrom(node.shareinputscan());
+    baseRef = ref_->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+  void setShareType(UNIVPLANSHARETYPE type) { ref_->set_sharetype(type); }
+
+  void setShareId(int32_t shareId) { ref_->set_sharedid(shareId); }
+
+  void setDriverSlice(int32_t driverSlice) {
+    ref_->set_driverslice(driverSlice);
+  }
+
+ private:
+  UnivPlanShareInputScan *ref_;
+  std::unique_ptr<UnivPlanShareInputScan> planNode_;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SHAREINPUT_SCAN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sink.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sink.h
new file mode 100644
index 0000000..6787621
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sink.h
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SINK_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SINK_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+// this class is only used in stagize
+class UnivPlanBuilderSinkInput : public UnivPlanBuilderNode {
+ public:
+  typedef std::unique_ptr<UnivPlanBuilderSinkInput> uptr;
+  UnivPlanBuilderSinkInput() : UnivPlanBuilderNode() {}
+  virtual ~UnivPlanBuilderSinkInput() {}
+  virtual void setTargetStageNo(int32_t targetStageNo) = 0;
+  virtual int32_t getTargetStageNo() = 0;
+  virtual void setCurrentStageNo(int32_t targetStageNo) = 0;
+  virtual int32_t getCurrentStageNo() = 0;
+  virtual void from(const UnivPlanConnector &from) {
+    baseRef->CopyFrom(from.super());
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+  // virtual UNIVPLANNODETYPE getType() = 0;
+};
+
+class UnivPlanBuilderSink : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderSink() : UnivPlanBuilderNode() {
+    planNode.reset(new UnivPlanSink());
+    ref = planNode.get();
+
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderSink() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderSink> uptr;
+
+  // only used in stagize
+  void from(const UnivPlanConnector &from) {
+    baseRef->CopyFrom(from.super());
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+    ref->set_connectortype(from.type());
+    for (int i = 0; i < from.colidx_size(); ++i)
+      ref->add_colidx(from.colidx(i));
+    for (int i = 0; i < from.sortfuncid_size(); ++i)
+      ref->add_sortfuncid(from.sortfuncid(i));
+  }
+
+  void setSourceStageNo(int32_t sourceStageNo) {
+    ref->set_sourcestageno(sourceStageNo);
+  }
+
+  void setCurrentStageNo(int32_t currentStageNo) {
+    ref->set_currentstageno(currentStageNo);
+  }
+
+  void setConnectorType(UNIVPLANCONNECTORTYPE connectorType) {
+    ref->set_connectortype(connectorType);
+  }
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_SINK, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_sink(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_SINK);
+    ref->CopyFrom(node.sink());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+ private:
+  UnivPlanSink *ref;
+  std::unique_ptr<UnivPlanSink> planNode;
+};
+
+class UnivPlanBuilderConverge : public UnivPlanBuilderSinkInput {
+ public:
+  UnivPlanBuilderConverge() : UnivPlanBuilderSinkInput() {
+    planNode.reset(new UnivPlanConverge());
+    ref = planNode.get();
+
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderConverge() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderConverge> uptr;
+
+  void from(const UnivPlanConnector &from) override {
+    UnivPlanBuilderSinkInput::from(from);
+  }
+
+  void setTargetStageNo(int32_t targetStageNo) override {
+    ref->set_targetstageno(targetStageNo);
+  }
+
+  int32_t getTargetStageNo() override { return ref->targetstageno(); }
+
+  void setCurrentStageNo(int32_t currentStageNo) override {
+    ref->set_currentstageno(currentStageNo);
+  }
+
+  int32_t getCurrentStageNo() override { return ref->currentstageno(); }
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_CONVERGE, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_converge(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_CONVERGE);
+    ref->CopyFrom(node.converge());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+ private:
+  UnivPlanConverge *ref;
+  std::unique_ptr<UnivPlanConverge> planNode;
+};
+
+class UnivPlanBuilderShuffle : public UnivPlanBuilderSinkInput {
+ public:
+  UnivPlanBuilderShuffle() : UnivPlanBuilderSinkInput() {
+    planNode.reset(new UnivPlanShuffle());
+    ref = planNode.get();
+
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderShuffle() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderShuffle> uptr;
+
+  void from(const UnivPlanConnector &from) override {
+    UnivPlanBuilderSinkInput::from(from);
+    ref->set_magmatable(from.magmatable());
+    ref->mutable_hashexpr()->CopyFrom(from.hashexpr());
+    ref->mutable_magmamap()->CopyFrom(from.magmamap());
+    ref->set_rangenum(from.rangenum());
+  }
+
+  void setTargetStageNo(int32_t targetStageNo) override {
+    ref->set_targetstageno(targetStageNo);
+  }
+
+  int32_t getTargetStageNo() override { return ref->targetstageno(); }
+
+  void setCurrentStageNo(int32_t currentStageNo) override {
+    ref->set_currentstageno(currentStageNo);
+  }
+
+  int32_t getCurrentStageNo() override { return ref->currentstageno(); }
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_SHUFFLE, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_shuffle(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_SHUFFLE);
+    ref->CopyFrom(node.shuffle());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+ private:
+  UnivPlanShuffle *ref;
+  std::unique_ptr<UnivPlanShuffle> planNode;
+};
+
+class UnivPlanBuilderBroadcast : public UnivPlanBuilderSinkInput {
+ public:
+  UnivPlanBuilderBroadcast() : UnivPlanBuilderSinkInput() {
+    planNode.reset(new UnivPlanBroadcast());
+    ref = planNode.get();
+
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderBroadcast() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderBroadcast> uptr;
+
+  void from(const UnivPlanConnector &from) override {
+    UnivPlanBuilderSinkInput::from(from);
+  }
+
+  void setTargetStageNo(int32_t targetStageNo) override {
+    ref->set_targetstageno(targetStageNo);
+  }
+
+  int32_t getTargetStageNo() override { return ref->targetstageno(); }
+
+  void setCurrentStageNo(int32_t currentStageNo) override {
+    ref->set_currentstageno(currentStageNo);
+  }
+
+  int32_t getCurrentStageNo() override { return ref->currentstageno(); }
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(
+        new UnivPlanBuilderPlanNodePoly(UNIVPLAN_BROADCAST, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_broadcast(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_BROADCAST);
+    ref->CopyFrom(node.broadcast());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+  }
+
+ private:
+  UnivPlanBroadcast *ref;
+  std::unique_ptr<UnivPlanBroadcast> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SINK_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sort.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sort.cc
new file mode 100644
index 0000000..be1d9eb
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sort.cc
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-sort.h"
+
+namespace univplan {
+
+UnivPlanBuilderSort::UnivPlanBuilderSort() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanSort());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderPlanNodePoly::uptr UnivPlanBuilderSort::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_SORT, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_sort(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderSort::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_SORT);
+  ref->CopyFrom(node.sort());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderSort::setColIdx(const std::vector<int32_t> &nArray) {
+  for (int i = 0; i < nArray.size(); ++i) {
+    ref->add_colidx(nArray[i]);
+  }
+}
+void UnivPlanBuilderSort::setSortFuncId(const std::vector<int32_t> &nArray) {
+  for (int i = 0; i < nArray.size(); ++i) {
+    ref->add_sortfuncid(nArray[i]);
+  }
+}
+
+void UnivPlanBuilderSort::setLimitOffset(UnivPlanBuilderExprTree::uptr offset) {
+  ref->set_allocated_limitoffset(offset->ownExprPoly().release());
+}
+
+void UnivPlanBuilderSort::setLimitCount(UnivPlanBuilderExprTree::uptr count) {
+  ref->set_allocated_limitcount(count->ownExprPoly().release());
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sort.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sort.h
new file mode 100644
index 0000000..7c3ee35
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-sort.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SORT_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SORT_H_
+
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderSort : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderSort();
+  virtual ~UnivPlanBuilderSort() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderSort> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setColIdx(const std::vector<int32_t> &nArray);
+  void setSortFuncId(const std::vector<int32_t> &nArray);
+
+  void setLimitOffset(UnivPlanBuilderExprTree::uptr limitOffset);
+  void setLimitCount(UnivPlanBuilderExprTree::uptr limitCount);
+
+ private:
+  UnivPlanSort *ref;
+  std::unique_ptr<UnivPlanSort> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SORT_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-subquery-scan.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-subquery-scan.h
new file mode 100644
index 0000000..ddb4767
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-subquery-scan.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SUBQUERY_SCAN_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SUBQUERY_SCAN_H_
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderSubqueryScan : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderSubqueryScan() {
+    planNode.reset(new UnivPlanSubqueryScan());
+    ref = planNode.get();
+
+    ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+  }
+  virtual ~UnivPlanBuilderSubqueryScan() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderSubqueryScan> uptr;
+
+  std::unique_ptr<UnivPlanBuilderPlanNodePoly> ownPlanNode() override {
+    UnivPlanBuilderPlanNodePoly::uptr pn(new UnivPlanBuilderPlanNodePoly(
+        UNIVPLAN_SUBQUERYSCAN, planNode->super()));
+    pn->getPlanNodePoly()->set_allocated_subqueryscan(planNode.release());
+    return std::move(pn);
+  }
+
+  void from(const UnivPlanPlanNodePoly &node) override {
+    assert(node.type() == UNIVPLAN_SUBQUERYSCAN);
+    ref->CopyFrom(node.subqueryscan());
+    baseRef = ref->mutable_super();
+    baseRef->clear_leftplan();
+    baseRef->clear_rightplan();
+    ref->clear_subplan();
+  }
+
+  void setSubPlan(const UnivPlanPlanNodePoly &node) {
+    ref->mutable_subplan()->CopyFrom(node);
+  }
+
+ private:
+  UnivPlanSubqueryScan *ref;
+  std::unique_ptr<UnivPlanSubqueryScan> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_SUBQUERY_SCAN_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-table.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-table.h
new file mode 100644
index 0000000..e5e0b6b
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-table.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_TABLE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_TABLE_H_
+
+#include <string>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-column.h"
+
+namespace univplan {
+
+class UnivPlanBuilderTable {
+ public:
+  UnivPlanBuilderTable() {
+    node.reset(new UnivPlanTable());
+    ref = node.get();
+  }
+  virtual ~UnivPlanBuilderTable() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderTable> uptr;
+
+  std::unique_ptr<UnivPlanTable> ownTable() { return std::move(node); }
+
+  void setTableId(int64_t id) { ref->set_tableid(id); }
+
+  void setTableFormat(UNIVPLANFORMATTYPE type) { ref->set_format(type); }
+
+  void setTableLocation(const std::string &location) {
+    ref->set_location(location);
+  }
+
+  void setTableOptionsInJson(const std::string &optStr) {
+    ref->set_tableoptionsinjson(optStr);
+  }
+
+  UnivPlanBuilderColumn::uptr addPlanColumnAndGetBuilder() {
+    UnivPlanColumn *column = ref->add_columns();
+    UnivPlanBuilderColumn::uptr bld(new UnivPlanBuilderColumn(column));
+    return std::move(bld);
+  }
+
+ private:
+  UnivPlanTable *ref;
+  std::unique_ptr<UnivPlanTable> node;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_TABLE_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-target-entry.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-target-entry.h
new file mode 100644
index 0000000..c3fbdeb
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-target-entry.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_TARGET_ENTRY_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_TARGET_ENTRY_H_
+
+#include "univplan/univplanbuilder/univplanbuilder-expr-node.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-tree.h"
+
+namespace univplan {
+
+class UnivPlanBuilderTargetEntry : public UnivPlanBuilderExprNode {
+ public:
+  UnivPlanBuilderTargetEntry() {
+    node.reset(new UnivPlanTargetEntry());
+    ref = node.get();
+  }
+
+  explicit UnivPlanBuilderTargetEntry(UnivPlanTargetEntry *targetEntry)
+      : ref(targetEntry) {}
+
+  virtual ~UnivPlanBuilderTargetEntry() {}
+
+  typedef std::unique_ptr<UnivPlanBuilderTargetEntry> uptr;
+
+  UnivPlanBuilderExprPoly::uptr getExprPolyBuilder() {
+    assert(node != nullptr);
+    UnivPlanBuilderExprPoly::uptr exprPoly(
+        new UnivPlanBuilderExprPoly(UNIVPLAN_EXPR_TARGETENTRY));
+    exprPoly->getExprPoly()->set_allocated_targetentry(node.release());
+    return std::move(exprPoly);
+  }
+
+  void setResJunk(bool resJunk) { ref->set_resjunk(resJunk); }
+
+  void setExpr(UnivPlanBuilderExprPoly::uptr exprPoly) {
+    ref->set_allocated_expression(exprPoly->ownExprPoly().release());
+  }
+
+  void setExpr(UnivPlanBuilderExprTree::uptr expr) {
+    ref->set_allocated_expression(expr->ownExprPoly().release());
+  }
+
+ private:
+  UnivPlanTargetEntry *ref = nullptr;
+  std::unique_ptr<UnivPlanTargetEntry> node;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_TARGET_ENTRY_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-unique.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-unique.cc
new file mode 100644
index 0000000..fd03ca5
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-unique.cc
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder-unique.h"
+
+#include <utility>
+
+namespace univplan {
+
+UnivPlanBuilderUnique::UnivPlanBuilderUnique() : UnivPlanBuilderNode() {
+  planNode.reset(new UnivPlanUnique());
+  ref = planNode.get();
+
+  ref->set_allocated_super(UnivPlanBuilderNode::basePlanNode.release());
+}
+
+UnivPlanBuilderUnique::~UnivPlanBuilderUnique() {}
+
+UnivPlanBuilderPlanNodePoly::uptr UnivPlanBuilderUnique::ownPlanNode() {
+  UnivPlanBuilderPlanNodePoly::uptr pn(
+      new UnivPlanBuilderPlanNodePoly(UNIVPLAN_UNIQUE, planNode->super()));
+  pn->getPlanNodePoly()->set_allocated_unique(planNode.release());
+  return std::move(pn);
+}
+
+void UnivPlanBuilderUnique::from(const UnivPlanPlanNodePoly &node) {
+  assert(node.type() == UNIVPLAN_UNIQUE);
+  ref->CopyFrom(node.unique());
+  baseRef = ref->mutable_super();
+  baseRef->clear_leftplan();
+  baseRef->clear_rightplan();
+}
+
+void UnivPlanBuilderUnique::setUniqColIdxs(size_t numCols,
+                                           const int32_t *uniqColIdxs) {
+  for (int i = 0; i < numCols; ++i) {
+    ref->add_uniqcolidxs(uniqColIdxs[i]);
+  }
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-unique.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-unique.h
new file mode 100644
index 0000000..e89747e
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder-unique.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_UNIQUE_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_UNIQUE_H_
+
+#include <memory>
+#include <vector>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+
+namespace univplan {
+
+class UnivPlanBuilderUnique : public UnivPlanBuilderNode {
+ public:
+  UnivPlanBuilderUnique();
+  virtual ~UnivPlanBuilderUnique();
+
+  typedef std::unique_ptr<UnivPlanBuilderUnique> uptr;
+
+  UnivPlanBuilderPlanNodePoly::uptr ownPlanNode() override;
+
+  void from(const UnivPlanPlanNodePoly &node) override;
+
+  void setUniqColIdxs(size_t numCols, const int32_t *uniqColIdxs);
+
+ private:
+  UnivPlanUnique *ref;
+  std::unique_ptr<UnivPlanUnique> planNode;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_UNIQUE_H_
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder.cc b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder.cc
new file mode 100644
index 0000000..44032c4
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder.cc
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include "univplan/univplanbuilder/univplanbuilder.h"
+
+#include <utility>
+
+#include "dbcommon/log/logger.h"
+#include "dbcommon/type/type-kind.h"
+
+namespace univplan {
+
+typedef dbcommon::TypeKind TypeKind;
+
+UnivPlanBuilderPlan::uptr UnivPlanBuilder::swapBuilder(
+    UnivPlanBuilderPlan::uptr newBuilder) {
+  UnivPlanBuilderPlan::uptr ret = std::move(builder);
+  builder = std::move(newBuilder);
+  return std::move(ret);
+}
+
+void UnivPlanBuilder::addPlanNode(bool isleft, UnivPlanBuilderNode::uptr bld) {
+  UnivPlanBuilderPlanNodePoly::uptr pn = bld->ownPlanNode();
+  int32_t uid = bld->uid;  // required field
+  int32_t pid = bld->pid;  // required field
+
+  // Check if this node has node id set already
+  if (uid < 0)
+    LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+              "UnivPlanBuilder::addPlanNode uid is negative");
+
+  // Check if the id is unique
+  if (idToPlanNodes.find(uid) != idToPlanNodes.end())
+    LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+              "UnivPlanBuilder::addPlanNode uid is duplicate");
+
+  // Use hash table to track this node quickly
+  idToPlanNodes[uid] = std::move(bld);
+
+  UnivPlanPlan *plan = builder->getPlan();
+
+  if (pid < 0) {
+    // If a node has no parent id, it is a root node of the plan or subplan.
+    if (builder->isSettingSubplan())
+      builder->setSubplanNode(std::move(pn));
+    else
+      builder->setPlanNode(std::move(pn));
+
+  } else {
+    // If this node has a parent id, that means this node should be connected
+    // to its expected parent node.
+
+    // Find its parent node if its parent id is specified
+    if (idToPlanNodes.find(pid) == idToPlanNodes.end())
+      LOG_ERROR(ERRCODE_INTERNAL_ERROR,
+                "UnivPlanBuilder::addPlanNode unexpected parent id %d found",
+                pid);
+
+    // add to parent node
+    if (isleft) {
+      idToPlanNodes[pid]->setNodeLeftPlan(std::move(pn));
+    } else {
+      idToPlanNodes[pid]->setNodeRightPlan(std::move(pn));
+    }
+  }
+}
+
+std::string UnivPlanBuilder::getJsonFormatedPlan() {
+  return builder->getPlan()->DebugString();
+}
+
+std::string UnivPlanBuilder::serialize() {
+  return builder->getPlan()->SerializeAsString();
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/src/univplan/univplanbuilder/univplanbuilder.h b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder.h
new file mode 100644
index 0000000..f8e2f0b
--- /dev/null
+++ b/depends/univplan/src/univplan/univplanbuilder/univplanbuilder.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_H_
+#define UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_H_
+
+#include <map>
+#include <string>
+
+#include "univplan/proto/universal-plan.pb.h"
+#include "univplan/univplanbuilder/univplanbuilder-node.h"
+#include "univplan/univplanbuilder/univplanbuilder-plan.h"
+
+namespace univplan {
+
+class UnivPlanBuilder {
+ public:
+  UnivPlanBuilder() {
+    builder.reset(new UnivPlanBuilderPlan());
+    idToPlanNodes.clear();
+  }
+  virtual ~UnivPlanBuilder() {}
+
+  typedef std::unique_ptr<UnivPlanBuilder> uptr;
+
+  int32_t getUid() { return nodeCounter++; }
+  UnivPlanBuilderPlan *getPlanBuilderPlan() { return builder.get(); }
+  UnivPlanBuilderPlan::uptr swapBuilder(UnivPlanBuilderPlan::uptr newBuilder);
+
+  //----------------------------
+  // Add plan node to this plan
+  //----------------------------
+  void addPlanNode(bool isleft,  // as its parent's left child
+                   UnivPlanBuilderNode::uptr bld);
+
+  //-----------------------------
+  // Utility
+  //-----------------------------
+  std::string getJsonFormatedPlan();
+
+  std::string serialize();
+
+ private:
+  UnivPlanBuilderPlan::uptr builder;
+
+  int32_t nodeCounter = 0;
+  std::map<int32_t, UnivPlanBuilderNode::uptr> idToPlanNodes;
+};
+
+}  // namespace univplan
+
+#endif  // UNIVPLAN_SRC_UNIVPLAN_UNIVPLANBUILDER_UNIVPLANBUILDER_H_
diff --git a/depends/univplan/test/CMakeLists.txt b/depends/univplan/test/CMakeLists.txt
new file mode 100644
index 0000000..088ab7f
--- /dev/null
+++ b/depends/univplan/test/CMakeLists.txt
@@ -0,0 +1,33 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+SET(CMAKE_BUILD_TYPE "Debug")
+SET(TEST_WORKING_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data/)
+ADD_DEFINITIONS(-DDATA_DIR="${TEST_WORKING_DIR}/")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-access-control")
+
+INCLUDE_DIRECTORIES(${univplan_ROOT_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/src)
+INCLUDE_DIRECTORIES(${DEPENDENCY_INSTALL_PREFIX}/package/include)
+
+LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/src)
+LINK_DIRECTORIES(${DEPENDENCY_INSTALL_PREFIX}/package/lib)
+
+ADD_SUBDIRECTORY(unit)
+
+IF(TEST_RUNNER)
+    SEPARATE_ARGUMENTS(TEST_RUNNER_LIST UNIX_COMMAND ${TEST_RUNNER})
+ENDIF(TEST_RUNNER)
+
+ADD_CUSTOM_TARGET(unittest
+    COMMAND ${TEST_RUNNER_LIST} ${CMAKE_CURRENT_BINARY_DIR}/unit/unit
+    DEPENDS unit
+    WORKING_DIRECTORY ${TEST_WORKING_DIR}
+    COMMENT "Run Unit Test..."
+)
+
+ADD_CUSTOM_TARGET(punittest
+    COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/parallel/parallel-launcher.py ${TEST_RUNNER_LIST} ${CMAKE_CURRENT_BINARY_DIR}/unit/unit
+    DEPENDS unit
+    WORKING_DIRECTORY ${TEST_WORKING_DIR}
+    COMMENT "Run Unit Test in parallel..."
+)
diff --git a/depends/univplan/test/data/TestAgg b/depends/univplan/test/data/TestAgg
new file mode 100644
index 0000000..c20fd96
--- /dev/null
+++ b/depends/univplan/test/data/TestAgg
@@ -0,0 +1,260 @@
+plan {
+  type: UNIVPLAN_CONNECTOR
+  connector {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_AGG
+        agg {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_AGGREF
+                  aggref {
+                    transFuncId: 494
+                    retType: 103
+                    transInitVal: true
+                    finalFuncId: 38324
+                    args {
+                      type: UNIVPLAN_EXPR_VAR
+                      var {
+                        varNo: 65001
+                        varAttNo: 1
+                        typeId: 103
+                        typeMod: -1
+                      }
+                    }
+                    funcId: 492
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 2
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            leftPlan {
+              type: UNIVPLAN_CONNECTOR
+              connector {
+                super {
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 1
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 3
+                          typeId: 250
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  leftPlan {
+                    type: UNIVPLAN_AGG
+                    agg {
+                      super {
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_AGGREF
+                              aggref {
+                                transFuncId: 493
+                                retType: 103
+                                transInitVal: true
+                                args {
+                                  type: UNIVPLAN_EXPR_VAR
+                                  var {
+                                    varNo: 65001
+                                    varAttNo: 1
+                                    typeId: 103
+                                    typeMod: -1
+                                  }
+                                }
+                                funcId: 492
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 2
+                                typeId: 102
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 3
+                                typeId: 250
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        leftPlan {
+                          type: UNIVPLAN_SCAN_SEQ
+                          scanSeq {
+                            super {
+                              targetList {
+                                type: UNIVPLAN_EXPR_TARGETENTRY
+                                targetentry {
+                                  expression {
+                                    type: UNIVPLAN_EXPR_VAR
+                                    var {
+                                      varNo: 1
+                                      varAttNo: 1
+                                      typeId: 102
+                                      typeMod: -1
+                                    }
+                                  }
+                                  resJunk: false
+                                }
+                              }
+                              targetList {
+                                type: UNIVPLAN_EXPR_TARGETENTRY
+                                targetentry {
+                                  expression {
+                                    type: UNIVPLAN_EXPR_VAR
+                                    var {
+                                      varNo: 1
+                                      varAttNo: 3
+                                      typeId: 250
+                                      typeMod: -1
+                                    }
+                                  }
+                                  resJunk: false
+                                }
+                              }
+                            }
+                            relId: 1
+                            tasks {
+                              serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                            }
+                            tasks {
+                              serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                            }
+                            columnsToRead: 0
+                            columnsToRead: 1
+                          }
+                        }
+                      }
+                      numGroups: 1000
+                      groupColIndexes: 1
+                      groupColIndexes: 3
+                    }
+                  }
+                }
+                type: CONNECTORTYPE_SHUFFLE
+                hashExpr {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 0
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+              }
+            }
+          }
+          numGroups: 1000
+          groupColIndexes: 1
+          groupColIndexes: 3
+        }
+      }
+    }
+    type: CONNECTORTYPE_CONVERGE
+  }
+}
diff --git a/depends/univplan/test/data/TestCompletedPlanAfter b/depends/univplan/test/data/TestCompletedPlanAfter
new file mode 100644
index 0000000..c40ea64
--- /dev/null
+++ b/depends/univplan/test/data/TestCompletedPlanAfter
@@ -0,0 +1,849 @@
+plan {
+  type: UNIVPLAN_LIMIT
+  limit {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 2
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_SINK
+        sink {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 2
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+          }
+          connectorType: CONNECTORTYPE_CONVERGE
+          sourceStageNo: 2
+          currentStageNo: 0
+        }
+      }
+      planRows: 110
+      planRowWidth: 0
+      operatorMemKB: 0
+    }
+    limitOffset {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "100"
+        typeMod: -1
+      }
+    }
+    limitCount {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "10"
+        typeMod: -1
+      }
+    }
+  }
+}
+childStages {
+  plan {
+    type: UNIVPLAN_CONVERGE
+    converge {
+      super {
+        targetList {
+          type: UNIVPLAN_EXPR_TARGETENTRY
+          targetentry {
+            expression {
+              type: UNIVPLAN_EXPR_VAR
+              var {
+                varNo: 1
+                varAttNo: 1
+                typeId: 102
+                typeMod: -1
+              }
+            }
+            resJunk: false
+          }
+        }
+        targetList {
+          type: UNIVPLAN_EXPR_TARGETENTRY
+          targetentry {
+            expression {
+              type: UNIVPLAN_EXPR_VAR
+              var {
+                varNo: 1
+                varAttNo: 2
+                typeId: 102
+                typeMod: -1
+              }
+            }
+            resJunk: false
+          }
+        }
+        targetList {
+          type: UNIVPLAN_EXPR_TARGETENTRY
+          targetentry {
+            expression {
+              type: UNIVPLAN_EXPR_VAR
+              var {
+                varNo: 1
+                varAttNo: 3
+                typeId: 250
+                typeMod: -1
+              }
+            }
+            resJunk: false
+          }
+        }
+        leftPlan {
+          type: UNIVPLAN_LIMIT
+          limit {
+            super {
+              targetList {
+                type: UNIVPLAN_EXPR_TARGETENTRY
+                targetentry {
+                  expression {
+                    type: UNIVPLAN_EXPR_VAR
+                    var {
+                      varNo: 1
+                      varAttNo: 1
+                      typeId: 102
+                      typeMod: -1
+                    }
+                  }
+                  resJunk: false
+                }
+              }
+              targetList {
+                type: UNIVPLAN_EXPR_TARGETENTRY
+                targetentry {
+                  expression {
+                    type: UNIVPLAN_EXPR_VAR
+                    var {
+                      varNo: 1
+                      varAttNo: 2
+                      typeId: 102
+                      typeMod: -1
+                    }
+                  }
+                  resJunk: false
+                }
+              }
+              targetList {
+                type: UNIVPLAN_EXPR_TARGETENTRY
+                targetentry {
+                  expression {
+                    type: UNIVPLAN_EXPR_VAR
+                    var {
+                      varNo: 1
+                      varAttNo: 3
+                      typeId: 250
+                      typeMod: -1
+                    }
+                  }
+                  resJunk: false
+                }
+              }
+              leftPlan {
+                type: UNIVPLAN_AGG
+                agg {
+                  super {
+                    targetList {
+                      type: UNIVPLAN_EXPR_TARGETENTRY
+                      targetentry {
+                        expression {
+                          type: UNIVPLAN_EXPR_AGGREF
+                          aggref {
+                            transFuncId: 494
+                            retType: 103
+                            transInitVal: true
+                            finalFuncId: 38324
+                            args {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 65001
+                                varAttNo: 1
+                                typeId: 103
+                                typeMod: -1
+                              }
+                            }
+                            funcId: 492
+                          }
+                        }
+                        resJunk: false
+                      }
+                    }
+                    targetList {
+                      type: UNIVPLAN_EXPR_TARGETENTRY
+                      targetentry {
+                        expression {
+                          type: UNIVPLAN_EXPR_VAR
+                          var {
+                            varNo: 1
+                            varAttNo: 2
+                            typeId: 102
+                            typeMod: -1
+                          }
+                        }
+                        resJunk: false
+                      }
+                    }
+                    targetList {
+                      type: UNIVPLAN_EXPR_TARGETENTRY
+                      targetentry {
+                        expression {
+                          type: UNIVPLAN_EXPR_VAR
+                          var {
+                            varNo: 1
+                            varAttNo: 3
+                            typeId: 250
+                            typeMod: -1
+                          }
+                        }
+                        resJunk: false
+                      }
+                    }
+                    leftPlan {
+                      type: UNIVPLAN_SINK
+                      sink {
+                        super {
+                          targetList {
+                            type: UNIVPLAN_EXPR_TARGETENTRY
+                            targetentry {
+                              expression {
+                                type: UNIVPLAN_EXPR_VAR
+                                var {
+                                  varNo: 1
+                                  varAttNo: 1
+                                  typeId: 102
+                                  typeMod: -1
+                                }
+                              }
+                              resJunk: false
+                            }
+                          }
+                          targetList {
+                            type: UNIVPLAN_EXPR_TARGETENTRY
+                            targetentry {
+                              expression {
+                                type: UNIVPLAN_EXPR_VAR
+                                var {
+                                  varNo: 1
+                                  varAttNo: 2
+                                  typeId: 102
+                                  typeMod: -1
+                                }
+                              }
+                              resJunk: false
+                            }
+                          }
+                          targetList {
+                            type: UNIVPLAN_EXPR_TARGETENTRY
+                            targetentry {
+                              expression {
+                                type: UNIVPLAN_EXPR_VAR
+                                var {
+                                  varNo: 1
+                                  varAttNo: 3
+                                  typeId: 250
+                                  typeMod: -1
+                                }
+                              }
+                              resJunk: false
+                            }
+                          }
+                        }
+                        connectorType: CONNECTORTYPE_SHUFFLE
+                        sourceStageNo: 1
+                        currentStageNo: 2
+                      }
+                    }
+                  }
+                  numGroups: 1000
+                  groupColIndexes: 1
+                  groupColIndexes: 3
+                }
+              }
+              planRows: 110
+              planRowWidth: 0
+              operatorMemKB: 0
+            }
+            limitOffset {
+              type: UNIVPLAN_EXPR_CONST
+              val {
+                type: 103
+                isNull: false
+                value: "0"
+                typeMod: -1
+              }
+            }
+            limitCount {
+              type: UNIVPLAN_EXPR_OPEXPR
+              opexpr {
+                funcId: 339
+                retType: 103
+                args {
+                  type: UNIVPLAN_EXPR_CONST
+                  val {
+                    type: 103
+                    isNull: false
+                    value: "10"
+                    typeMod: -1
+                  }
+                }
+                args {
+                  type: UNIVPLAN_EXPR_CONST
+                  val {
+                    type: 103
+                    isNull: false
+                    value: "100"
+                    typeMod: -1
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      targetStageNo: 0
+      currentStageNo: 2
+    }
+  }
+  childStages {
+    plan {
+      type: UNIVPLAN_SHUFFLE
+      shuffle {
+        super {
+          targetList {
+            type: UNIVPLAN_EXPR_TARGETENTRY
+            targetentry {
+              expression {
+                type: UNIVPLAN_EXPR_VAR
+                var {
+                  varNo: 1
+                  varAttNo: 1
+                  typeId: 102
+                  typeMod: -1
+                }
+              }
+              resJunk: false
+            }
+          }
+          targetList {
+            type: UNIVPLAN_EXPR_TARGETENTRY
+            targetentry {
+              expression {
+                type: UNIVPLAN_EXPR_VAR
+                var {
+                  varNo: 1
+                  varAttNo: 2
+                  typeId: 102
+                  typeMod: -1
+                }
+              }
+              resJunk: false
+            }
+          }
+          targetList {
+            type: UNIVPLAN_EXPR_TARGETENTRY
+            targetentry {
+              expression {
+                type: UNIVPLAN_EXPR_VAR
+                var {
+                  varNo: 1
+                  varAttNo: 3
+                  typeId: 250
+                  typeMod: -1
+                }
+              }
+              resJunk: false
+            }
+          }
+          leftPlan {
+            type: UNIVPLAN_AGG
+            agg {
+              super {
+                targetList {
+                  type: UNIVPLAN_EXPR_TARGETENTRY
+                  targetentry {
+                    expression {
+                      type: UNIVPLAN_EXPR_AGGREF
+                      aggref {
+                        transFuncId: 493
+                        retType: 103
+                        transInitVal: true
+                        args {
+                          type: UNIVPLAN_EXPR_VAR
+                          var {
+                            varNo: 65001
+                            varAttNo: 1
+                            typeId: 103
+                            typeMod: -1
+                          }
+                        }
+                        funcId: 492
+                      }
+                    }
+                    resJunk: false
+                  }
+                }
+                targetList {
+                  type: UNIVPLAN_EXPR_TARGETENTRY
+                  targetentry {
+                    expression {
+                      type: UNIVPLAN_EXPR_VAR
+                      var {
+                        varNo: 1
+                        varAttNo: 2
+                        typeId: 102
+                        typeMod: -1
+                      }
+                    }
+                    resJunk: false
+                  }
+                }
+                targetList {
+                  type: UNIVPLAN_EXPR_TARGETENTRY
+                  targetentry {
+                    expression {
+                      type: UNIVPLAN_EXPR_VAR
+                      var {
+                        varNo: 1
+                        varAttNo: 3
+                        typeId: 250
+                        typeMod: -1
+                      }
+                    }
+                    resJunk: false
+                  }
+                }
+                leftPlan {
+                  type: UNIVPLAN_SCAN_SEQ
+                  scanSeq {
+                    super {
+                      targetList {
+                        type: UNIVPLAN_EXPR_TARGETENTRY
+                        targetentry {
+                          expression {
+                            type: UNIVPLAN_EXPR_VAR
+                            var {
+                              varNo: 1
+                              varAttNo: 1
+                              typeId: 102
+                              typeMod: -1
+                            }
+                          }
+                          resJunk: false
+                        }
+                      }
+                      targetList {
+                        type: UNIVPLAN_EXPR_TARGETENTRY
+                        targetentry {
+                          expression {
+                            type: UNIVPLAN_EXPR_VAR
+                            var {
+                              varNo: 1
+                              varAttNo: 3
+                              typeId: 250
+                              typeMod: -1
+                            }
+                          }
+                          resJunk: false
+                        }
+                      }
+                      qualList {
+                        type: UNIVPLAN_EXPR_OPEXPR
+                        opexpr {
+                          funcId: 158
+                          retType: 300
+                          args {
+                            type: UNIVPLAN_EXPR_VAR
+                            var {
+                              varNo: 1
+                              varAttNo: 2
+                              typeId: 102
+                              typeMod: -1
+                            }
+                          }
+                          args {
+                            type: UNIVPLAN_EXPR_CONST
+                            val {
+                              type: 102
+                              isNull: false
+                              value: "1"
+                              typeMod: -1
+                            }
+                          }
+                        }
+                      }
+                      qualList {
+                        type: UNIVPLAN_EXPR_OPEXPR
+                        opexpr {
+                          funcId: 92
+                          retType: 300
+                          args {
+                            type: UNIVPLAN_EXPR_VAR
+                            var {
+                              varNo: 1
+                              varAttNo: 1
+                              typeId: 102
+                              typeMod: -1
+                            }
+                          }
+                          args {
+                            type: UNIVPLAN_EXPR_VAR
+                            var {
+                              varNo: 1
+                              varAttNo: 2
+                              typeId: 102
+                              typeMod: -1
+                            }
+                          }
+                        }
+                      }
+                      qualList {
+                        type: UNIVPLAN_EXPR_OPEXPR
+                        opexpr {
+                          funcId: 35
+                          retType: 300
+                          args {
+                            type: UNIVPLAN_EXPR_FUNCEXPR
+                            funcexpr {
+                              funcId: 0
+                              retType: 300
+                            }
+                          }
+                          args {
+                            type: UNIVPLAN_EXPR_VAR
+                            var {
+                              varNo: 1
+                              varAttNo: 1
+                              typeId: 102
+                              typeMod: -1
+                            }
+                          }
+                        }
+                      }
+                      qualList {
+                        type: UNIVPLAN_EXPR_BOOLEXPR
+                        boolexpr {
+                          args {
+                            type: UNIVPLAN_EXPR_OPEXPR
+                            opexpr {
+                              funcId: 164
+                              retType: 300
+                              args {
+                                type: UNIVPLAN_EXPR_VAR
+                                var {
+                                  varNo: 1
+                                  varAttNo: 1
+                                  typeId: 102
+                                  typeMod: -1
+                                }
+                              }
+                              args {
+                                type: UNIVPLAN_EXPR_CONST
+                                val {
+                                  type: 102
+                                  isNull: false
+                                  value: "1"
+                                  typeMod: -1
+                                }
+                              }
+                            }
+                          }
+                          args {
+                            type: UNIVPLAN_EXPR_OPEXPR
+                            opexpr {
+                              funcId: 14
+                              retType: 300
+                              args {
+                                type: UNIVPLAN_EXPR_VAR
+                                var {
+                                  varNo: 1
+                                  varAttNo: 2
+                                  typeId: 102
+                                  typeMod: -1
+                                }
+                              }
+                              args {
+                                type: UNIVPLAN_EXPR_CONST
+                                val {
+                                  type: 102
+                                  isNull: false
+                                  value: "10"
+                                  typeMod: -1
+                                }
+                              }
+                            }
+                          }
+                          type: BOOLEXPRTYPE_OR_EXPR
+                        }
+                      }
+                    }
+                    relId: 1
+                    tasks {
+                      serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                    }
+                    tasks {
+                      serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                    }
+                    columnsToRead: 0
+                    columnsToRead: 1
+                  }
+                }
+              }
+              numGroups: 1000
+              groupColIndexes: 1
+              groupColIndexes: 3
+            }
+          }
+        }
+        targetStageNo: 2
+        currentStageNo: 1
+        hashExpr {
+          type: UNIVPLAN_EXPR_VAR
+          var {
+            varNo: 1
+            varAttNo: 0
+            typeId: 102
+            typeMod: -1
+          }
+        }
+        magmaTable: false
+        rangeNum: 0
+      }
+    }
+    rangeTables {
+      table {
+        tableId: 0
+        format: INVALID_FORMAT
+        location: ""
+        tableOptionsInJson: ""
+      }
+    }
+    rangeTables {
+      table {
+        tableId: 2
+        format: ORC_FORMAT
+        location: "/tmp"
+        tableOptionsInJson: "TableOptionsInJson_20"
+        columns {
+          columnName: "p1"
+          typeId: 102
+          typeMod: -1
+        }
+        columns {
+          columnName: "p2"
+          typeId: 250
+          typeMod: -1
+        }
+      }
+    }
+    receivers {
+      listener {
+        address: "mdw"
+        port: 101
+      }
+    }
+    receivers {
+      listener {
+        address: "smdw"
+        port: 201
+      }
+      listener {
+        address: "smdw"
+        port: 203
+      }
+    }
+    stageNo: 1
+    doInstrument: true
+    nCrossLevelParams: 0
+    commonValue {
+      key: "TableOptionsInJson_20"
+      value: "option1 string in json"
+    }
+    cmdType: CMD_SELECT
+  }
+  rangeTables {
+    table {
+      tableId: 0
+      format: INVALID_FORMAT
+      location: ""
+      tableOptionsInJson: ""
+    }
+  }
+  rangeTables {
+    table {
+      tableId: 2
+      format: ORC_FORMAT
+      location: "/tmp"
+      tableOptionsInJson: "TableOptionsInJson_20"
+      columns {
+        columnName: "p1"
+        typeId: 102
+        typeMod: -1
+      }
+      columns {
+        columnName: "p2"
+        typeId: 250
+        typeMod: -1
+      }
+    }
+  }
+  receivers {
+    listener {
+      address: "mdw"
+      port: 101
+    }
+  }
+  receivers {
+    listener {
+      address: "smdw"
+      port: 201
+    }
+    listener {
+      address: "smdw"
+      port: 203
+    }
+  }
+  stageNo: 2
+  doInstrument: true
+  nCrossLevelParams: 0
+  commonValue {
+    key: "TableOptionsInJson_20"
+    value: "option1 string in json"
+  }
+  cmdType: CMD_SELECT
+}
+rangeTables {
+  table {
+    tableId: 0
+    format: INVALID_FORMAT
+    location: ""
+    tableOptionsInJson: ""
+  }
+}
+rangeTables {
+  table {
+    tableId: 2
+    format: ORC_FORMAT
+    location: "/tmp"
+    tableOptionsInJson: "TableOptionsInJson_20"
+    columns {
+      columnName: "p1"
+      typeId: 102
+      typeMod: -1
+    }
+    columns {
+      columnName: "p2"
+      typeId: 250
+      typeMod: -1
+    }
+  }
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
+stageNo: 0
+doInstrument: true
+nCrossLevelParams: 0
+commonValue {
+  key: "TableOptionsInJson_20"
+  value: "option1 string in json"
+}
+cmdType: CMD_SELECT
diff --git a/depends/univplan/test/data/TestCompletedPlanBefore b/depends/univplan/test/data/TestCompletedPlanBefore
new file mode 100644
index 0000000..19e755e
--- /dev/null
+++ b/depends/univplan/test/data/TestCompletedPlanBefore
@@ -0,0 +1,628 @@
+plan {
+  type: UNIVPLAN_LIMIT
+  limit {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 2
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_CONNECTOR
+        connector {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 2
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            leftPlan {
+              type: UNIVPLAN_LIMIT
+              limit {
+                super {
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 1
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 2
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 3
+                          typeId: 250
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  leftPlan {
+                    type: UNIVPLAN_AGG
+                    agg {
+                      super {
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_AGGREF
+                              aggref {
+                                transFuncId: 494
+                                retType: 103
+                                transInitVal: true
+                                finalFuncId: 38324
+                                args {
+                                  type: UNIVPLAN_EXPR_VAR
+                                  var {
+                                    varNo: 65001
+                                    varAttNo: 1
+                                    typeId: 103
+                                    typeMod: -1
+                                  }
+                                }
+                                funcId: 492
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 2
+                                typeId: 102
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 3
+                                typeId: 250
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        leftPlan {
+                          type: UNIVPLAN_CONNECTOR
+                          connector {
+                            super {
+                              targetList {
+                                type: UNIVPLAN_EXPR_TARGETENTRY
+                                targetentry {
+                                  expression {
+                                    type: UNIVPLAN_EXPR_VAR
+                                    var {
+                                      varNo: 1
+                                      varAttNo: 1
+                                      typeId: 102
+                                      typeMod: -1
+                                    }
+                                  }
+                                  resJunk: false
+                                }
+                              }
+                              targetList {
+                                type: UNIVPLAN_EXPR_TARGETENTRY
+                                targetentry {
+                                  expression {
+                                    type: UNIVPLAN_EXPR_VAR
+                                    var {
+                                      varNo: 1
+                                      varAttNo: 2
+                                      typeId: 102
+                                      typeMod: -1
+                                    }
+                                  }
+                                  resJunk: false
+                                }
+                              }
+                              targetList {
+                                type: UNIVPLAN_EXPR_TARGETENTRY
+                                targetentry {
+                                  expression {
+                                    type: UNIVPLAN_EXPR_VAR
+                                    var {
+                                      varNo: 1
+                                      varAttNo: 3
+                                      typeId: 250
+                                      typeMod: -1
+                                    }
+                                  }
+                                  resJunk: false
+                                }
+                              }
+                              leftPlan {
+                                type: UNIVPLAN_AGG
+                                agg {
+                                  super {
+                                    targetList {
+                                      type: UNIVPLAN_EXPR_TARGETENTRY
+                                      targetentry {
+                                        expression {
+                                          type: UNIVPLAN_EXPR_AGGREF
+                                          aggref {
+                                            transFuncId: 493
+                                            retType: 103
+                                            transInitVal: true
+                                            args {
+                                              type: UNIVPLAN_EXPR_VAR
+                                              var {
+                                                varNo: 65001
+                                                varAttNo: 1
+                                                typeId: 103
+                                                typeMod: -1
+                                              }
+                                            }
+                                            funcId: 492
+                                          }
+                                        }
+                                        resJunk: false
+                                      }
+                                    }
+                                    targetList {
+                                      type: UNIVPLAN_EXPR_TARGETENTRY
+                                      targetentry {
+                                        expression {
+                                          type: UNIVPLAN_EXPR_VAR
+                                          var {
+                                            varNo: 1
+                                            varAttNo: 2
+                                            typeId: 102
+                                            typeMod: -1
+                                          }
+                                        }
+                                        resJunk: false
+                                      }
+                                    }
+                                    targetList {
+                                      type: UNIVPLAN_EXPR_TARGETENTRY
+                                      targetentry {
+                                        expression {
+                                          type: UNIVPLAN_EXPR_VAR
+                                          var {
+                                            varNo: 1
+                                            varAttNo: 3
+                                            typeId: 250
+                                            typeMod: -1
+                                          }
+                                        }
+                                        resJunk: false
+                                      }
+                                    }
+                                    leftPlan {
+                                      type: UNIVPLAN_SCAN_SEQ
+                                      scanSeq {
+                                        super {
+                                          targetList {
+                                            type: UNIVPLAN_EXPR_TARGETENTRY
+                                            targetentry {
+                                              expression {
+                                                type: UNIVPLAN_EXPR_VAR
+                                                var {
+                                                  varNo: 1
+                                                  varAttNo: 1
+                                                  typeId: 102
+                                                  typeMod: -1
+                                                }
+                                              }
+                                              resJunk: false
+                                            }
+                                          }
+                                          targetList {
+                                            type: UNIVPLAN_EXPR_TARGETENTRY
+                                            targetentry {
+                                              expression {
+                                                type: UNIVPLAN_EXPR_VAR
+                                                var {
+                                                  varNo: 1
+                                                  varAttNo: 3
+                                                  typeId: 250
+                                                  typeMod: -1
+                                                }
+                                              }
+                                              resJunk: false
+                                            }
+                                          }
+                                          qualList {
+                                            type: UNIVPLAN_EXPR_OPEXPR
+                                            opexpr {
+                                              funcId: 158
+                                              retType: 300
+                                              args {
+                                                type: UNIVPLAN_EXPR_VAR
+                                                var {
+                                                  varNo: 1
+                                                  varAttNo: 2
+                                                  typeId: 102
+                                                  typeMod: -1
+                                                }
+                                              }
+                                              args {
+                                                type: UNIVPLAN_EXPR_CONST
+                                                val {
+                                                  type: 102
+                                                  isNull: false
+                                                  value: "1"
+                                                  typeMod: -1
+                                                }
+                                              }
+                                            }
+                                          }
+                                          qualList {
+                                            type: UNIVPLAN_EXPR_OPEXPR
+                                            opexpr {
+                                              funcId: 92
+                                              retType: 300
+                                              args {
+                                                type: UNIVPLAN_EXPR_VAR
+                                                var {
+                                                  varNo: 1
+                                                  varAttNo: 1
+                                                  typeId: 102
+                                                  typeMod: -1
+                                                }
+                                              }
+                                              args {
+                                                type: UNIVPLAN_EXPR_VAR
+                                                var {
+                                                  varNo: 1
+                                                  varAttNo: 2
+                                                  typeId: 102
+                                                  typeMod: -1
+                                                }
+                                              }
+                                            }
+                                          }
+                                          qualList {
+                                            type: UNIVPLAN_EXPR_OPEXPR
+                                            opexpr {
+                                              funcId: 35
+                                              retType: 300
+                                              args {
+                                                type: UNIVPLAN_EXPR_FUNCEXPR
+                                                funcexpr {
+                                                  funcId: 0
+                                                  retType: 300
+                                                }
+                                              }
+                                              args {
+                                                type: UNIVPLAN_EXPR_VAR
+                                                var {
+                                                  varNo: 1
+                                                  varAttNo: 1
+                                                  typeId: 102
+                                                  typeMod: -1
+                                                }
+                                              }
+                                            }
+                                          }
+                                          qualList {
+                                            type: UNIVPLAN_EXPR_BOOLEXPR
+                                            boolexpr {
+                                              args {
+                                                type: UNIVPLAN_EXPR_OPEXPR
+                                                opexpr {
+                                                  funcId: 164
+                                                  retType: 300
+                                                  args {
+                                                    type: UNIVPLAN_EXPR_VAR
+                                                    var {
+                                                      varNo: 1
+                                                      varAttNo: 1
+                                                      typeId: 102
+                                                      typeMod: -1
+                                                    }
+                                                  }
+                                                  args {
+                                                    type: UNIVPLAN_EXPR_CONST
+                                                    val {
+                                                      type: 102
+                                                      isNull: false
+                                                      value: "1"
+                                                      typeMod: -1
+                                                    }
+                                                  }
+                                                }
+                                              }
+                                              args {
+                                                type: UNIVPLAN_EXPR_OPEXPR
+                                                opexpr {
+                                                  funcId: 14
+                                                  retType: 300
+                                                  args {
+                                                    type: UNIVPLAN_EXPR_VAR
+                                                    var {
+                                                      varNo: 1
+                                                      varAttNo: 2
+                                                      typeId: 102
+                                                      typeMod: -1
+                                                    }
+                                                  }
+                                                  args {
+                                                    type: UNIVPLAN_EXPR_CONST
+                                                    val {
+                                                      type: 102
+                                                      isNull: false
+                                                      value: "10"
+                                                      typeMod: -1
+                                                    }
+                                                  }
+                                                }
+                                              }
+                                              type: BOOLEXPRTYPE_OR_EXPR
+                                            }
+                                          }
+                                        }
+                                        relId: 1
+                                        tasks {
+                                          serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                                        }
+                                        tasks {
+                                          serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                                        }
+                                        columnsToRead: 0
+                                        columnsToRead: 1
+                                      }
+                                    }
+                                  }
+                                  numGroups: 1000
+                                  groupColIndexes: 1
+                                  groupColIndexes: 3
+                                }
+                              }
+                            }
+                            type: CONNECTORTYPE_SHUFFLE
+                            hashExpr {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 0
+                                typeId: 102
+                                typeMod: -1
+                              }
+                            }
+                          }
+                        }
+                      }
+                      numGroups: 1000
+                      groupColIndexes: 1
+                      groupColIndexes: 3
+                    }
+                  }
+                  planRows: 110
+                  planRowWidth: 0
+                  operatorMemKB: 0
+                }
+                limitOffset {
+                  type: UNIVPLAN_EXPR_CONST
+                  val {
+                    type: 103
+                    isNull: false
+                    value: "0"
+                    typeMod: -1
+                  }
+                }
+                limitCount {
+                  type: UNIVPLAN_EXPR_OPEXPR
+                  opexpr {
+                    funcId: 339
+                    retType: 103
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 103
+                        isNull: false
+                        value: "10"
+                        typeMod: -1
+                      }
+                    }
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 103
+                        isNull: false
+                        value: "100"
+                        typeMod: -1
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+          type: CONNECTORTYPE_CONVERGE
+        }
+      }
+      planRows: 110
+      planRowWidth: 0
+      operatorMemKB: 0
+    }
+    limitOffset {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "100"
+        typeMod: -1
+      }
+    }
+    limitCount {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "10"
+        typeMod: -1
+      }
+    }
+  }
+}
+rangeTables {
+  table {
+    tableId: 0
+    format: INVALID_FORMAT
+    location: ""
+    tableOptionsInJson: ""
+  }
+}
+rangeTables {
+  table {
+    tableId: 2
+    format: ORC_FORMAT
+    location: "/tmp"
+    tableOptionsInJson: "TableOptionsInJson_20"
+    columns {
+      columnName: "p1"
+      typeId: 102
+      typeMod: -1
+    }
+    columns {
+      columnName: "p2"
+      typeId: 250
+      typeMod: -1
+    }
+  }
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
+doInstrument: true
+commonValue {
+  key: "TableOptionsInJson_20"
+  value: "option1 string in json"
+}
diff --git a/depends/univplan/test/data/TestLimitCount b/depends/univplan/test/data/TestLimitCount
new file mode 100644
index 0000000..56da317
--- /dev/null
+++ b/depends/univplan/test/data/TestLimitCount
@@ -0,0 +1,228 @@
+plan {
+  type: UNIVPLAN_LIMIT
+  limit {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 2
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_CONNECTOR
+        connector {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            leftPlan {
+              type: UNIVPLAN_LIMIT
+              limit {
+                super {
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 1
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 2
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 3
+                          typeId: 250
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  leftPlan {
+                    type: UNIVPLAN_SCAN_SEQ
+                    scanSeq {
+                      super {
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 1
+                                typeId: 102
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 3
+                                typeId: 250
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                      }
+                      relId: 1
+                      tasks {
+                        serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                      }
+                      tasks {
+                        serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                      }
+                      columnsToRead: 0
+                      columnsToRead: 1
+                    }
+                  }
+                  planRows: 9
+                  planRowWidth: 0
+                  operatorMemKB: 0
+                }
+                limitCount {
+                  type: UNIVPLAN_EXPR_OPEXPR
+                  opexpr {
+                    funcId: 339
+                    retType: 103
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 103
+                        isNull: false
+                        value: "10"
+                        typeMod: -1
+                      }
+                    }
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 103
+                        isNull: false
+                        value: "-1"
+                        typeMod: -1
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+          type: CONNECTORTYPE_CONVERGE
+        }
+      }
+      planRows: 9
+      planRowWidth: 0
+      operatorMemKB: 0
+    }
+    limitCount {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "10"
+        typeMod: -1
+      }
+    }
+  }
+}
diff --git a/depends/univplan/test/data/TestLimitCountOffset b/depends/univplan/test/data/TestLimitCountOffset
new file mode 100644
index 0000000..f1bebf4
--- /dev/null
+++ b/depends/univplan/test/data/TestLimitCountOffset
@@ -0,0 +1,246 @@
+plan {
+  type: UNIVPLAN_LIMIT
+  limit {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 2
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_CONNECTOR
+        connector {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            leftPlan {
+              type: UNIVPLAN_LIMIT
+              limit {
+                super {
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 1
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 2
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 3
+                          typeId: 250
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  leftPlan {
+                    type: UNIVPLAN_SCAN_SEQ
+                    scanSeq {
+                      super {
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 1
+                                typeId: 102
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 3
+                                typeId: 250
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                      }
+                      relId: 1
+                      tasks {
+                        serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                      }
+                      tasks {
+                        serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                      }
+                      columnsToRead: 0
+                      columnsToRead: 1
+                    }
+                  }
+                  planRows: 110
+                  planRowWidth: 0
+                  operatorMemKB: 0
+                }
+                limitOffset {
+                  type: UNIVPLAN_EXPR_CONST
+                  val {
+                    type: 103
+                    isNull: false
+                    value: "0"
+                    typeMod: -1
+                  }
+                }
+                limitCount {
+                  type: UNIVPLAN_EXPR_OPEXPR
+                  opexpr {
+                    funcId: 339
+                    retType: 103
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 103
+                        isNull: false
+                        value: "10"
+                        typeMod: -1
+                      }
+                    }
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 103
+                        isNull: false
+                        value: "100"
+                        typeMod: -1
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+          type: CONNECTORTYPE_CONVERGE
+        }
+      }
+      planRows: 110
+      planRowWidth: 0
+      operatorMemKB: 0
+    }
+    limitOffset {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "100"
+        typeMod: -1
+      }
+    }
+    limitCount {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "10"
+        typeMod: -1
+      }
+    }
+  }
+}
diff --git a/depends/univplan/test/data/TestLimitOffset b/depends/univplan/test/data/TestLimitOffset
new file mode 100644
index 0000000..dd7a801
--- /dev/null
+++ b/depends/univplan/test/data/TestLimitOffset
@@ -0,0 +1,212 @@
+plan {
+  type: UNIVPLAN_LIMIT
+  limit {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 2
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_CONNECTOR
+        connector {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            leftPlan {
+              type: UNIVPLAN_LIMIT
+              limit {
+                super {
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 1
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 2
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 3
+                          typeId: 250
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  leftPlan {
+                    type: UNIVPLAN_SCAN_SEQ
+                    scanSeq {
+                      super {
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 1
+                                typeId: 102
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                        targetList {
+                          type: UNIVPLAN_EXPR_TARGETENTRY
+                          targetentry {
+                            expression {
+                              type: UNIVPLAN_EXPR_VAR
+                              var {
+                                varNo: 1
+                                varAttNo: 3
+                                typeId: 250
+                                typeMod: -1
+                              }
+                            }
+                            resJunk: false
+                          }
+                        }
+                      }
+                      relId: 1
+                      tasks {
+                        serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                      }
+                      tasks {
+                        serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                      }
+                      columnsToRead: 0
+                      columnsToRead: 1
+                    }
+                  }
+                  planRows: 99
+                  planRowWidth: 0
+                  operatorMemKB: 0
+                }
+                limitOffset {
+                  type: UNIVPLAN_EXPR_CONST
+                  val {
+                    type: 103
+                    isNull: false
+                    value: "0"
+                    typeMod: -1
+                  }
+                }
+              }
+            }
+          }
+          type: CONNECTORTYPE_CONVERGE
+        }
+      }
+      planRows: 99
+      planRowWidth: 0
+      operatorMemKB: 0
+    }
+    limitOffset {
+      type: UNIVPLAN_EXPR_CONST
+      val {
+        type: 103
+        isNull: false
+        value: "100"
+        typeMod: -1
+      }
+    }
+  }
+}
diff --git a/depends/univplan/test/data/TestNullTest b/depends/univplan/test/data/TestNullTest
new file mode 100644
index 0000000..5f2e916
--- /dev/null
+++ b/depends/univplan/test/data/TestNullTest
@@ -0,0 +1,136 @@
+plan {
+  type: UNIVPLAN_CONNECTOR
+  connector {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_SCAN_SEQ
+        scanSeq {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_NULLTEST
+                  nulltest {
+                    arg {
+                      type: UNIVPLAN_EXPR_VAR
+                      var {
+                        varNo: 1
+                        varAttNo: 3
+                        typeId: 250
+                        typeMod: -1
+                      }
+                    }
+                    type: NULLTESTTYPE_IS_NOT_NULL
+                  }
+                }
+                resJunk: false
+              }
+            }
+          }
+          relId: 1
+          tasks {
+            serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+          }
+          tasks {
+            serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+          }
+          columnsToRead: 0
+          columnsToRead: 1
+        }
+      }
+    }
+    type: CONNECTORTYPE_CONVERGE
+  }
+}
+rangeTables {
+  table {
+    tableId: 0
+    format: INVALID_FORMAT
+    location: ""
+    tableOptionsInJson: ""
+  }
+}
+rangeTables {
+  table {
+    tableId: 2
+    format: ORC_FORMAT
+    location: "/tmp"
+    tableOptionsInJson: "TableOptionsInJson_20"
+    columns {
+      columnName: "p1"
+      typeId: 102
+      typeMod: -1
+    }
+    columns {
+      columnName: "p2"
+      typeId: 250
+      typeMod: -1
+    }
+  }
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
+commonValue {
+  key: "TableOptionsInJson_20"
+  value: "option1 string in json"
+}
diff --git a/depends/univplan/test/data/TestQualListAndExpr b/depends/univplan/test/data/TestQualListAndExpr
new file mode 100644
index 0000000..27382a0
--- /dev/null
+++ b/depends/univplan/test/data/TestQualListAndExpr
@@ -0,0 +1,259 @@
+plan {
+  type: UNIVPLAN_CONNECTOR
+  connector {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_SCAN_SEQ
+        scanSeq {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            qualList {
+              type: UNIVPLAN_EXPR_OPEXPR
+              opexpr {
+                funcId: 158
+                retType: 300
+                args {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 2
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                args {
+                  type: UNIVPLAN_EXPR_CONST
+                  val {
+                    type: 102
+                    isNull: false
+                    value: "1"
+                    typeMod: -1
+                  }
+                }
+              }
+            }
+            qualList {
+              type: UNIVPLAN_EXPR_OPEXPR
+              opexpr {
+                funcId: 92
+                retType: 300
+                args {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                args {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 2
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+              }
+            }
+            qualList {
+              type: UNIVPLAN_EXPR_OPEXPR
+              opexpr {
+                funcId: 35
+                retType: 300
+                args {
+                  type: UNIVPLAN_EXPR_FUNCEXPR
+                  funcexpr {
+                    funcId: 0
+                    retType: 300
+                  }
+                }
+                args {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+              }
+            }
+            qualList {
+              type: UNIVPLAN_EXPR_BOOLEXPR
+              boolexpr {
+                args {
+                  type: UNIVPLAN_EXPR_OPEXPR
+                  opexpr {
+                    funcId: 164
+                    retType: 300
+                    args {
+                      type: UNIVPLAN_EXPR_VAR
+                      var {
+                        varNo: 1
+                        varAttNo: 1
+                        typeId: 102
+                        typeMod: -1
+                      }
+                    }
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 102
+                        isNull: false
+                        value: "1"
+                        typeMod: -1
+                      }
+                    }
+                  }
+                }
+                args {
+                  type: UNIVPLAN_EXPR_OPEXPR
+                  opexpr {
+                    funcId: 14
+                    retType: 300
+                    args {
+                      type: UNIVPLAN_EXPR_VAR
+                      var {
+                        varNo: 1
+                        varAttNo: 2
+                        typeId: 102
+                        typeMod: -1
+                      }
+                    }
+                    args {
+                      type: UNIVPLAN_EXPR_CONST
+                      val {
+                        type: 102
+                        isNull: false
+                        value: "10"
+                        typeMod: -1
+                      }
+                    }
+                  }
+                }
+                type: BOOLEXPRTYPE_OR_EXPR
+              }
+            }
+          }
+          relId: 1
+          tasks {
+            serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+          }
+          tasks {
+            serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+          }
+          columnsToRead: 0
+          columnsToRead: 1
+        }
+      }
+    }
+    type: CONNECTORTYPE_CONVERGE
+  }
+}
+rangeTables {
+  table {
+    tableId: 0
+    format: INVALID_FORMAT
+    location: ""
+    tableOptionsInJson: ""
+  }
+}
+rangeTables {
+  table {
+    tableId: 2
+    format: ORC_FORMAT
+    location: "/tmp"
+    tableOptionsInJson: "TableOptionsInJson_20"
+    columns {
+      columnName: "p1"
+      typeId: 102
+      typeMod: -1
+    }
+    columns {
+      columnName: "p2"
+      typeId: 250
+      typeMod: -1
+    }
+  }
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
+commonValue {
+  key: "TableOptionsInJson_20"
+  value: "option1 string in json"
+}
diff --git a/depends/univplan/test/data/TestSort b/depends/univplan/test/data/TestSort
new file mode 100644
index 0000000..0d2b6fd
--- /dev/null
+++ b/depends/univplan/test/data/TestSort
@@ -0,0 +1,99 @@
+plan {
+  type: UNIVPLAN_CONNECTOR
+  connector {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_SORT
+        sort {
+          super {
+            leftPlan {
+              type: UNIVPLAN_SCAN_SEQ
+              scanSeq {
+                super {
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 1
+                          typeId: 102
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                  targetList {
+                    type: UNIVPLAN_EXPR_TARGETENTRY
+                    targetentry {
+                      expression {
+                        type: UNIVPLAN_EXPR_VAR
+                        var {
+                          varNo: 1
+                          varAttNo: 3
+                          typeId: 250
+                          typeMod: -1
+                        }
+                      }
+                      resJunk: false
+                    }
+                  }
+                }
+                relId: 1
+                tasks {
+                  serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+                }
+                tasks {
+                  serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+                }
+                columnsToRead: 0
+                columnsToRead: 1
+              }
+            }
+          }
+          colIdx: 1
+          colIdx: 2
+          sortFuncId: 21
+          sortFuncId: 14
+        }
+      }
+    }
+    type: CONNECTORTYPE_CONVERGE
+    colIdx: 1
+    colIdx: 2
+    sortFuncId: 21
+    sortFuncId: 14
+  }
+}
diff --git a/depends/univplan/test/data/TestStagizeAfter b/depends/univplan/test/data/TestStagizeAfter
new file mode 100644
index 0000000..1a8787a
--- /dev/null
+++ b/depends/univplan/test/data/TestStagizeAfter
@@ -0,0 +1,167 @@
+plan {
+  type: UNIVPLAN_SINK
+  sink {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+    }
+    connectorType: CONNECTORTYPE_BROADCAST
+    sourceStageNo: 1
+    currentStageNo: 0
+  }
+}
+childStages {
+  plan {
+    type: UNIVPLAN_BROADCAST
+    broadcast {
+      super {
+        targetList {
+          type: UNIVPLAN_EXPR_TARGETENTRY
+          targetentry {
+            expression {
+              type: UNIVPLAN_EXPR_VAR
+              var {
+                varNo: 1
+                varAttNo: 1
+                typeId: 102
+                typeMod: -1
+              }
+            }
+            resJunk: false
+          }
+        }
+        targetList {
+          type: UNIVPLAN_EXPR_TARGETENTRY
+          targetentry {
+            expression {
+              type: UNIVPLAN_EXPR_VAR
+              var {
+                varNo: 1
+                varAttNo: 3
+                typeId: 250
+                typeMod: -1
+              }
+            }
+            resJunk: false
+          }
+        }
+        leftPlan {
+          type: UNIVPLAN_SCAN_SEQ
+          scanSeq {
+            super {
+              targetList {
+                type: UNIVPLAN_EXPR_TARGETENTRY
+                targetentry {
+                  expression {
+                    type: UNIVPLAN_EXPR_VAR
+                    var {
+                      varNo: 1
+                      varAttNo: 1
+                      typeId: 102
+                      typeMod: -1
+                    }
+                  }
+                  resJunk: false
+                }
+              }
+              targetList {
+                type: UNIVPLAN_EXPR_TARGETENTRY
+                targetentry {
+                  expression {
+                    type: UNIVPLAN_EXPR_VAR
+                    var {
+                      varNo: 1
+                      varAttNo: 3
+                      typeId: 250
+                      typeMod: -1
+                    }
+                  }
+                  resJunk: false
+                }
+              }
+            }
+            relId: 1
+            tasks {
+              serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+            }
+            tasks {
+              serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+            }
+            columnsToRead: 0
+            columnsToRead: 1
+          }
+        }
+      }
+      targetStageNo: 0
+      currentStageNo: 1
+    }
+  }
+  receivers {
+    listener {
+      address: "mdw"
+      port: 101
+    }
+  }
+  receivers {
+    listener {
+      address: "smdw"
+      port: 201
+    }
+    listener {
+      address: "smdw"
+      port: 203
+    }
+  }
+  stageNo: 1
+  doInstrument: false
+  nCrossLevelParams: 0
+  cmdType: CMD_SELECT
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
+stageNo: 0
+doInstrument: false
+nCrossLevelParams: 0
+cmdType: CMD_SELECT
diff --git a/depends/univplan/test/data/TestStagizeBefore b/depends/univplan/test/data/TestStagizeBefore
new file mode 100644
index 0000000..50903ee
--- /dev/null
+++ b/depends/univplan/test/data/TestStagizeBefore
@@ -0,0 +1,100 @@
+plan {
+  type: UNIVPLAN_CONNECTOR
+  connector {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_SCAN_SEQ
+        scanSeq {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+          }
+          relId: 1
+          tasks {
+            serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+          }
+          tasks {
+            serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+          }
+          columnsToRead: 0
+          columnsToRead: 1
+        }
+      }
+    }
+    type: CONNECTORTYPE_BROADCAST
+  }
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
diff --git a/depends/univplan/test/data/TestUnivPlanProtoGenerate b/depends/univplan/test/data/TestUnivPlanProtoGenerate
new file mode 100644
index 0000000..859d2ae
--- /dev/null
+++ b/depends/univplan/test/data/TestUnivPlanProtoGenerate
@@ -0,0 +1,130 @@
+plan {
+  type: UNIVPLAN_CONNECTOR
+  connector {
+    super {
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 1
+              typeId: 102
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      targetList {
+        type: UNIVPLAN_EXPR_TARGETENTRY
+        targetentry {
+          expression {
+            type: UNIVPLAN_EXPR_VAR
+            var {
+              varNo: 1
+              varAttNo: 3
+              typeId: 250
+              typeMod: -1
+            }
+          }
+          resJunk: false
+        }
+      }
+      leftPlan {
+        type: UNIVPLAN_SCAN_SEQ
+        scanSeq {
+          super {
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 1
+                    typeId: 102
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+            targetList {
+              type: UNIVPLAN_EXPR_TARGETENTRY
+              targetentry {
+                expression {
+                  type: UNIVPLAN_EXPR_VAR
+                  var {
+                    varNo: 1
+                    varAttNo: 3
+                    typeId: 250
+                    typeMod: -1
+                  }
+                }
+                resJunk: false
+              }
+            }
+          }
+          relId: 1
+          tasks {
+            serializedSplits: "\000\000\001\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000/a\010\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\001\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000\377\377\377\377"
+          }
+          tasks {
+            serializedSplits: "\000\000\002\000\000\000\005\000\000\000\000\000\000\000\370\372\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\004\000\000\000\000\000\000\000/a/b\020\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\310\000\000\000\000\000\000\000d\000\000\000\000\000\000\000g\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\020\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377f\000\000\000\377\377\377\377\377\377\377\377\002\000\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\000\010\000\000\000\000\000\000\000\377\377\377\377\377\377\377\377"
+          }
+          columnsToRead: 0
+          columnsToRead: 1
+        }
+      }
+    }
+    type: CONNECTORTYPE_CONVERGE
+  }
+}
+rangeTables {
+  table {
+    tableId: 0
+    format: INVALID_FORMAT
+    location: ""
+    tableOptionsInJson: ""
+  }
+}
+rangeTables {
+  table {
+    tableId: 2
+    format: ORC_FORMAT
+    location: "/tmp"
+    tableOptionsInJson: "TableOptionsInJson_20"
+    columns {
+      columnName: "p1"
+      typeId: 102
+      typeMod: -1
+    }
+    columns {
+      columnName: "p2"
+      typeId: 250
+      typeMod: -1
+    }
+  }
+}
+receivers {
+  listener {
+    address: "mdw"
+    port: 101
+  }
+}
+receivers {
+  listener {
+    address: "smdw"
+    port: 201
+  }
+  listener {
+    address: "smdw"
+    port: 203
+  }
+}
+commonValue {
+  key: "TableOptionsInJson_20"
+  value: "option1 string in json"
+}
diff --git a/depends/univplan/test/parallel/parallel-launcher.py b/depends/univplan/test/parallel/parallel-launcher.py
new file mode 100755
index 0000000..f572f7b
--- /dev/null
+++ b/depends/univplan/test/parallel/parallel-launcher.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+This tool launches several shards of a gtest-based binary
+in parallel on a local machine.
+
+Example usage:
+
+parallel_launcher.py path/to/base_unittests
+"""
+
+import optparse
+import os
+import subprocess
+import sys
+import threading
+import time
+
+
+def StreamCopyWindows(stream_from, stream_to):
+  """Copies stream_from to stream_to."""
+
+  while True:
+    buf = stream_from.read(1024)
+    if not buf:
+      break
+    stream_to.write(buf)
+    stream_to.flush()
+
+def StreamCopyPosix(stream_from, stream_to, child_exited):
+  """
+  Copies stream_from to stream_to, and exits if child_exited
+  is signaled.
+  """
+
+  import fcntl
+
+  # Put the source stream in a non-blocking mode, so we can check
+  # child_exited when there is no data.
+  fd = stream_from.fileno()
+  fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+  fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+  while True:
+    try:
+      buf = os.read(fd, 1024)
+    except OSError, e:
+      if e.errno == 11 or e.errno == 35:
+        if child_exited.isSet():
+          break
+        time.sleep(0.1)
+        continue
+      raise
+    if not buf:
+      break
+    stream_to.write(buf)
+    stream_to.flush()
+
+class TestLauncher(object):
+  def __init__(self, args, executable, num_shards, shard):
+    self._args = args
+    self._executable = executable
+    self._num_shards = num_shards
+    self._shard = shard
+    self._test = None
+
+  def launch(self):
+    env = os.environ.copy()
+
+    env['CHROME_LOG_FILE'] = 'chrome_log_%d' % self._shard
+
+    if 'GTEST_TOTAL_SHARDS' in env:
+      # Handle the requested sharding transparently.
+      outer_shards = int(env['GTEST_TOTAL_SHARDS'])
+      outer_index = int(env['GTEST_SHARD_INDEX'])
+
+      env['GTEST_TOTAL_SHARDS'] = str(self._num_shards * outer_shards)
+
+      # Calculate the right shard index to pass to the child. This is going
+      # to be a shard of a shard.
+      env['GTEST_SHARD_INDEX'] = str((self._num_shards * outer_index) +
+                                     self._shard)
+    else:
+      env['GTEST_TOTAL_SHARDS'] = str(self._num_shards)
+      env['GTEST_SHARD_INDEX'] = str(self._shard)
+
+    args = self._args + ['--test-server-shard=' + str(self._shard)]
+
+    self._test = subprocess.Popen(args=args,
+                                  executable=self._executable,
+                                  stdout=subprocess.PIPE,
+                                  stderr=subprocess.STDOUT,
+                                  env=env)
+  def wait(self):
+    if subprocess.mswindows:
+      stdout_thread = threading.Thread(
+          target=StreamCopyWindows,
+          args=[self._test.stdout, sys.stdout])
+      stdout_thread.start()
+      code = self._test.wait()
+      stdout_thread.join()
+      return code
+    else:
+      child_exited = threading.Event()
+      stdout_thread = threading.Thread(
+          target=StreamCopyPosix,
+          args=[self._test.stdout, sys.stdout, child_exited])
+      stdout_thread.start()
+      code = self._test.wait()
+      child_exited.set()
+      stdout_thread.join()
+      return code
+
+def main(argv):
+  parser = optparse.OptionParser()
+  parser.add_option("--shards", type="int", dest="shards", default=16)
+
+  # Make it possible to pass options to the launched process.
+  # Options for parallel_launcher should be first, then the binary path,
+  # and finally - optional arguments for the launched binary.
+  parser.disable_interspersed_args()
+
+  options, args = parser.parse_args(argv)
+
+  if not args:
+    print 'You must provide path to the test binary'
+    return 1
+
+  env = os.environ
+  if bool('GTEST_TOTAL_SHARDS' in env) != bool('GTEST_SHARD_INDEX' in env):
+    print 'Inconsistent environment. GTEST_TOTAL_SHARDS and GTEST_SHARD_INDEX'
+    print 'should either be both defined, or both undefined.'
+    return 1
+
+  launchers = []
+
+  for shard in range(options.shards):
+    launcher = TestLauncher(args, args[0], options.shards, shard)
+    launcher.launch()
+    launchers.append(launcher)
+
+  return_code = 0
+  for launcher in launchers:
+    if launcher.wait() != 0:
+      return_code = 1
+
+  return return_code
+
+if __name__ == "__main__":
+  sys.exit(main(sys.argv[1:]))
diff --git a/depends/univplan/test/unit/CMakeLists.txt b/depends/univplan/test/unit/CMakeLists.txt
new file mode 100644
index 0000000..2eebc74
--- /dev/null
+++ b/depends/univplan/test/unit/CMakeLists.txt
@@ -0,0 +1,14 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+
+AUTO_SOURCES(unit_SOURCES "*.cc" "RECURSE" ${CMAKE_CURRENT_SOURCE_DIR})
+
+FIND_PACKAGE(GTest REQUIRED)
+
+INCLUDE_DIRECTORIES(${univplan_ROOT_DIR}/test/unit)
+INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS})
+
+ADD_EXECUTABLE(unit EXCLUDE_FROM_ALL
+    ${unit_SOURCES}
+)
+
+target_link_libraries(unit ${CLANG_LDFLAGS} univplan-shared gtest gmock)
diff --git a/depends/univplan/test/unit/test-basic-univplan.cc b/depends/univplan/test/unit/test-basic-univplan.cc
new file mode 100644
index 0000000..50caf34
--- /dev/null
+++ b/depends/univplan/test/unit/test-basic-univplan.cc
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#include "./test-univplan.h"
+
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+#include "dbcommon/log/exception.h"
+#include "dbcommon/log/logger.h"
+
+#include "univplan/common/plannode-util.h"
+#include "univplan/common/stagize.h"
+#include "univplan/common/var-util.h"
+#include "univplan/cwrapper/univplan-c.h"
+#include "univplan/testutil/univplan-proto-util.h"
+#include "univplan/univplanbuilder/univplanbuilder-expr-tree.h"
+#include "univplan/univplanbuilder/univplanbuilder-plan.h"
+#include "univplan/univplanbuilder/univplanbuilder-table.h"
+#include "univplan/univplanbuilder/univplanbuilder.h"
+
+namespace univplan {
+
+TEST(TestBasicUnivPlan, TestBasicUnivPlan) {
+  univplan::UnivPlanBuilder upb;
+  univplan::UnivPlanBuilderAgg *aggb;
+
+  univplan::UnivPlanBuilderNode::uptr aggb1 =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_AGG);
+  aggb = dynamic_cast<univplan::UnivPlanBuilderAgg *>(aggb1.get());
+
+  aggb->setNumGroups(0);
+  aggb->uid = 1;
+  aggb->pid = -1;
+
+  univplan::UnivPlanBuilderNode::uptr aggb2 =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_AGG);
+  aggb = dynamic_cast<univplan::UnivPlanBuilderAgg *>(aggb2.get());
+  aggb->setNumGroups(0);
+  aggb->uid = 2;
+  aggb->pid = 1;
+
+  univplan::UnivPlanBuilderNode::uptr aggb3 =
+      univplan::PlanNodeUtil::createPlanBuilderNode(univplan::UNIVPLAN_AGG);
+  aggb = dynamic_cast<univplan::UnivPlanBuilderAgg *>(aggb3.get());
+  aggb->setNumGroups(0);
+  aggb->uid = 3;
+  aggb->pid = 1;
+
+  upb.addPlanNode(true, std::move(aggb1));
+  upb.addPlanNode(true, std::move(aggb2));
+  upb.addPlanNode(false, std::move(aggb3));
+
+  UnivPlanPlan plan;
+  plan.ParseFromString(upb.serialize());
+  EXPECT_STREQ(upb.getJsonFormatedPlan().c_str(), plan.DebugString().c_str());
+}
+
+TEST(TestBasicUnivPlan, TestUnivPlanProtoGenerate) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, false, 0);
+  upu.constructRangeTable();
+  upu.constructReceiver();
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestUnivPlanProtoGenerate", jsonFormatPlan);
+  plan.readFromFile("TestUnivPlanProtoGenerate");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestLimitCountOffset) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructLimitTop(-1, 10, 100);
+  int32_t uid2 = upu.constructConnector(uid1, 2, false);
+  int32_t uid3 = upu.constructLimitBelow(uid2, 10, 100);
+  int32_t uid4 = upu.constructSeqScan(uid3, false, 0);
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestLimitCountOffset", jsonFormatPlan);
+  plan.readFromFile("TestLimitCountOffset");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestLimitCount) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructLimitTop(-1, 10, -1);
+  int32_t uid2 = upu.constructConnector(uid1, 2, false);
+  int32_t uid3 = upu.constructLimitBelow(uid2, 10, -1);
+  int32_t uid4 = upu.constructSeqScan(uid3, false, 0);
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestLimitCount", jsonFormatPlan);
+  plan.readFromFile("TestLimitCount");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestLimitOffset) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructLimitTop(-1, -1, 100);
+  int32_t uid2 = upu.constructConnector(uid1, 2, false);
+  int32_t uid3 = upu.constructLimitBelow(uid2, -1, 100);
+  int32_t uid4 = upu.constructSeqScan(uid3, false, 0);
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestLimitOffset", jsonFormatPlan);
+  plan.readFromFile("TestLimitOffset");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestAgg) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructAgg(uid1, 3);
+  int32_t uid3 = upu.constructConnector(uid2, 0, false);
+  int32_t uid4 = upu.constructAgg(uid3, 1);
+  int32_t uid5 = upu.constructSeqScan(uid4, false, 0);
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestAgg", jsonFormatPlan);
+  plan.readFromFile("TestAgg");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestSort) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructSortConnector(-1, 2);
+  int32_t uid2 = upu.constructSort(uid1);
+  int32_t uid3 = upu.constructSeqScan(uid2, false, 0);
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestSort", jsonFormatPlan);
+  plan.readFromFile("TestSort");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestQualListAndExpr) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, true, 0);
+  upu.constructRangeTable();
+  upu.constructReceiver();
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestQualListAndExpr", jsonFormatPlan);
+  plan.readFromFile("TestQualListAndExpr");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestBasicUnivPlan, TestNullTest) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, false, 2);
+  upu.constructRangeTable();
+  upu.constructReceiver();
+  const char *jsonFormatPlan = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestNullTest", jsonFormatPlan);
+  plan.readFromFile("TestNullTest");
+  const char *expecedPlan = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan, jsonFormatPlan);
+}
+
+TEST(TestUnivPlan, TestCommonValue) {
+  univplan::UnivPlanBuilder upb;
+  upb.getPlanBuilderPlan()->addCommonValue("abc", "value", nullptr);
+  EXPECT_EQ(1, upb.getPlanBuilderPlan()->getPlan()->commonvalue().size());
+
+  for (auto i = upb.getPlanBuilderPlan()->getPlan()->commonvalue().begin();
+       i != upb.getPlanBuilderPlan()->getPlan()->commonvalue().end(); ++i) {
+    LOG_INFO("key %s value %s", i->first.c_str(), i->second.c_str());
+  }
+
+  std::string fetch1 =
+      upb.getPlanBuilderPlan()->getPlan()->commonvalue().find("abc")->second;
+  LOG_INFO("pass 1");
+
+  EXPECT_STREQ("value", fetch1.c_str());
+  // add duplicate key when having no new key referenced, in this case, should
+  // have old value overwrited
+  upb.getPlanBuilderPlan()->addCommonValue("abc", "value2", nullptr);
+  fetch1 = upb.getPlanBuilderPlan()->getPlan()->commonvalue().at("abc");
+  EXPECT_STREQ("value2", fetch1.c_str());
+  LOG_INFO("pass 2");
+  // pass new key reference, duplicate key will cause new key generated
+  std::string nkey1, nkey2;
+  upb.getPlanBuilderPlan()->addCommonValue("dupkey1", "value1", &nkey1);
+  fetch1 = upb.getPlanBuilderPlan()->getPlan()->commonvalue().at(nkey1);
+  EXPECT_STREQ("value1", fetch1.c_str());
+  LOG_INFO("pass 3");
+  upb.getPlanBuilderPlan()->addCommonValue("dupkey1", "value2", &nkey2);
+  fetch1 = upb.getPlanBuilderPlan()->getPlan()->commonvalue().at(nkey2);
+  EXPECT_STREQ("value2", fetch1.c_str());
+  EXPECT_STRNE(nkey1.c_str(), nkey2.c_str());
+  LOG_INFO("pass 4");
+  // passing duplicate key and its value, there should cause nothing updated
+  upb.getPlanBuilderPlan()->addCommonValue("dupkey1", "value1", &nkey1);
+  EXPECT_STREQ("dupkey10", nkey1.c_str());
+  LOG_INFO("pass 5");
+}
+
+/*
+ * SELECT count(int1), text from test2 where int1 > 1 or int2 < 10 and random()
+ * < int1 group by int1, text limit 10 offset 100;
+ */
+TEST(TestUnivPlanCWrapper, TestCompletedPlan) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructLimitTop(-1, 10, 100);
+  int32_t uid2 = upu.constructConnector(uid1, 2, true);
+  int32_t uid3 = upu.constructLimitBelow(uid2, 10, 100);
+  int32_t uid4 = upu.constructAgg(uid3, 3);
+  int32_t uid5 = upu.constructConnector(uid4, 0, true);
+  int32_t uid6 = upu.constructAgg(uid5, 1);
+  int32_t uid7 = upu.constructSeqScan(uid6, true, 0);
+  upu.constructRangeTable();
+  upu.univPlanSetDoInstrument(true);
+  upu.constructReceiver();
+
+  const char *jsonFormatPlan1 = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestCompletedPlanBefore", jsonFormatPlan1);
+  plan.readFromFile("TestCompletedPlanBefore");
+  const char *expecedPlan1 = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan1, jsonFormatPlan1);
+
+  upu.univPlanStagize();
+
+  const char *jsonFormatPlan2 = upu.univPlanGetJsonFormatedPlan();
+  // plan.writeIntoFile("TestCompletedPlanAfter", jsonFormatPlan2);
+  plan.readFromFile("TestCompletedPlanAfter");
+  const char *expecedPlan2 = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan2, jsonFormatPlan2);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/test/unit/test-minmax-cotasklist.cc b/depends/univplan/test/unit/test-minmax-cotasklist.cc
new file mode 100644
index 0000000..424143e
--- /dev/null
+++ b/depends/univplan/test/unit/test-minmax-cotasklist.cc
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+#include "dbcommon/log/exception.h"
+#include "dbcommon/log/logger.h"
+
+#include "univplan/minmax/minmax-predicates.h"
+
+namespace univplan {
+//-----------------------------------------------------------------------------
+// 1 source task covers target task first half
+//-----------------------------------------------------------------------------
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect11) {
+  // task        [[           ]]
+  // src   [[  ]       ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 1024, 128, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(1024, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect12) {
+  // task        [[                ]]
+  // src   [[         ]       ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 1024, 228, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(327, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect13) {
+  // task        [[               ]]
+  // src   [             [  ] ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 1024, 512, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(512, task1.taskBeginRowId);
+  EXPECT_EQ(611, task1.getTaskEndRowId());
+}
+
+//-----------------------------------------------------------------------------
+// 2 source task covers target task completely
+//-----------------------------------------------------------------------------
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect21) {
+  // task        [[           ]]
+  // src   [[  ]                  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 128, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(0, task1.taskRowCount);
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect22) {
+  // task     [[           ]]
+  // src   [ [   ]                 ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 128, 256);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(383, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect23) {
+  // task        [[           ]]
+  // src   [             [  ]           ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 512, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(512, task1.taskBeginRowId);
+  EXPECT_EQ(611, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect24) {
+  // task        [[           ]]
+  // src   [                [    ]  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 1024, 512);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(1024, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect25) {
+  // task        [[           ]]
+  // src   [                     [  ]  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 1536, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(0, task1.taskRowCount);
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect26) {
+  // task        [[           ]]
+  // src   [   [                  ]  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 128, 1536);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+//-----------------------------------------------------------------------------
+// 3 source task covers target task second half
+//-----------------------------------------------------------------------------
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect31) {
+  // task        [[           ]]
+  // src             [   [  ]           ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 512, 2048, 513, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(513, task1.taskBeginRowId);
+  EXPECT_EQ(612, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect32) {
+  // task     [[           ]]
+  // src              [ [       ]    ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 512, 2048, 513, 1024);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(513, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect33) {
+  // task        [[           ]]
+  // src              [             [  ]   ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048, 1536, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(0, task1.taskRowCount);
+}
+
+//-----------------------------------------------------------------------------
+// 4 target task covers source task
+//-----------------------------------------------------------------------------
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect41) {
+  // task        [[             ]]
+  // src            [ [    ]  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 512, 512, 520, 100);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(520, task1.taskBeginRowId);
+  EXPECT_EQ(619, task1.getTaskEndRowId());
+}
+
+//----------------------------------------
+// 5 second source task after intersected
+//----------------------------------------
+
+TEST(TestMinMaxCOBlockTask, BasicIntersect51) {
+  // task        [[             ]]
+  // src            [ [    ] ][[    ]]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 512, 512, 520, 100);
+  COBlockTask tasksrc2(1280, 1024, 512);
+  task1.resetIntersection();
+  task1.intersect(tasksrc);
+  EXPECT_EQ(520, task1.taskBeginRowId);
+  EXPECT_EQ(619, task1.getTaskEndRowId());
+  task1.intersect(tasksrc2);
+  EXPECT_EQ(520, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicUnionAll11) {
+  // task        [[           ]]
+  // src   [[  ]       ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 1024, 128, 100);
+  task1.resetUnionAll();
+  task1.unionAll(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicUnionAll12) {
+  // task        [[           ]]
+  // src      [ [     ]  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 1024, 128, 512);
+  task1.resetUnionAll();
+  task1.unionAll(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicUnionAll13) {
+  // task        [[                 ]]
+  // src               [ [     ]  ]
+  COBlockTask task1(1024, 256, 2048);
+  COBlockTask tasksrc(256, 512, 512);
+  task1.resetUnionAll();
+  task1.unionAll(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(2303, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicUnionAll14) {
+  // task        [[                 ]]
+  // src               [ [               ]  ]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 1024, 512);
+  task1.resetUnionAll();
+  task1.unionAll(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+
+TEST(TestMinMaxCOBlockTask, BasicUnionAll21) {
+  // task        [[       ]]
+  // src     [[               ]]
+  COBlockTask task1(1024, 256, 1024);
+  COBlockTask tasksrc(256, 0, 2048);
+  task1.resetUnionAll();
+  task1.unionAll(tasksrc);
+  EXPECT_EQ(256, task1.taskBeginRowId);
+  EXPECT_EQ(1279, task1.getTaskEndRowId());
+}
+}  // namespace univplan
diff --git a/depends/univplan/test/unit/test-univplan-cwrapper.cc b/depends/univplan/test/unit/test-univplan-cwrapper.cc
new file mode 100644
index 0000000..45a7b81
--- /dev/null
+++ b/depends/univplan/test/unit/test-univplan-cwrapper.cc
@@ -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.
+ */
+
+#include "./test-univplan.h"
+
+#include <errno.h>
+
+#include "gtest/gtest.h"
+
+#include "dbcommon/log/exception.h"
+#include "dbcommon/log/logger.h"
+#include "dbcommon/type/type-kind.h"
+
+#include "univplan/cwrapper/univplan-c.h"
+#include "univplan/testutil/univplan-proto-util.h"
+#include "univplan/univplanbuilder/univplanbuilder.h"
+
+namespace univplan {
+
+TEST(TestUnivPlanCWrapper, TestUnivPlanVarutil) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, false, 0);
+  upu.constructRangeTable();
+  upu.constructReceiver();
+  upu.univPlanFixVarType();
+}
+
+TEST(TestUnivPlanCWrapper, TestStagize) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 1, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, false, 0);
+  upu.constructReceiver();
+
+  const char *jsonFormatPlan1 = upu.univPlanGetJsonFormatedPlan();
+  UnivPlanPlanString plan;
+  // plan.writeIntoFile("TestStagizeBefore", jsonFormatPlan1);
+  plan.readFromFile("TestStagizeBefore");
+  //  puts("--TestStagizeBefore--");
+  //  puts(jsonFormatPlan1);
+  const char *expecedPlan1 = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan1, jsonFormatPlan1);
+
+  upu.univPlanStagize();
+
+  const char *jsonFormatPlan2 = upu.univPlanGetJsonFormatedPlan();
+  // plan.writeIntoFile("TestStagizeAfter", jsonFormatPlan2);
+  plan.readFromFile("TestStagizeAfter");
+  //  puts("--TestStagizeAfter--");
+  //  puts(jsonFormatPlan2);
+  const char *expecedPlan2 = plan.getPlanString();
+  EXPECT_STREQ(expecedPlan2, jsonFormatPlan2);
+}
+
+TEST(TestUnivPlanCWrapper, TestSerialize) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, false, 0);
+  upu.constructRangeTable();
+  upu.constructReceiver();
+  int32_t len;
+  upu.univPlanSerialize(&len);
+  EXPECT_EQ(406, len);
+}
+
+TEST(TestUnivPlanCWrapper, TestSetDoInstrument) {
+  UnivPlanProtoUtility upu;
+  int32_t uid1 = upu.constructConnector(-1, 2, false);
+  int32_t uid2 = upu.constructSeqScan(uid1, false, 0);
+  upu.constructRangeTable();
+  upu.univPlanSetDoInstrument(true);
+}
+
+}  // namespace univplan
diff --git a/depends/univplan/test/unit/test-univplan.h b/depends/univplan/test/unit/test-univplan.h
new file mode 100644
index 0000000..bbc3fe2
--- /dev/null
+++ b/depends/univplan/test/unit/test-univplan.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef UNIVPLAN_TEST_UNIT_TEST_UNIVPLAN_H_
+#define UNIVPLAN_TEST_UNIT_TEST_UNIVPLAN_H_
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace univplan {
+class UnivPlanPlanString {
+ public:
+  UnivPlanPlanString() {}
+  ~UnivPlanPlanString() { planString.clear(); }
+
+  void readFromFile(const char *fileName) {
+    std::ifstream in;
+    std::string filePath(DATA_DIR);
+    filePath.append(fileName);
+    in.open(filePath);
+    ASSERT_TRUE(!!in);
+    std::string str;
+    planString.clear();
+    while (getline(in, str)) {
+      planString.append(str);
+      planString.append("\n");
+    }
+    in.close();
+  }
+
+  // only used for creating univplan ans file
+  void writeIntoFile(const char *fileName, const char *str) {
+    std::ofstream out;
+    std::string filePath(DATA_DIR);
+    filePath.append(fileName);
+    out.open(filePath);
+    out << str;
+    out.close();
+  }
+
+  const char *getPlanString() { return planString.c_str(); }
+
+ private:
+  std::string planString;
+};
+}  // namespace univplan
+#endif  // UNIVPLAN_TEST_UNIT_TEST_UNIVPLAN_H_
diff --git a/depends/univplan/test/unit/unit-test-main.cc b/depends/univplan/test/unit/unit-test-main.cc
new file mode 100644
index 0000000..970802f
--- /dev/null
+++ b/depends/univplan/test/unit/unit-test-main.cc
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "gtest/gtest.h"
+
+#include "dbcommon/log/logger.h"
+
+int main(int argc, char** argv) {
+  ::testing::InitGoogleTest(&argc, argv);
+
+#ifdef TEST_ROOT_DIRECTORY
+  chdir(TEST_ROOT_DIRECTORY);
+#endif
+
+  return RUN_ALL_TESTS();
+}