You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2013/12/30 10:18:02 UTC

[6/6] git commit: TAJO-456: Separate tajo-jdbc and tajo-client from tajo-core-backend. (hyunsik)

TAJO-456: Separate tajo-jdbc and tajo-client from tajo-core-backend. (hyunsik)


Project: http://git-wip-us.apache.org/repos/asf/incubator-tajo/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tajo/commit/b6a5ff0c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tajo/tree/b6a5ff0c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tajo/diff/b6a5ff0c

Branch: refs/heads/master
Commit: b6a5ff0c39e3fd50fc27c2a3804218fc884135f5
Parents: d39bb99
Author: Hyunsik Choi <hy...@apache.org>
Authored: Mon Dec 30 18:17:07 2013 +0900
Committer: Hyunsik Choi <hy...@apache.org>
Committed: Mon Dec 30 18:17:42 2013 +0900

----------------------------------------------------------------------
 CHANGES.txt                                     |    2 +
 pom.xml                                         |    2 +
 tajo-catalog/tajo-catalog-client/pom.xml        |    6 +-
 tajo-catalog/tajo-catalog-common/pom.xml        |    6 +-
 tajo-catalog/tajo-catalog-server/pom.xml        |    1 +
 tajo-client/pom.xml                             |  399 ++++++
 .../main/java/org/apache/tajo/cli/TajoCli.java  |  723 +++++++++++
 .../org/apache/tajo/client/QueryStatus.java     |   86 ++
 .../org/apache/tajo/client/ResultSetUtil.java   |  193 +++
 .../java/org/apache/tajo/client/TajoClient.java |  462 +++++++
 .../java/org/apache/tajo/client/TajoDump.java   |  129 ++
 .../java/org/apache/tajo/jdbc/SQLStates.java    |   33 +
 .../org/apache/tajo/jdbc/TajoResultSet.java     |  152 +++
 .../org/apache/tajo/jdbc/TajoResultSetBase.java | 1129 +++++++++++++++++
 .../apache/tajo/jdbc/TajoResultSetMetaData.java |  161 +++
 tajo-client/src/main/proto/ClientProtos.proto   |  138 ++
 .../main/proto/QueryMasterClientProtocol.proto  |   35 +
 .../main/proto/TajoMasterClientProtocol.proto   |   62 +
 tajo-client/src/main/resources/log4j.properties |   27 +
 tajo-common/pom.xml                             |    8 +-
 .../java/org/apache/tajo/conf/TajoConf.java     |    7 +-
 .../java/org/apache/tajo/storage/Tuple.java     |   80 --
 .../main/java/org/apache/tajo/util/TUtil.java   |    5 +
 tajo-common/src/main/proto/tajo_protos.proto    |   46 +
 tajo-core/pom.xml                               |    4 -
 tajo-core/tajo-core-backend/pom.xml             |   30 +-
 .../main/java/org/apache/tajo/cli/TajoCli.java  |  724 -----------
 .../org/apache/tajo/client/QueryStatus.java     |   86 --
 .../org/apache/tajo/client/ResultSetUtil.java   |   49 -
 .../java/org/apache/tajo/client/SQLStates.java  |   33 -
 .../java/org/apache/tajo/client/TajoClient.java |  461 -------
 .../java/org/apache/tajo/client/TajoDump.java   |  129 --
 .../org/apache/tajo/jdbc/MetaDataTuple.java     |  194 ---
 .../org/apache/tajo/jdbc/TajoConnection.java    |  400 ------
 .../apache/tajo/jdbc/TajoDatabaseMetaData.java  | 1196 ------------------
 .../java/org/apache/tajo/jdbc/TajoDriver.java   |  233 ----
 .../apache/tajo/jdbc/TajoMetaDataResultSet.java |   77 --
 .../apache/tajo/jdbc/TajoPreparedStatement.java |  660 ----------
 .../org/apache/tajo/jdbc/TajoResultSet.java     |  152 ---
 .../org/apache/tajo/jdbc/TajoResultSetBase.java | 1129 -----------------
 .../apache/tajo/jdbc/TajoResultSetMetaData.java |  160 ---
 .../org/apache/tajo/jdbc/TajoStatement.java     |  289 -----
 .../src/main/proto/ClientProtos.proto           |  139 --
 .../main/proto/QueryMasterClientProtocol.proto  |   36 -
 .../main/proto/TajoMasterClientProtocol.proto   |   63 -
 .../src/main/proto/tajo_protos.proto            |   46 -
 tajo-core/tajo-core-pullserver/pom.xml          |    4 +-
 .../java/org/apache/tajo/storage/Tuple.java     |   80 ++
 tajo-core/tajo-core-storage/pom.xml             |   42 +-
 tajo-dist/pom.xml                               |    6 +
 tajo-jdbc/pom.xml                               |  191 +++
 .../org/apache/tajo/jdbc/MetaDataTuple.java     |  192 +++
 .../org/apache/tajo/jdbc/TajoConnection.java    |  398 ++++++
 .../apache/tajo/jdbc/TajoDatabaseMetaData.java  | 1196 ++++++++++++++++++
 .../java/org/apache/tajo/jdbc/TajoDriver.java   |   89 ++
 .../apache/tajo/jdbc/TajoMetaDataResultSet.java |   75 ++
 .../apache/tajo/jdbc/TajoPreparedStatement.java |  658 ++++++++++
 .../org/apache/tajo/jdbc/TajoStatement.java     |  287 +++++
 tajo-jdbc/src/main/resources/log4j.properties   |   27 +
 tajo-project/pom.xml                            |   18 +-
 tajo-rpc/pom.xml                                |   56 +-
 .../org/apache/tajo/rpc/RpcConnectionPool.java  |    4 +-
 62 files changed, 7073 insertions(+), 6432 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index fae9d1d..76b0acc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -110,6 +110,8 @@ Release 0.8.0 - unreleased
 
   IMPROVEMENTS
 
+    TAJO-456: Separate tajo-jdbc and tajo-client from tajo-core-backend. (hyunsik)
+
     TAJO-432: Add shuffle phase for column-partitioned table store. (Min Zhou via jihoon)
 
     TAJO-135: Bump up hadoop to 2.2.0. (jihoon)

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index c485119..5b0effb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,8 @@
     <module>tajo-core</module>
     <module>tajo-rpc</module>
     <module>tajo-catalog</module>
+    <module>tajo-client</module>
+    <module>tajo-jdbc</module>
     <module>tajo-dist</module>
   </modules>
 

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-catalog/tajo-catalog-client/pom.xml
----------------------------------------------------------------------
diff --git a/tajo-catalog/tajo-catalog-client/pom.xml b/tajo-catalog/tajo-catalog-client/pom.xml
index 41e7bbe..b511739 100644
--- a/tajo-catalog/tajo-catalog-client/pom.xml
+++ b/tajo-catalog/tajo-catalog-client/pom.xml
@@ -136,6 +136,7 @@
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>commons-logging</groupId>
@@ -145,11 +146,6 @@
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.derby</groupId>
-      <artifactId>derby</artifactId>
-      <version>10.8.2.2</version>
-    </dependency>
   </dependencies>
 
   <profiles>

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-catalog/tajo-catalog-common/pom.xml
----------------------------------------------------------------------
diff --git a/tajo-catalog/tajo-catalog-common/pom.xml b/tajo-catalog/tajo-catalog-common/pom.xml
index f424110..a4db647 100644
--- a/tajo-catalog/tajo-catalog-common/pom.xml
+++ b/tajo-catalog/tajo-catalog-common/pom.xml
@@ -150,6 +150,7 @@
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>commons-logging</groupId>
@@ -159,11 +160,6 @@
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging-api</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.derby</groupId>
-      <artifactId>derby</artifactId>
-      <version>10.8.2.2</version>
-    </dependency>
   </dependencies>
 
   <profiles>

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-catalog/tajo-catalog-server/pom.xml
----------------------------------------------------------------------
diff --git a/tajo-catalog/tajo-catalog-server/pom.xml b/tajo-catalog/tajo-catalog-server/pom.xml
index ab3139b..03e92ec 100644
--- a/tajo-catalog/tajo-catalog-server/pom.xml
+++ b/tajo-catalog/tajo-catalog-server/pom.xml
@@ -135,6 +135,7 @@
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
+      <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>commons-logging</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/pom.xml
----------------------------------------------------------------------
diff --git a/tajo-client/pom.xml b/tajo-client/pom.xml
new file mode 100644
index 0000000..797ad3a
--- /dev/null
+++ b/tajo-client/pom.xml
@@ -0,0 +1,399 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tajo-project</artifactId>
+    <groupId>org.apache.tajo</groupId>
+    <version>0.8.0-SNAPSHOT</version>
+    <relativePath>../tajo-project</relativePath>
+  </parent>
+  <artifactId>tajo-client</artifactId>
+  <packaging>jar</packaging>
+  <name>Tajo Client</name>
+  <version>0.8.0-SNAPSHOT</version>
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <metrics.version>3.0.1</metrics.version>
+  </properties>
+
+  <repositories>
+    <repository>
+      <id>repository.jboss.org</id>
+      <url>https://repository.jboss.org/nexus/content/repositories/releases/
+      </url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+          <encoding>${project.build.sourceEncoding}</encoding>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.12.4</version>
+        <configuration>
+          <systemProperties>
+            <tajo.test>TRUE</tajo.test>
+          </systemProperties>
+          <argLine>-Xms512m -Xmx1024m -Dfile.encoding=UTF-8</argLine>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.2</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>create-protobuf-generated-sources-directory</id>
+            <phase>initialize</phase>
+            <configuration>
+              <target>
+                <mkdir dir="target/generated-sources/proto" />
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.2</version>
+        <executions>
+          <execution>
+            <id>generate-sources</id>
+            <phase>generate-sources</phase>
+            <configuration>
+              <executable>protoc</executable>
+              <arguments>
+                <argument>-Isrc/main/proto/</argument>
+                <argument>--proto_path=../tajo-common/src/main/proto</argument>
+                <argument>--proto_path=../tajo-catalog/tajo-catalog-common/src/main/proto</argument>
+                <argument>--proto_path=../tajo-core/tajo-core-backend/src/main/proto</argument>
+                <argument>--java_out=target/generated-sources/proto</argument>
+                <argument>src/main/proto/ClientProtos.proto</argument>
+                <argument>src/main/proto/TajoMasterClientProtocol.proto</argument>
+                <argument>src/main/proto/QueryMasterClientProtocol.proto</argument>
+              </arguments>
+            </configuration>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.5</version>
+        <executions>
+          <execution>
+            <id>add-source</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>add-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>target/generated-sources/proto</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-dependencies</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/lib</outputDirectory>
+              <overWriteReleases>false</overWriteReleases>
+              <overWriteSnapshots>false</overWriteSnapshots>
+              <overWriteIfNewer>true</overWriteIfNewer>
+              <excludeGroupIds>org.apache.hadoop</excludeGroupIds>
+              <excludeScope>provided</excludeScope>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>2.7.1</version>
+      </plugin>
+    </plugins>
+  </build>
+
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tajo</groupId>
+      <artifactId>tajo-common</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>com.google.code.gson</groupId>
+          <artifactId>gson</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>joda-time</groupId>
+          <artifactId>joda-time</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>com.sun.jersey.jersey-test-framework</groupId>
+          <artifactId>jersey-test-framework-grizzly2</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tajo</groupId>
+      <artifactId>tajo-catalog-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tajo</groupId>
+      <artifactId>tajo-core-storage</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tajo</groupId>
+      <artifactId>tajo-rpc</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-common</artifactId>
+      <scope>provided</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>commons-el</groupId>
+          <artifactId>commons-el</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>tomcat</groupId>
+          <artifactId>jasper-runtime</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>tomcat</groupId>
+          <artifactId>jasper-compiler</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>joda-time</groupId>
+          <artifactId>joda-time</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.mortbay.jetty</groupId>
+          <artifactId>jsp-2.1-jetty</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>com.sun.jersey.jersey-test-framework</groupId>
+          <artifactId>jersey-test-framework-grizzly2</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-hdfs</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>jline</groupId>
+      <artifactId>jline</artifactId>
+      <version>2.11</version>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>docs</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-javadoc-plugin</artifactId>
+            <executions>
+              <execution>
+                <!-- build javadoc jars per jar for publishing to maven -->
+                <id>module-javadocs</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>jar</goal>
+                </goals>
+                <configuration>
+                  <destDir>${project.build.directory}</destDir>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>src</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-source-plugin</artifactId>
+            <executions>
+              <execution>
+                <!-- builds source jars and attaches them to the project for publishing -->
+                <id>tajo-java-sources</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>jar-no-fork</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+    <profile>
+      <id>dist</id>
+      <activation>
+        <activeByDefault>false</activeByDefault>
+        <property>
+          <name>tar|rpm|deb</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-antrun-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>dist</id>
+                <phase>package</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+                <configuration>
+                  <target>
+                    <echo file="${project.build.directory}/dist-layout-stitching.sh">
+                      run() {
+                      echo "\$ ${@}"
+                      "${@}"
+                      res=$?
+                      if [ $res != 0 ]; then
+                      echo
+                      echo "Failed!"
+                      echo
+                      exit $res
+                      fi
+                      }
+
+                      ROOT=`cd ${basedir}/..;pwd`
+                      echo
+                      echo "Current directory `pwd`"
+                      echo
+                      run rm -rf ${project.artifactId}-${project.version}
+                      run mkdir ${project.artifactId}-${project.version}
+                      run cd ${project.artifactId}-${project.version}
+                      run cp -r ${basedir}/target/${project.artifactId}-${project.version}*.jar .
+                      echo
+                      echo "Tajo Client dist layout available at: ${project.build.directory}/${project.artifactId}-${project.version}"
+                      echo
+                    </echo>
+                    <exec executable="sh" dir="${project.build.directory}" failonerror="true">
+                      <arg line="./dist-layout-stitching.sh"/>
+                    </exec>
+                  </target>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>2.4</version>
+        <configuration>
+          <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+        <version>2.15</version>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
+

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/cli/TajoCli.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/cli/TajoCli.java b/tajo-client/src/main/java/org/apache/tajo/cli/TajoCli.java
new file mode 100644
index 0000000..2e7a92c
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/cli/TajoCli.java
@@ -0,0 +1,723 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.cli;
+
+import jline.console.ConsoleReader;
+import jline.console.history.FileHistory;
+import jline.console.history.PersistentHistory;
+import org.apache.commons.cli.*;
+import org.apache.commons.lang.StringUtils;
+import org.apache.tajo.QueryId;
+import org.apache.tajo.QueryIdFactory;
+import org.apache.tajo.TajoConstants;
+import org.apache.tajo.TajoProtos.QueryState;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.catalog.partition.Specifier;
+import org.apache.tajo.catalog.statistics.TableStats;
+import org.apache.tajo.client.QueryStatus;
+import org.apache.tajo.client.TajoClient;
+import org.apache.tajo.conf.TajoConf;
+import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.ipc.ClientProtos;
+import org.apache.tajo.jdbc.TajoResultSet;
+import org.apache.tajo.util.FileUtil;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class TajoCli {
+  private final TajoConf conf;
+  private static final Options options;
+
+  private TajoClient client;
+
+  private final ConsoleReader reader;
+  private final InputStream sin;
+  private final PrintWriter sout;
+
+  private static final int PRINT_LIMIT = 24;
+  private final Map<String, Command> commands = new TreeMap<String, Command>();
+
+  private static final Class [] registeredCommands = {
+      DescTableCommand.class,
+      HelpCommand.class,
+      ExitCommand.class,
+      Copyright.class,
+      Version.class
+  };
+
+  private static final String HOME_DIR = System.getProperty("user.home");
+  private static final String HISTORY_FILE = ".tajo_history";
+
+  static {
+    options = new Options();
+    options.addOption("c", "command", true, "execute only single command, then exit");
+    options.addOption("f", "file", true, "execute commands from file, then exit");
+    options.addOption("h", "host", true, "Tajo server host");
+    options.addOption("p", "port", true, "Tajo server port");
+  }
+
+  public TajoCli(TajoConf c, String [] args,
+                 InputStream in, OutputStream out) throws Exception {
+    this.conf = new TajoConf(c);
+    this.sin = in;
+    this.reader = new ConsoleReader(sin, out);
+    this.sout = new PrintWriter(reader.getOutput());
+
+    CommandLineParser parser = new PosixParser();
+    CommandLine cmd = parser.parse(options, args);
+
+    String hostName = null;
+    Integer port = null;
+    if (cmd.hasOption("h")) {
+      hostName = cmd.getOptionValue("h");
+    }
+    if (cmd.hasOption("p")) {
+      port = Integer.parseInt(cmd.getOptionValue("p"));
+    }
+
+    // if there is no "-h" option,
+    if(hostName == null) {
+      if (conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS) != null) {
+        // it checks if the client service address is given in configuration and distributed mode.
+        // if so, it sets entryAddr.
+        hostName = conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS).split(":")[0];
+      }
+    }
+    if (port == null) {
+      if (conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS) != null) {
+        // it checks if the client service address is given in configuration and distributed mode.
+        // if so, it sets entryAddr.
+        port = Integer.parseInt(conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS).split(":")[1]);
+      }
+    }
+
+    if ((hostName == null) ^ (port == null)) {
+      System.err.println("ERROR: cannot find valid Tajo server address");
+      System.exit(-1);
+    } else if (hostName != null && port != null) {
+      conf.setVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS, hostName+":"+port);
+      client = new TajoClient(conf);
+    } else if (hostName == null && port == null) {
+      client = new TajoClient(conf);
+    }
+
+    initHistory();
+    initCommands();
+
+    if (cmd.hasOption("c")) {
+      executeStatements(cmd.getOptionValue("c"));
+      sout.flush();
+      System.exit(0);
+    }
+    if (cmd.hasOption("f")) {
+      File sqlFile = new File(cmd.getOptionValue("f"));
+      if (sqlFile.exists()) {
+        String contents = FileUtil.readTextFile(new File(cmd.getOptionValue("f")));
+        executeStatements(contents);
+        sout.flush();
+        System.exit(0);
+      } else {
+        System.err.println("No such a file \"" + cmd.getOptionValue("f") + "\"");
+        System.exit(-1);
+      }
+    }
+  }
+
+  private void initHistory() {
+    try {
+      String historyPath = HOME_DIR + File.separator + HISTORY_FILE;
+      if ((new File(HOME_DIR)).exists()) {
+        reader.setHistory(new FileHistory(new File(historyPath)));
+      } else {
+        System.err.println("ERROR: home directory : '" + HOME_DIR +"' does not exist.");
+      }
+    } catch (Exception e) {
+      System.err.println(e.getMessage());
+    }
+  }
+
+  private void initCommands() {
+    for (Class clazz : registeredCommands) {
+      Command cmd = null;
+      try {
+         Constructor cons = clazz.getConstructor(new Class[] {TajoCli.class});
+         cmd = (Command) cons.newInstance(this);
+      } catch (Exception e) {
+        System.err.println(e.getMessage());
+        System.exit(-1);
+      }
+      commands.put(cmd.getCommand(), cmd);
+    }
+  }
+
+  public int runShell() throws Exception {
+
+    String raw;
+    String line;
+    String accumulatedLine = "";
+    String prompt = "tajo";
+    String curPrompt = prompt;
+    boolean newStatement = true;
+    int code = 0;
+
+    sout.write("Try \\? for help.\n");
+    while((raw = reader.readLine(curPrompt + "> ")) != null) {
+      // each accumulated line has a space delimiter
+      if (!accumulatedLine.equals("")) {
+        accumulatedLine += ' ';
+      }
+
+      line = raw.trim();
+
+      if (line.length() == 0) { // if empty line
+        continue;
+
+      } else if (line.charAt(0) == '/') { // warning for legacy usage
+        printInvalidCommand(line);
+        continue;
+
+      } else if (line.charAt(0) == '\\') { // command mode
+        executeCommand(line);
+        ((PersistentHistory)reader.getHistory()).flush();
+
+      } else if (line.endsWith(";") && !line.endsWith("\\;")) {
+
+        // remove a trailing newline
+        line = StringUtils.chomp(line).trim();
+
+        // get a punctuated statement
+        String punctuated = accumulatedLine + line;
+
+        if (!newStatement) {
+          // why do two lines are removed?
+          // First history line indicates an accumulated line.
+          // Second history line is a just typed line.
+          reader.getHistory().removeLast();
+          reader.getHistory().removeLast();
+          reader.getHistory().add(punctuated);
+          ((PersistentHistory)reader.getHistory()).flush();
+        }
+
+        code = executeStatements(punctuated);
+
+        // reset accumulated lines
+        newStatement = true;
+        accumulatedLine = "";
+        curPrompt = prompt;
+
+      } else {
+        line = StringUtils.chomp(raw).trim();
+
+        // accumulate a line
+        accumulatedLine = accumulatedLine + line;
+
+        // replace the latest line with a accumulated line
+        if (!newStatement) { // if this is not first line, remove one more line.
+          reader.getHistory().removeLast();
+        } else {
+          newStatement = false;
+        }
+        reader.getHistory().removeLast();
+        reader.getHistory().add(accumulatedLine);
+
+        // use an alternative prompt during accumulating lines
+        curPrompt = StringUtils.repeat(" ", prompt.length());
+        continue;
+      }
+    }
+    return code;
+  }
+
+  private void invokeCommand(String [] cmds) {
+    // this command should be moved to GlobalEngine
+    Command invoked;
+    try {
+      invoked = commands.get(cmds[0]);
+      invoked.invoke(cmds);
+    } catch (Throwable t) {
+      sout.println(t.getMessage());
+    }
+  }
+
+  public int executeStatements(String line) throws Exception {
+
+    // TODO - comment handling and multi line queries should be improved
+    // remove comments
+    String filtered = line.replaceAll("--[^\\r\\n]*", "").trim();
+
+    String stripped;
+    for (String statement : filtered.split(";")) {
+      stripped = StringUtils.chomp(statement);
+      if (StringUtils.isBlank(stripped)) {
+        continue;
+      }
+
+      String [] cmds = stripped.split(" ");
+      if (cmds[0].equalsIgnoreCase("exit") || cmds[0].equalsIgnoreCase("quit")) {
+        sout.println("\n\nbye!");
+        sout.flush();
+        ((PersistentHistory)this.reader.getHistory()).flush();
+        System.exit(0);
+      } else if (cmds[0].equalsIgnoreCase("detach") && cmds.length > 1 && cmds[1].equalsIgnoreCase("table")) {
+        // this command should be moved to GlobalEngine
+        invokeCommand(cmds);
+
+      } else { // submit a query to TajoMaster
+        ClientProtos.GetQueryStatusResponse response = client.executeQuery(stripped);
+        if (response == null) {
+          sout.println("response is null");
+        }
+        else if (response.getResultCode() == ClientProtos.ResultCode.OK) {
+          QueryId queryId = null;
+          try {
+            queryId = new QueryId(response.getQueryId());
+            if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+              sout.println("OK");
+            } else {
+              getQueryResult(queryId);
+            }
+          } finally {
+            if(queryId != null) {
+              client.closeQuery(queryId);
+            }
+          }
+        } else {
+          if (response.hasErrorMessage()) {
+            sout.println(response.getErrorMessage());
+          }
+        }
+      }
+    }
+    return 0;
+  }
+
+  private boolean isFailed(QueryState state) {
+    return state == QueryState.QUERY_ERROR || state == QueryState.QUERY_FAILED;
+  }
+
+  private void getQueryResult(QueryId queryId) {
+    // if query is empty string
+    if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+      return;
+    }
+
+    // query execute
+    try {
+
+      QueryStatus status;
+      while (true) {
+        // TODO - configurable
+        Thread.sleep(1000);
+        status = client.getQueryStatus(queryId);
+        if(status.getState() == QueryState.QUERY_MASTER_INIT || status.getState() == QueryState.QUERY_MASTER_LAUNCHED) {
+          continue;
+        }
+
+        if (status.getState() == QueryState.QUERY_RUNNING ||
+            status.getState() == QueryState.QUERY_SUCCEEDED) {
+          sout.println("Progress: " + (int)(status.getProgress() * 100.0f)
+              + "%, response time: " + ((float)(status.getFinishTime() - status.getSubmitTime()) / 1000.0) + " sec");
+          sout.flush();
+        }
+
+        if (status.getState() != QueryState.QUERY_RUNNING && status.getState() != QueryState.QUERY_NOT_ASSIGNED) {
+          break;
+        }
+      }
+
+      if (status.getState() == QueryState.QUERY_ERROR) {
+        sout.println("Internal error!");
+      } else if (status.getState() == QueryState.QUERY_FAILED) {
+        sout.println("Query failed!");
+      } else if (status.getState() == QueryState.QUERY_KILLED) {
+        sout.println(queryId + " is killed.");
+      } else {
+        if (status.getState() == QueryState.QUERY_SUCCEEDED) {
+          sout.println("final state: " + status.getState()
+              + ", response time: " + (((float)(status.getFinishTime() - status.getSubmitTime()) / 1000.0)
+              + " sec"));
+          if (status.hasResult()) {
+            ResultSet res = null;
+            TableDesc desc = null;
+            if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+              res = client.createNullResultSet(queryId);
+            } else {
+              ClientProtos.GetQueryResultResponse response = client.getResultResponse(queryId);
+              desc = CatalogUtil.newTableDesc(response.getTableDesc());
+              conf.setVar(ConfVars.USERNAME, response.getTajoUserName());
+              res = new TajoResultSet(client, queryId, conf, desc);
+            }
+            try {
+              if (res == null) {
+                sout.println("OK");
+                return;
+              }
+
+              ResultSetMetaData rsmd = res.getMetaData();
+
+              TableStats stat = desc.getStats();
+              String volume = FileUtil.humanReadableByteCount(stat.getNumBytes(), false);
+              long resultRows = stat.getNumRows();
+              sout.println("result: " + desc.getPath() + ", " + resultRows + " rows (" + volume + ")");
+
+              int numOfColumns = rsmd.getColumnCount();
+              for (int i = 1; i <= numOfColumns; i++) {
+                if (i > 1) sout.print(",  ");
+                String columnName = rsmd.getColumnName(i);
+                sout.print(columnName);
+              }
+              sout.println("\n-------------------------------");
+
+              int numOfPrintedRows = 0;
+              while (res.next()) {
+                // TODO - to be improved to print more formatted text
+                for (int i = 1; i <= numOfColumns; i++) {
+                  if (i > 1) sout.print(",  ");
+                  String columnValue = res.getObject(i).toString();
+                  if(res.wasNull()){
+                    sout.print("null");
+                  } else {
+                    sout.print(columnValue);
+                  }
+                }
+                sout.println();
+                sout.flush();
+                numOfPrintedRows++;
+                if (numOfPrintedRows >= PRINT_LIMIT) {
+                  sout.print("continue... ('q' is quit)");
+                  sout.flush();
+                  if (sin.read() == 'q') {
+                    break;
+                  }
+                  numOfPrintedRows = 0;
+                  sout.println();
+                }
+              }
+            } finally {
+              if(res != null) {
+                res.close();
+              }
+            }
+          } else {
+            sout.println("OK");
+          }
+        }
+      }
+    } catch (Throwable t) {
+      t.printStackTrace();
+      System.err.println(t.getMessage());
+    }
+  }
+
+  private void printUsage() {
+    HelpFormatter formatter = new HelpFormatter();
+    formatter.printHelp( "tajo cli [options]", options );
+  }
+
+  public static abstract class Command {
+    public abstract String getCommand();
+    public abstract void invoke(String [] command) throws Exception;
+    public abstract String getUsage();
+    public abstract String getDescription();
+  }
+
+  private String toFormattedString(TableDesc desc) {
+    StringBuilder sb = new StringBuilder();
+    sb.append("\ntable name: ").append(desc.getName()).append("\n");
+    sb.append("table path: ").append(desc.getPath()).append("\n");
+    sb.append("store type: ").append(desc.getMeta().getStoreType()).append("\n");
+    if (desc.getStats() != null) {
+      sb.append("number of rows: ").append(desc.getStats().getNumRows()).append("\n");
+      sb.append("volume: ").append(
+          FileUtil.humanReadableByteCount(desc.getStats().getNumBytes(),
+              true)).append("\n");
+    }
+    sb.append("Options: \n");
+    for(Map.Entry<String, String> entry : desc.getMeta().toMap().entrySet()){
+      sb.append("\t").append("'").append(entry.getKey()).append("'").append("=")
+          .append("'").append(entry.getValue()).append("'").append("\n");
+    }
+    sb.append("\n");
+    sb.append("schema: \n");
+
+    for(int i = 0; i < desc.getSchema().getColumnNum(); i++) {
+      Column col = desc.getSchema().getColumn(i);
+      sb.append(col.getColumnName()).append("\t").append(col.getDataType().getType());
+      if (col.getDataType().hasLength()) {
+        sb.append("(").append(col.getDataType().getLength()).append(")");
+      }
+      sb.append("\n");
+    }
+
+    sb.append("\n");
+    sb.append("Partitions: \n");
+    if (desc.getPartitions() != null) {
+      sb.append("type:").append(desc.getPartitions().getPartitionsType().name()).append("\n");
+      if (desc.getPartitions().getNumPartitions() > 0)
+        sb.append("numbers:").append(desc.getPartitions().getNumPartitions()).append("\n");
+
+      sb.append("columns:").append("\n");
+      for(Column eachColumn: desc.getPartitions().getColumns()) {
+        sb.append("  ");
+        sb.append(eachColumn.getColumnName()).append("\t").append(eachColumn.getDataType().getType());
+        if (eachColumn.getDataType().hasLength()) {
+          sb.append("(").append(eachColumn.getDataType().getLength()).append(")");
+        }
+        sb.append("\n");
+      }
+
+      if (desc.getPartitions().getSpecifiers() != null) {
+        sb.append("specifier:").append("\n");
+        for(Specifier specifier :desc.getPartitions().getSpecifiers()) {
+          sb.append("  ");
+          sb.append("name:").append(specifier.getName());
+          if (!specifier.getExpressions().equals("")) {
+            sb.append(", expressions:").append(specifier.getExpressions());
+          } else {
+            if (desc.getPartitions().getPartitionsType().name().equals("RANGE"));
+              sb.append(" expressions: MAXVALUE");
+          }
+          sb.append("\n");
+        }
+      }
+    }
+
+    return sb.toString();
+  }
+
+  public class DescTableCommand extends Command {
+    public DescTableCommand() {}
+
+    @Override
+    public String getCommand() {
+      return "\\d";
+    }
+
+    @Override
+    public void invoke(String[] cmd) throws Exception {
+      if (cmd.length == 2) {
+        TableDesc desc = client.getTableDesc(cmd[1]);
+        if (desc == null) {
+          sout.println("Did not find any relation named \"" + cmd[1] + "\"");
+        } else {
+          sout.println(toFormattedString(desc));
+        }
+      } else if (cmd.length == 1) {
+        List<String> tableList = client.getTableList();
+        if (tableList.size() == 0) {
+          sout.println("No Relation Found");
+        }
+        for (String table : tableList) {
+          sout.println(table);
+        }
+      } else {
+        throw new IllegalArgumentException();
+      }
+    }
+
+    @Override
+    public String getUsage() {
+      return "[table_name]";
+    }
+
+    @Override
+    public String getDescription() {
+      return "show table description";
+    }
+  }
+
+  public class HelpCommand extends Command {
+
+    @Override
+    public String getCommand() {
+      return "\\?";
+    }
+
+    @Override
+    public void invoke(String[] cmd) throws Exception {
+      sout.println();
+
+      sout.println("General");
+      sout.println("  \\copyright  show Apache License 2.0");
+      sout.println("  \\version    show Tajo version");
+      sout.println("  \\?          show help");
+      sout.println("  \\q          quit tsql");
+      sout.println();
+      sout.println();
+
+      sout.println("Informational");
+      sout.println("  \\d         list tables");
+      sout.println("  \\d  NAME   describe table");
+      sout.println();
+      sout.println();
+
+      sout.println("Documentations");
+      sout.println("  tsql guide        http://wiki.apache.org/tajo/tsql");
+      sout.println("  Query language    http://wiki.apache.org/tajo/QueryLanguage");
+      sout.println("  Functions         http://wiki.apache.org/tajo/Functions");
+      sout.println("  Backup & restore  http://wiki.apache.org/tajo/BackupAndRestore");
+      sout.println("  Configuration     http://wiki.apache.org/tajo/Configuration");
+      sout.println();
+    }
+
+    @Override
+    public String getUsage() {
+      return "";
+    }
+
+    @Override
+    public String getDescription() {
+      return "show command lists and their usages";
+    }
+  }
+
+  public class Version extends Command {
+
+    @Override
+    public String getCommand() {
+      return "\\version";
+    }
+
+    @Override
+    public void invoke(String[] cmd) throws Exception {
+      sout.println(TajoConstants.TAJO_VERSION);
+    }
+
+    @Override
+    public String getUsage() {
+      return "";
+    }
+
+    @Override
+    public String getDescription() {
+      return "show Apache License 2.0";
+    }
+  }
+
+  public class Copyright extends Command {
+
+    @Override
+    public String getCommand() {
+      return "\\copyright";
+    }
+
+    @Override
+    public void invoke(String[] cmd) throws Exception {
+      sout.println();
+      sout.println(
+      "  Licensed to the Apache Software Foundation (ASF) under one\n" +
+      "  or more contributor license agreements.  See the NOTICE file\n" +
+      "  distributed with this work for additional information\n" +
+      "  regarding copyright ownership.  The ASF licenses this file\n" +
+      "  to you under the Apache License, Version 2.0 (the\n" +
+      "  \"License\"); you may not use this file except in compliance\n" +
+      "  with the License.  You may obtain a copy of the License at\n" +
+      "\n" +
+      "       http://www.apache.org/licenses/LICENSE-2.0\n" +
+      "\n" +
+      "   Unless required by applicable law or agreed to in writing, software\n" +
+      "   distributed under the License is distributed on an \"AS IS\" BASIS,\n" +
+      "   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +
+      "   See the License for the specific language governing permissions and\n" +
+      "   limitations under the License.");
+      sout.println();
+    }
+
+    @Override
+    public String getUsage() {
+      return "";
+    }
+
+    @Override
+    public String getDescription() {
+      return "show Apache License 2.0";
+    }
+  }
+
+  public class ExitCommand extends Command {
+
+    @Override
+    public String getCommand() {
+      return "\\q";
+    }
+
+    @Override
+    public void invoke(String[] cmd) throws Exception {
+      sout.println("bye!");
+      System.exit(0);
+    }
+
+    @Override
+    public String getUsage() {
+      return "";
+    }
+
+    @Override
+    public String getDescription() {
+      return "quit";
+    }
+  }
+
+  public int executeCommand(String line) throws Exception {
+    String [] metaCommands = line.split(";");
+    for (String metaCommand : metaCommands) {
+      String arguments [];
+      arguments = metaCommand.split(" ");
+
+      Command invoked = commands.get(arguments[0]);
+      if (invoked == null) {
+        printInvalidCommand(arguments[0]);
+        return -1;
+      }
+
+      try {
+        invoked.invoke(arguments);
+      } catch (IllegalArgumentException ige) {
+        sout.println(ige.getMessage());
+      } catch (Exception e) {
+        sout.println(e.getMessage());
+      }
+    }
+
+    return 0;
+  }
+
+  private void printInvalidCommand(String command) {
+    sout.println("Invalid command " + command +". Try \\? for help.");
+  }
+
+  public static void main(String [] args) throws Exception {
+    TajoConf conf = new TajoConf();
+    TajoCli shell = new TajoCli(conf, args, System.in, System.out);
+    System.out.println();
+    int status = shell.runShell();
+    System.exit(status);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/client/QueryStatus.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/client/QueryStatus.java b/tajo-client/src/main/java/org/apache/tajo/client/QueryStatus.java
new file mode 100644
index 0000000..203f9aa
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/client/QueryStatus.java
@@ -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.
+ */
+
+package org.apache.tajo.client;
+
+import org.apache.tajo.QueryId;
+import org.apache.tajo.TajoProtos.QueryState;
+import org.apache.tajo.ipc.ClientProtos.GetQueryStatusResponse;
+
+public class QueryStatus {
+  private QueryId queryId;
+  private QueryState state;
+  private float progress;
+  private long submitTime;
+  private long finishTime;
+  private boolean hasResult;
+  private String errorText;
+  private String queryMasterHost;
+  private int queryMasterPort;
+
+  public QueryStatus(GetQueryStatusResponse proto) {
+    queryId = new QueryId(proto.getQueryId());
+    state = proto.getState();
+    progress = proto.getProgress();
+    submitTime = proto.getSubmitTime();
+    finishTime = proto.getFinishTime();
+    hasResult = proto.getHasResult();
+    if (proto.hasErrorMessage()) {
+      errorText = proto.getErrorMessage();
+    }
+
+    queryMasterHost = proto.getQueryMasterHost();
+    queryMasterPort = proto.getQueryMasterPort();
+  }
+
+  public String getQueryMasterHost() {
+    return queryMasterHost;
+  }
+
+  public int getQueryMasterPort() {
+    return queryMasterPort;
+  }
+
+  public QueryId getQueryId() {
+    return this.queryId;
+  }
+
+  public QueryState getState() {
+    return this.state;
+  }
+
+  public float getProgress() {
+    return progress;
+  }
+
+  public long getSubmitTime() {
+    return this.submitTime;
+  }
+
+  public long getFinishTime() {
+    return this.finishTime;
+  }
+
+  public boolean hasResult() {
+    return this.hasResult;
+  }
+
+  public String getErrorMessage() {
+    return errorText;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/client/ResultSetUtil.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/client/ResultSetUtil.java b/tajo-client/src/main/java/org/apache/tajo/client/ResultSetUtil.java
new file mode 100644
index 0000000..1573978
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/client/ResultSetUtil.java
@@ -0,0 +1,193 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.client;
+
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.exception.UnsupportedException;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+
+public class ResultSetUtil {
+  public static String prettyFormat(ResultSet res) throws SQLException {
+    StringBuilder sb = new StringBuilder();
+    ResultSetMetaData rsmd = res.getMetaData();
+    int numOfColumns = rsmd.getColumnCount();
+
+    for (int i = 1; i <= numOfColumns; i++) {
+      if (i > 1) sb.append(",  ");
+      String columnName = rsmd.getColumnName(i);
+      sb.append(columnName);
+    }
+    sb.append("\n-------------------------------\n");
+
+    while (res.next()) {
+      for (int i = 1; i <= numOfColumns; i++) {
+        if (i > 1) sb.append(",  ");
+        String columnValue = res.getObject(i).toString();
+        sb.append(columnValue);
+      }
+      sb.append("\n");
+    }
+
+    return sb.toString();
+  }
+
+  public static String toSqlType(TajoDataTypes.DataType type) {
+    switch (type.getType()) {
+    case BOOLEAN:
+      return "boolean";
+    case INT1:
+      return "tinyint";
+    case INT2:
+      return "smallint";
+    case INT4:
+      return "integer";
+    case INT8:
+      return "bigint";
+    case FLOAT4:
+      return "float";
+    case FLOAT8:
+      return "float8";
+    case DECIMAL:
+      return "numeric";
+    case VARBINARY:
+      return "bytea";
+    case CHAR:
+      return "character";
+    case DATE:
+      return "date";
+    case VARCHAR:
+      return "varchar";
+    case TEXT:
+      return "varchar";
+    default:
+      throw new UnsupportedException("Unrecognized column type:" + type);
+    }
+  }
+
+  public static int tajoTypeToSqlType(TajoDataTypes.DataType type) throws SQLException {
+    switch (type.getType()) {
+    case BOOLEAN:
+      return Types.BOOLEAN;
+    case INT1:
+      return Types.TINYINT;
+    case INT2:
+      return Types.SMALLINT;
+    case INT4:
+      return Types.INTEGER;
+    case INT8:
+      return Types.BIGINT;
+    case FLOAT4:
+      return Types.FLOAT;
+    case FLOAT8:
+      return Types.DOUBLE;
+    case DECIMAL:
+      return Types.DECIMAL;
+    case DATE:
+      return Types.TIMESTAMP;
+    case VARCHAR:
+      return Types.VARCHAR;
+    case TEXT:
+      return Types.VARCHAR;
+    default:
+      throw new SQLException("Unrecognized column type: " + type);
+    }
+  }
+
+  public static int columnDisplaySize(int columnType) throws SQLException {
+    // according to hiveTypeToSqlType possible options are:
+    switch(columnType) {
+    case Types.BOOLEAN:
+      return columnPrecision(columnType);
+    case Types.VARCHAR:
+      return Integer.MAX_VALUE; // hive has no max limit for strings
+    case Types.TINYINT:
+    case Types.SMALLINT:
+    case Types.INTEGER:
+    case Types.BIGINT:
+      return columnPrecision(columnType) + 1; // allow +/-
+    case Types.TIMESTAMP:
+      return columnPrecision(columnType);
+    // see http://download.oracle.com/javase/6/docs/api/constant-values.html#java.lang.Float.MAX_EXPONENT
+    case Types.FLOAT:
+      return 24; // e.g. -(17#).e-###
+    // see http://download.oracle.com/javase/6/docs/api/constant-values.html#java.lang.Double.MAX_EXPONENT
+    case Types.DOUBLE:
+      return 25; // e.g. -(17#).e-####
+    case Types.DECIMAL:
+      return Integer.MAX_VALUE;
+    default:
+      throw new SQLException("Invalid column type: " + columnType);
+    }
+  }
+
+  public static int columnPrecision(int columnType) throws SQLException {
+    // according to hiveTypeToSqlType possible options are:
+    switch(columnType) {
+    case Types.BOOLEAN:
+      return 1;
+    case Types.VARCHAR:
+      return Integer.MAX_VALUE; // hive has no max limit for strings
+    case Types.TINYINT:
+      return 3;
+    case Types.SMALLINT:
+      return 5;
+    case Types.INTEGER:
+      return 10;
+    case Types.BIGINT:
+      return 19;
+    case Types.FLOAT:
+      return 7;
+    case Types.DOUBLE:
+      return 15;
+    case Types.TIMESTAMP:
+      return 29;
+    case Types.DECIMAL:
+      return Integer.MAX_VALUE;
+    default:
+      throw new SQLException("Invalid column type: " + columnType);
+    }
+  }
+
+  public static int columnScale(int columnType) throws SQLException {
+    // according to hiveTypeToSqlType possible options are:
+    switch(columnType) {
+    case Types.BOOLEAN:
+    case Types.VARCHAR:
+    case Types.TINYINT:
+    case Types.SMALLINT:
+    case Types.INTEGER:
+    case Types.BIGINT:
+      return 0;
+    case Types.FLOAT:
+      return 7;
+    case Types.DOUBLE:
+      return 15;
+    case Types.TIMESTAMP:
+      return 9;
+    case Types.DECIMAL:
+      return Integer.MAX_VALUE;
+    default:
+      throw new SQLException("Invalid column type: " + columnType);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/client/TajoClient.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/client/TajoClient.java b/tajo-client/src/main/java/org/apache/tajo/client/TajoClient.java
new file mode 100644
index 0000000..05a5eff
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/client/TajoClient.java
@@ -0,0 +1,462 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.client;
+
+import com.google.protobuf.ServiceException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.Path;
+import org.apache.tajo.QueryId;
+import org.apache.tajo.QueryIdFactory;
+import org.apache.tajo.TajoProtos.QueryState;
+import org.apache.tajo.annotation.ThreadSafe;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.catalog.TableMeta;
+import org.apache.tajo.conf.TajoConf;
+import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.ipc.ClientProtos.*;
+import org.apache.tajo.ipc.QueryMasterClientProtocol;
+import org.apache.tajo.ipc.QueryMasterClientProtocol.QueryMasterClientProtocolService;
+import org.apache.tajo.ipc.TajoMasterClientProtocol;
+import org.apache.tajo.ipc.TajoMasterClientProtocol.TajoMasterClientProtocolService;
+import org.apache.tajo.jdbc.SQLStates;
+import org.apache.tajo.jdbc.TajoResultSet;
+import org.apache.tajo.rpc.*;
+import org.apache.tajo.rpc.protocolrecords.PrimitiveProtos.StringProto;
+import org.apache.tajo.util.NetUtils;
+import org.apache.tajo.rpc.ServerCallable;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@ThreadSafe
+public class TajoClient {
+  private final Log LOG = LogFactory.getLog(TajoClient.class);
+
+  private final TajoConf conf;
+
+  private Map<QueryId, InetSocketAddress> queryMasterMap = new ConcurrentHashMap<QueryId, InetSocketAddress>();
+
+  private InetSocketAddress tajoMasterAddr;
+
+  private RpcConnectionPool connPool;
+
+  public TajoClient(TajoConf conf) throws IOException {
+    this(conf, NetUtils.createSocketAddr(conf.getVar(ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS)));
+  }
+
+  public TajoClient(TajoConf conf, InetSocketAddress addr) throws IOException {
+    this.conf = conf;
+    this.conf.set("tajo.disk.scheduler.report.interval", "0");
+    this.tajoMasterAddr = addr;
+
+    connPool = RpcConnectionPool.getPool(conf);
+  }
+
+  public TajoClient(InetSocketAddress addr) throws IOException {
+    this(new TajoConf(), addr);
+  }
+
+  public TajoClient(String hostname, int port) throws IOException {
+    this(new TajoConf(), NetUtils.createSocketAddr(hostname, port));
+  }
+
+  public void close() {
+    if(connPool != null) {
+      connPool.close();
+    }
+    queryMasterMap.clear();
+  }
+
+  public TajoConf getConf() {
+    return conf;
+  }
+
+  /**
+   * Call to QueryMaster closing query resources
+   * @param queryId
+   */
+  public void closeQuery(final QueryId queryId) {
+    if(queryMasterMap.containsKey(queryId)) {
+      NettyClientBase qmClient = null;
+      try {
+        qmClient = connPool.getConnection(queryMasterMap.get(queryId), QueryMasterClientProtocol.class, false);
+        QueryMasterClientProtocolService.BlockingInterface queryMasterService = qmClient.getStub();
+        queryMasterService.killQuery(null, queryId.getProto());
+      } catch (Exception e) {
+        LOG.warn("Fail to close a QueryMaster connection (qid=" + queryId + ", msg=" + e.getMessage() + ")", e);
+      } finally {
+        connPool.closeConnection(qmClient);
+        queryMasterMap.remove(queryId);
+      }
+    }
+  }
+
+  /**
+   * It submits a query statement and get a response immediately.
+   * The response only contains a query id, and submission status.
+   * In order to get the result, you should use {@link #getQueryResult(org.apache.tajo.QueryId)}
+   * or {@link #getQueryResultAndWait(org.apache.tajo.QueryId)}.
+   */
+  public GetQueryStatusResponse executeQuery(final String sql) throws ServiceException {
+    return new ServerCallable<GetQueryStatusResponse>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public GetQueryStatusResponse call(NettyClientBase client) throws ServiceException {
+        final QueryRequest.Builder builder = QueryRequest.newBuilder();
+        builder.setQuery(sql);
+
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+        return tajoMasterService.submitQuery(null, builder.build());
+      }
+    }.withRetries();
+  }
+
+  /**
+   * It submits a query statement and get a response.
+   * The main difference from {@link #executeQuery(String)}
+   * is a blocking method. So, this method is wait for
+   * the finish of the submitted query.
+   *
+   * @return If failed, return null.
+   */
+  public ResultSet executeQueryAndGetResult(final String sql)
+      throws ServiceException, IOException {
+    GetQueryStatusResponse response = new ServerCallable<GetQueryStatusResponse>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public GetQueryStatusResponse call(NettyClientBase client) throws ServiceException {
+        final QueryRequest.Builder builder = QueryRequest.newBuilder();
+        builder.setQuery(sql);
+
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+        return tajoMasterService.submitQuery(null, builder.build());
+      }
+    }.withRetries();
+
+    QueryId queryId = new QueryId(response.getQueryId());
+    if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+      return this.createNullResultSet(queryId);
+    } else {
+      return this.getQueryResultAndWait(queryId);
+    }
+  }
+
+  public QueryStatus getQueryStatus(QueryId queryId) throws ServiceException {
+    GetQueryStatusRequest.Builder builder
+        = GetQueryStatusRequest.newBuilder();
+    builder.setQueryId(queryId.getProto());
+
+    GetQueryStatusResponse res = null;
+    if(queryMasterMap.containsKey(queryId)) {
+      NettyClientBase qmClient = null;
+      try {
+        qmClient = connPool.getConnection(queryMasterMap.get(queryId),
+            QueryMasterClientProtocol.class, false);
+        QueryMasterClientProtocolService.BlockingInterface queryMasterService = qmClient.getStub();
+        res = queryMasterService.getQueryStatus(null, builder.build());
+      } catch (Exception e) {
+        throw new ServiceException(e.getMessage(), e);
+      } finally {
+        connPool.closeConnection(qmClient);
+      }
+    } else {
+      NettyClientBase tmClient = null;
+      try {
+        tmClient = connPool.getConnection(tajoMasterAddr,
+            TajoMasterClientProtocol.class, false);
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = tmClient.getStub();
+        res = tajoMasterService.getQueryStatus(null, builder.build());
+
+        String queryMasterHost = res.getQueryMasterHost();
+        if(queryMasterHost != null && !queryMasterHost.isEmpty()) {
+          NettyClientBase qmClient = null;
+          try {
+            InetSocketAddress qmAddr = NetUtils.createSocketAddr(queryMasterHost, res.getQueryMasterPort());
+            qmClient = connPool.getConnection(
+                qmAddr, QueryMasterClientProtocol.class, false);
+            QueryMasterClientProtocolService.BlockingInterface queryMasterService = qmClient.getStub();
+            res = queryMasterService.getQueryStatus(null, builder.build());
+
+            queryMasterMap.put(queryId, qmAddr);
+          } catch (Exception e) {
+            throw new ServiceException(e.getMessage(), e);
+          } finally {
+            connPool.closeConnection(qmClient);
+          }
+        }
+      } catch (Exception e) {
+        throw new ServiceException(e.getMessage(), e);
+      } finally {
+        connPool.closeConnection(tmClient);
+      }
+    }
+    return new QueryStatus(res);
+  }
+
+  private static boolean isQueryRunnning(QueryState state) {
+    return state == QueryState.QUERY_NEW ||
+        state == QueryState.QUERY_RUNNING ||
+        state == QueryState.QUERY_MASTER_LAUNCHED ||
+        state == QueryState.QUERY_MASTER_INIT ||
+        state == QueryState.QUERY_NOT_ASSIGNED;
+  }
+
+  public ResultSet getQueryResult(QueryId queryId)
+      throws ServiceException, IOException {
+    if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+      return createNullResultSet(queryId);
+    }
+    GetQueryResultResponse response = getResultResponse(queryId);
+    TableDesc tableDesc = CatalogUtil.newTableDesc(response.getTableDesc());
+    conf.setVar(ConfVars.USERNAME, response.getTajoUserName());
+    return new TajoResultSet(this, queryId, conf, tableDesc);
+  }
+
+  public ResultSet getQueryResultAndWait(QueryId queryId)
+      throws ServiceException, IOException {
+    if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+      return createNullResultSet(queryId);
+    }
+    QueryStatus status = getQueryStatus(queryId);
+
+    while(status != null && isQueryRunnning(status.getState())) {
+      try {
+        Thread.sleep(500);
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+
+      status = getQueryStatus(queryId);
+    }
+
+    if (status.getState() == QueryState.QUERY_SUCCEEDED) {
+      if (status.hasResult()) {
+        return getQueryResult(queryId);
+      } else {
+        return createNullResultSet(queryId);
+      }
+
+    } else {
+      LOG.warn("Query (" + status.getQueryId() + ") failed: " + status.getState());
+
+      //TODO throw SQLException(?)
+      return createNullResultSet(queryId);
+    }
+  }
+
+  public ResultSet createNullResultSet(QueryId queryId) throws IOException {
+    return new TajoResultSet(this, queryId);
+  }
+
+  public GetQueryResultResponse getResultResponse(QueryId queryId) throws ServiceException {
+    if (queryId.equals(QueryIdFactory.NULL_QUERY_ID)) {
+      return null;
+    }
+
+    NettyClientBase client = null;
+    try {
+      InetSocketAddress queryMasterAddr = queryMasterMap.get(queryId);
+      if(queryMasterAddr == null) {
+        LOG.warn("No Connection to QueryMaster for " + queryId);
+        return null;
+      }
+      client = connPool.getConnection(queryMasterAddr, QueryMasterClientProtocol.class, false);
+      QueryMasterClientProtocolService.BlockingInterface queryMasterService = client.getStub();
+      GetQueryResultRequest.Builder builder = GetQueryResultRequest.newBuilder();
+      builder.setQueryId(queryId.getProto());
+      GetQueryResultResponse response = queryMasterService.getQueryResult(null,
+          builder.build());
+
+      return response;
+    } catch (Exception e) {
+      throw new ServiceException(e.getMessage(), e);
+    } finally {
+      connPool.closeConnection(client);
+    }
+  }
+
+  public boolean updateQuery(final String sql) throws ServiceException {
+    return new ServerCallable<Boolean>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public Boolean call(NettyClientBase client) throws ServiceException {
+        QueryRequest.Builder builder = QueryRequest.newBuilder();
+        builder.setQuery(sql);
+
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+        ResultCode resultCode =
+            tajoMasterService.updateQuery(null, builder.build()).getResultCode();
+        return resultCode == ResultCode.OK;
+      }
+    }.withRetries();
+  }
+
+  /**
+   * Test for the existence of table in catalog data.
+   * <p/>
+   * This will return true if table exists, false if not.
+   * @param name
+   * @return
+   * @throws ServiceException
+   */
+  public boolean existTable(final String name) throws ServiceException {
+    return new ServerCallable<Boolean>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public Boolean call(NettyClientBase client) throws ServiceException {
+        StringProto.Builder builder = StringProto.newBuilder();
+        builder.setValue(name);
+
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+        return tajoMasterService.existTable(null, builder.build()).getValue();
+      }
+    }.withRetries();
+  }
+
+  public TableDesc createExternalTable(final String name, final Schema schema, final Path path, final TableMeta meta)
+      throws SQLException, ServiceException {
+    return new ServerCallable<TableDesc>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public TableDesc call(NettyClientBase client) throws ServiceException, SQLException {
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+
+        CreateTableRequest.Builder builder = CreateTableRequest.newBuilder();
+        builder.setName(name);
+        builder.setSchema(schema.getProto());
+        builder.setMeta(meta.getProto());
+        builder.setPath(path.toUri().toString());
+        TableResponse res = tajoMasterService.createExternalTable(null, builder.build());
+        if (res.getResultCode() == ResultCode.OK) {
+          return CatalogUtil.newTableDesc(res.getTableDesc());
+        } else {
+          throw new SQLException(res.getErrorMessage(), SQLStates.ER_NO_SUCH_TABLE.getState());
+        }
+      }
+    }.withRetries();
+  }
+
+  public boolean dropTable(final String tableName) throws ServiceException {
+    return dropTable(tableName, false);
+  }
+
+  /**
+   * Deletes table schema from catalog data and deletes data file in hdfs
+   * @param tableName
+   * @return
+   * @throws ServiceException
+   */
+  public boolean dropTable(final String tableName, final boolean purge) throws ServiceException {
+    return new ServerCallable<Boolean>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public Boolean call(NettyClientBase client) throws ServiceException {
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+
+        DropTableRequest.Builder builder = DropTableRequest.newBuilder();
+        builder.setName(tableName);
+        builder.setPurge(purge);
+        return tajoMasterService.dropTable(null, builder.build()).getValue();
+      }
+    }.withRetries();
+
+  }
+
+  /**
+   * Get a list of table names. All table and column names are
+   * represented as lower-case letters.
+   */
+  public List<String> getTableList() throws ServiceException {
+    return new ServerCallable<List<String>>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public List<String> call(NettyClientBase client) throws ServiceException {
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+
+        GetTableListRequest.Builder builder = GetTableListRequest.newBuilder();
+        GetTableListResponse res = tajoMasterService.getTableList(null, builder.build());
+        return res.getTablesList();
+      }
+    }.withRetries();
+  }
+
+  public TableDesc getTableDesc(final String tableName) throws SQLException, ServiceException {
+    return new ServerCallable<TableDesc>(conf, tajoMasterAddr,
+        TajoMasterClientProtocol.class, false, true) {
+      public TableDesc call(NettyClientBase client) throws ServiceException, SQLException {
+        TajoMasterClientProtocolService.BlockingInterface tajoMasterService = client.getStub();
+
+        GetTableDescRequest.Builder build = GetTableDescRequest.newBuilder();
+        build.setTableName(tableName);
+        TableResponse res = tajoMasterService.getTableDesc(null, build.build());
+        if (res.getResultCode() == ResultCode.OK) {
+          return CatalogUtil.newTableDesc(res.getTableDesc());
+        } else {
+          throw new SQLException(res.getErrorMessage(), SQLStates.ER_NO_SUCH_TABLE.getState());
+        }
+      }
+    }.withRetries();
+  }
+
+  public boolean killQuery(final QueryId queryId)
+      throws ServiceException, IOException {
+
+    QueryStatus status = getQueryStatus(queryId);
+
+    NettyClientBase tmClient = null;
+    try {
+      /* send a kill to the TM */
+      tmClient = connPool.getConnection(tajoMasterAddr, TajoMasterClientProtocol.class, false);
+      TajoMasterClientProtocolService.BlockingInterface tajoMasterService = tmClient.getStub();
+      tajoMasterService.killQuery(null, queryId.getProto());
+
+      long currentTimeMillis = System.currentTimeMillis();
+      long timeKillIssued = currentTimeMillis;
+      while ((currentTimeMillis < timeKillIssued + 10000L) && (status.getState()
+          != QueryState.QUERY_KILLED)) {
+        try {
+          Thread.sleep(1000L);
+        } catch(InterruptedException ie) {
+          /** interrupted, just break */
+          break;
+        }
+        currentTimeMillis = System.currentTimeMillis();
+        status = getQueryStatus(queryId);
+      }
+    } catch(Exception e) {
+      LOG.debug("Error when checking for application status", e);
+      return false;
+    } finally {
+      connPool.closeConnection(tmClient);
+    }
+
+    return true;
+  }
+
+  public static void main(String[] args) throws Exception {
+    TajoClient client = new TajoClient(new TajoConf());
+
+    client.close();
+
+    synchronized(client) {
+      client.wait();
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/client/TajoDump.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/client/TajoDump.java b/tajo-client/src/main/java/org/apache/tajo/client/TajoDump.java
new file mode 100644
index 0000000..486ff9f
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/client/TajoDump.java
@@ -0,0 +1,129 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.client;
+
+import com.google.common.collect.Lists;
+import com.google.protobuf.ServiceException;
+import org.apache.commons.cli.*;
+import org.apache.tajo.catalog.DDLBuilder;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.conf.TajoConf;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.sql.SQLException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.List;
+
+public class TajoDump {
+  private static final org.apache.commons.cli.Options options;
+
+  static {
+    options = new Options();
+    options.addOption("h", "host", true, "Tajo server host");
+    options.addOption("p", "port", true, "Tajo server port");
+    options.addOption("a", "all", false, "dump all table DDLs");
+  }
+
+  private static void printUsage() {
+    HelpFormatter formatter = new HelpFormatter();
+    formatter.printHelp( "tajo_dump [options] [table_name]", options );
+  }
+
+  public static void main(String [] args) throws ParseException, IOException, ServiceException, SQLException {
+    TajoConf conf = new TajoConf();
+
+    CommandLineParser parser = new PosixParser();
+    CommandLine cmd = parser.parse(options, args);
+
+    String hostName = null;
+    Integer port = null;
+    if (cmd.hasOption("h")) {
+      hostName = cmd.getOptionValue("h");
+    }
+    if (cmd.hasOption("p")) {
+      port = Integer.parseInt(cmd.getOptionValue("p"));
+    }
+
+    // if there is no "-h" option,
+    if(hostName == null) {
+      if (conf.getVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS) != null) {
+        // it checks if the client service address is given in configuration and distributed mode.
+        // if so, it sets entryAddr.
+        hostName = conf.getVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS).split(":")[0];
+      }
+    }
+    if (port == null) {
+      if (conf.getVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS) != null) {
+        // it checks if the client service address is given in configuration and distributed mode.
+        // if so, it sets entryAddr.
+        port = Integer.parseInt(conf.getVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS).split(":")[1]);
+      }
+    }
+
+    TajoClient client = null;
+    if ((hostName == null) ^ (port == null)) {
+      System.err.println("ERROR: cannot find valid Tajo server address");
+      System.exit(-1);
+    } else if (hostName != null && port != null) {
+      conf.setVar(TajoConf.ConfVars.TAJO_MASTER_CLIENT_RPC_ADDRESS, hostName+":"+port);
+      client = new TajoClient(conf);
+    } else if (hostName == null && port == null) {
+      client = new TajoClient(conf);
+    }
+
+    List<TableDesc> tableDescList = Lists.newArrayList();
+
+    if (cmd.hasOption("a")) {
+      for (String tableName : client.getTableList()) {
+        tableDescList.add(client.getTableDesc(tableName));
+      }
+    } else if (cmd.getArgs().length > 0) {
+      for (String tableName : cmd.getArgs()) {
+        tableDescList.add(client.getTableDesc(tableName));
+      }
+    } else {
+      printUsage();
+    }
+
+
+    Writer writer = new PrintWriter(System.out);
+    writer.write("--\n");
+    writer.write("-- Tajo database dump\n");
+    writer.write("-- Dump date: " + toDateString() + "\n");
+    writer.write("--\n");
+    writer.write("\n");
+    for (TableDesc tableDesc : tableDescList) {
+      writer.write(DDLBuilder.buildDDL(tableDesc));
+      writer.write("\n\n");
+    }
+    writer.flush();
+    writer.close();
+    System.exit(0);
+  }
+
+  private static String toDateString() {
+    DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+    java.util.Date today = Calendar.getInstance().getTime();
+    return df.format(today);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/jdbc/SQLStates.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/jdbc/SQLStates.java b/tajo-client/src/main/java/org/apache/tajo/jdbc/SQLStates.java
new file mode 100644
index 0000000..32ab19c
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/jdbc/SQLStates.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.jdbc;
+
+public enum SQLStates {
+  ER_NO_SUCH_TABLE("42S02");
+
+  private String state;
+
+  SQLStates(String state) {
+    this.state = state;
+  }
+
+  public String getState() {
+    return state;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/b6a5ff0c/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java b/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
new file mode 100644
index 0000000..1005765
--- /dev/null
+++ b/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
@@ -0,0 +1,152 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.jdbc;
+
+import com.google.common.collect.Lists;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.PathFilter;
+import org.apache.tajo.QueryId;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.catalog.TableMeta;
+import org.apache.tajo.client.TajoClient;
+import org.apache.tajo.conf.TajoConf;
+import org.apache.tajo.storage.FileScanner;
+import org.apache.tajo.storage.MergeScanner;
+import org.apache.tajo.storage.Scanner;
+import org.apache.tajo.storage.Tuple;
+import org.apache.tajo.storage.fragment.FileFragment;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+
+public class TajoResultSet extends TajoResultSetBase {
+  private FileSystem fs;
+  private Scanner scanner;
+  private TajoClient tajoClient;
+  QueryId queryId;
+
+  public TajoResultSet(TajoClient tajoClient, QueryId queryId) {
+    this.tajoClient = tajoClient;
+    this.queryId = queryId;
+    init();
+  }
+
+  public TajoResultSet(TajoClient tajoClient, QueryId queryId,
+                       Configuration conf, TableDesc desc) throws IOException {
+    this.schema = desc.getSchema();
+    this.tajoClient = tajoClient;
+    this.queryId = queryId;
+    if(desc != null) {
+      fs = FileScanner.getFileSystem((TajoConf)conf, desc.getPath());
+      this.totalRow = desc.getStats() != null ? desc.getStats().getNumRows() : 0;
+
+      Collection<FileFragment> frags = getFragments(desc.getMeta(), desc.getPath());
+      scanner = new MergeScanner(conf, schema, desc.getMeta(), frags);
+    }
+    init();
+  }
+
+  @Override
+  protected void init() {
+    cur = null;
+    curRow = 0;
+  }
+
+  class FileNameComparator implements Comparator<FileStatus> {
+
+    @Override
+    public int compare(FileStatus f1, FileStatus f2) {
+      return f2.getPath().getName().compareTo(f1.getPath().getName());
+    }
+  }
+
+  private Collection<FileFragment> getFragments(TableMeta meta, Path tablePath)
+      throws IOException {
+    List<FileFragment> fraglist = Lists.newArrayList();
+    FileStatus[] files = fs.listStatus(tablePath, new PathFilter() {
+      @Override
+      public boolean accept(Path path) {
+        return path.getName().charAt(0) != '.';
+      }
+    });
+    Arrays.sort(files, new FileNameComparator());
+
+    String tbname = tablePath.getName();
+    for (int i = 0; i < files.length; i++) {
+      if (files[i].getLen() == 0) {
+        continue;
+      }
+      fraglist.add(new FileFragment(tbname + "_" + i, files[i].getPath(), 0l, files[i].getLen()));
+    }
+    return fraglist;
+  }
+
+  @Override
+  public void close() throws SQLException {
+    try {
+      if(tajoClient != null) {
+        this.tajoClient.closeQuery(queryId);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    try {
+      if(scanner != null) {
+        this.scanner.close();
+      }
+      //TODO clean temp result file
+      cur = null;
+      curRow = -1;
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void beforeFirst() throws SQLException {
+    try {
+      if(scanner != null) {
+        scanner.reset();
+      }
+      init();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+
+  @Override
+  protected Tuple nextTuple() throws IOException {
+    if(scanner == null) {
+      return null;
+    }
+    return scanner.next();
+  }
+
+  public boolean hasResult() {
+    return scanner != null;
+  }
+}