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

eya pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-age.git


The following commit(s) were added to refs/heads/master by this push:
     new c1a91a2  feat: (driver/jdbc) create parser for agtype. (#68)
c1a91a2 is described below

commit c1a91a2c4764323055fe0f6d9a06c99d01d2bcac
Author: Alex Kwak <[email protected]>
AuthorDate: Tue May 18 08:56:03 2021 +0900

    feat: (driver/jdbc) create parser for agtype. (#68)
---
 drivers/Agtype.g4                                  | 116 ++++++
 drivers/jdbc/.gitattributes                        |   6 +
 drivers/jdbc/.gitignore                            |   5 +
 drivers/jdbc/gradle/wrapper/gradle-wrapper.jar     | Bin 0 -> 59203 bytes
 .../jdbc/gradle/wrapper/gradle-wrapper.properties  |   5 +
 drivers/jdbc/gradlew                               | 185 +++++++++
 drivers/jdbc/gradlew.bat                           |  89 +++++
 drivers/jdbc/lib/build.gradle.kts                  |  43 +++
 .../apache/age/jdbc/AgtypeUnrecognizedList.java    |  21 ++
 .../org/apache/age/jdbc/AgtypeUnrecognizedMap.java |  21 ++
 .../main/java/org/apache/age/jdbc/base/Agtype.java | 200 ++++++++++
 .../org/apache/age/jdbc/base/AgtypeFactory.java    |  44 +++
 .../org/apache/age/jdbc/base/AgtypeListener.java   | 187 +++++++++
 .../java/org/apache/age/jdbc/base/AgtypeUtil.java  | 345 +++++++++++++++++
 .../age/jdbc/base/InvalidAgtypeException.java      |  15 +
 .../age/jdbc/base/type/AgtypeAnnotation.java       |   6 +
 .../org/apache/age/jdbc/base/type/AgtypeList.java  | 159 ++++++++
 .../age/jdbc/base/type/AgtypeListBuilder.java      | 151 ++++++++
 .../apache/age/jdbc/base/type/AgtypeListImpl.java  |  55 +++
 .../org/apache/age/jdbc/base/type/AgtypeMap.java   | 199 ++++++++++
 .../age/jdbc/base/type/AgtypeMapBuilder.java       | 171 +++++++++
 .../apache/age/jdbc/base/type/AgtypeMapImpl.java   |  95 +++++
 .../apache/age/jdbc/base/type/AgtypeObject.java    |   5 +
 .../age/jdbc/base/type/UnrecognizedObject.java     |   6 +
 .../org/apache/age/jdbc/AgtypeFactoryTest.java     | 106 ++++++
 .../org/apache/age/jdbc/AgtypeStatementTest.java   | 183 +++++++++
 .../test/java/org/apache/age/jdbc/AgtypeTest.java  | 416 +++++++++++++++++++++
 .../java/org/apache/age/jdbc/AgtypeUtilTest.java   | 127 +++++++
 .../org/apache/age/jdbc/BaseDockerizedTest.java    |  54 +++
 drivers/jdbc/settings.gradle.kts                   |  11 +
 30 files changed, 3026 insertions(+)

diff --git a/drivers/Agtype.g4 b/drivers/Agtype.g4
new file mode 100644
index 0000000..1d75c1c
--- /dev/null
+++ b/drivers/Agtype.g4
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+grammar Agtype;
+
+agType
+  : agValue EOF
+  ;
+
+agValue
+  : value typeAnnotation?
+  ;
+
+value
+  : STRING #StringValue
+  | INTEGER #IntegerValue
+  | floatLiteral #FloatValue
+  | 'true' #TrueBoolean
+  | 'false' #FalseBoolean
+  | 'null' #NullValue
+  | obj #ObjectValue
+  | array #ArrayValue
+  ;
+
+obj
+  : '{' pair (',' pair)* '}'
+  | '{' '}'
+  ;
+
+pair
+  : STRING ':' agValue
+  ;
+
+array
+  : '[' agValue (',' agValue)* ']'
+  | '[' ']'
+  ;
+
+typeAnnotation
+  : '::' IDENT
+  ;
+
+IDENT
+  : [A-Z_a-z][$0-9A-Z_a-z]*
+  ;
+
+STRING
+  : '"' (ESC | SAFECODEPOINT)* '"'
+  ;
+
+fragment ESC
+  : '\\' (["\\/bfnrt] | UNICODE)
+  ;
+
+fragment UNICODE
+  : 'u' HEX HEX HEX HEX
+  ;
+
+fragment HEX
+  : [0-9a-fA-F]
+  ;
+
+fragment SAFECODEPOINT
+  : ~ ["\\\u0000-\u001F]
+  ;
+
+INTEGER
+  : '-'? INT
+  ;
+
+fragment INT
+  : '0' | [1-9] [0-9]*
+  ;
+
+floatLiteral
+  : RegularFloat
+  | ExponentFloat
+  | '-'? 'Infinity'
+  | 'NaN'
+  ;
+
+RegularFloat
+  : '-'? INT DECIMAL
+  ;
+
+ExponentFloat
+  : '-'? INT DECIMAL? SCIENTIFIC
+  ;
+
+fragment DECIMAL
+  : '.' [0-9]+
+  ;
+
+fragment SCIENTIFIC
+  : [Ee][+-]? [0-9]+
+  ;
+
+WS
+  : [ \t\n\r] + -> skip
+  ;
diff --git a/drivers/jdbc/.gitattributes b/drivers/jdbc/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/drivers/jdbc/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat           text eol=crlf
+
diff --git a/drivers/jdbc/.gitignore b/drivers/jdbc/.gitignore
new file mode 100644
index 0000000..1b6985c
--- /dev/null
+++ b/drivers/jdbc/.gitignore
@@ -0,0 +1,5 @@
+# Ignore Gradle project-specific cache directory
+.gradle
+
+# Ignore Gradle build output directory
+build
diff --git a/drivers/jdbc/gradle/wrapper/gradle-wrapper.jar 
b/drivers/jdbc/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/drivers/jdbc/gradle/wrapper/gradle-wrapper.jar 
differ
diff --git a/drivers/jdbc/gradle/wrapper/gradle-wrapper.properties 
b/drivers/jdbc/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..f371643
--- /dev/null
+++ b/drivers/jdbc/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/drivers/jdbc/gradlew b/drivers/jdbc/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/drivers/jdbc/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed 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
+#
+#      https://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.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to 
pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 
'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; 
then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" 
\"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### 
Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### 
Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" 
"$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" 
"$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" 
"$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; 
done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and 
substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 
"\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" 
org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/drivers/jdbc/gradlew.bat b/drivers/jdbc/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/drivers/jdbc/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS 
to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your 
PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% 
"-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" 
org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code 
instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/drivers/jdbc/lib/build.gradle.kts 
b/drivers/jdbc/lib/build.gradle.kts
new file mode 100644
index 0000000..e95d4bc
--- /dev/null
+++ b/drivers/jdbc/lib/build.gradle.kts
@@ -0,0 +1,43 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This generated file contains a sample Java library project to get you 
started.
+ * For more details take a look at the 'Building Java & JVM projects' chapter 
in the Gradle
+ * User Manual available at 
https://docs.gradle.org/7.0/userguide/building_java_projects.html
+ */
+
+plugins {
+    `java-library`
+    antlr
+}
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    implementation("org.postgresql:postgresql:42.2.20")
+    api("org.apache.commons:commons-text:1.9")
+    antlr("org.antlr:antlr4:4.9.2")
+
+    testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.1")
+    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
+
+    testImplementation("org.testcontainers:testcontainers:1.15.3")
+    testImplementation("org.postgresql:postgresql:42.2.20")
+}
+
+tasks.generateGrammarSource {
+    maxHeapSize = "64m"
+    source = project.objects
+        .sourceDirectorySet("antlr", "antlr")
+        .srcDir("${projectDir}/../../").apply {
+            include("*.g4")
+        }
+    arguments.addAll(arrayOf("-package", "org.apache.age.jdbc.antlr4"))
+    outputDirectory = file("$outputDirectory/org/apache/age/jdbc/antlr4")
+}
+
+tasks.test {
+    useJUnitPlatform()
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedList.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedList.java
new file mode 100644
index 0000000..91db969
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedList.java
@@ -0,0 +1,21 @@
+package org.apache.age.jdbc;
+
+import org.apache.age.jdbc.base.type.AgtypeAnnotation;
+import org.apache.age.jdbc.base.type.AgtypeListImpl;
+import org.apache.age.jdbc.base.type.UnrecognizedObject;
+
+public class AgtypeUnrecognizedList extends AgtypeListImpl implements 
UnrecognizedObject,
+    AgtypeAnnotation {
+
+    private String annotation;
+
+    @Override
+    public String getAnnotation() {
+        return this.annotation;
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        this.annotation = annotation;
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedMap.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedMap.java
new file mode 100644
index 0000000..b0d4ffc
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/AgtypeUnrecognizedMap.java
@@ -0,0 +1,21 @@
+package org.apache.age.jdbc;
+
+import org.apache.age.jdbc.base.type.AgtypeAnnotation;
+import org.apache.age.jdbc.base.type.AgtypeMapImpl;
+import org.apache.age.jdbc.base.type.UnrecognizedObject;
+
+public class AgtypeUnrecognizedMap extends AgtypeMapImpl implements 
UnrecognizedObject,
+    AgtypeAnnotation {
+
+    private String annotation;
+
+    @Override
+    public String getAnnotation() {
+        return this.annotation;
+    }
+
+    @Override
+    public void setAnnotation(String annotation) {
+        this.annotation = annotation;
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/Agtype.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/Agtype.java
new file mode 100644
index 0000000..c7c2bf5
--- /dev/null
+++ b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/Agtype.java
@@ -0,0 +1,200 @@
+package org.apache.age.jdbc.base;
+
+import java.sql.SQLException;
+import org.apache.age.jdbc.base.type.AgtypeAnnotation;
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+import org.postgresql.util.PGobject;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+/**
+ * Stores values of various kinds in a single object for use in PostgreSQL. 
The text representation
+ * is built on top of the <a href="https://tools.ietf.org/html/rfc8259";>JSON 
format
+ * specification</a>. The goal of the text representation is making it 
compatible with JSON as much
+ * as possible so that valid JSON values can be parsed without effort.
+ * <br><br>
+ * Valid Agtypes:
+ * <ul>
+ * <li>null</li>
+ * <li>int</li>
+ * <li>long</li>
+ * <li>double</li>
+ * <li>boolean</li>
+ * <li>String</li>
+ * <li>{@link AgtypeList}</li>
+ * <li>{@link AgtypeMap}</li>
+ * </ul>
+ */
+public class Agtype extends PGobject implements Cloneable {
+
+    private Object obj;
+
+    /**
+     * Public constructor for Agtype. Do not call directly, use the 
AgtypeFactory when creating
+     * Agtype objects on the client-side and casting the received object in 
the ResultSet when the
+     * object is created on the server-side.
+     */
+    public Agtype() {
+        super.setType("ag_catalog.agtype");
+    }
+
+    Agtype(Object obj) {
+        this();
+
+        this.obj = obj;
+    }
+
+    /**
+     * TODO: need to define for PreparedStatement.
+     */
+    @Override
+    public String getValue() {
+        if (value == null) {
+            value = AgtypeUtil.serializeAgtype(obj);
+        }
+
+        return value;
+    }
+
+    /**
+     * Parses the serialized value to it's Agtype value. {@inheritDoc}
+     *
+     * @param value Serialized representation of Agtype value.
+     * @throws SQLException throws if the String value cannot be parsed to a 
valid Agtype.
+     * @see AgtypeUtil#parse(String)
+     */
+    @Override
+    public void setValue(String value) throws SQLException {
+        try {
+            obj = AgtypeUtil.parse(value);
+        } catch (Exception e) {
+            throw new PSQLException("Parsing AgType failed", 
PSQLState.DATA_ERROR, e);
+        }
+
+        super.setValue(value);
+    }
+
+    /**
+     * Returns the value stored in Agtype as a String. Attempts to perform an 
implicit conversion of
+     * types stored as non-strings values.
+     *
+     * @return value stored in Agtype as a String.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as a
+     *                                String.
+     * @see AgtypeUtil#getString(Object)
+     */
+    public String getString() throws InvalidAgtypeException {
+        return AgtypeUtil.getString(obj);
+    }
+
+    /**
+     * Returns the value stored in Agtype as a generic object.
+     *
+     * @return value stored in Agtype as a generic object.
+     */
+    public Object getObject() throws InvalidAgtypeException {
+        return obj;
+    }
+
+    /**
+     * Returns the value stored in Agtype as an int. Attempts to perform an 
implicit conversion of
+     * types stored as non-int values.
+     *
+     * @return value stored in Agtype as an int.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as an
+     *                                int.
+     * @see AgtypeUtil#getInt(Object)
+     */
+    public int getInt() throws InvalidAgtypeException {
+        return AgtypeUtil.getInt(obj);
+    }
+
+    /**
+     * Returns the value stored in Agtype as a long. Attempts to perform an 
implicit conversion of
+     * types stored as non-long values.
+     *
+     * @return value stored in Agtype as a long.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as an
+     *                                long.
+     * @see AgtypeUtil#getLong(Object)
+     */
+    public long getLong() throws InvalidAgtypeException {
+        return AgtypeUtil.getLong(obj);
+    }
+
+    /**
+     * Returns the value stored in Agtype as a double. Attempts to perform an 
implicit conversion of
+     * types stored as non-double values.
+     *
+     * @return value stored in Agtype as a double.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as an
+     *                                double.
+     * @see AgtypeUtil#getDouble(Object)
+     */
+    public double getDouble() throws InvalidAgtypeException {
+        return AgtypeUtil.getDouble(obj);
+    }
+
+    /**
+     * Returns the value stored in Agtype as a boolean. Attempts to perform an 
implicit conversion
+     * of types stored as non-boolean values.
+     *
+     * @return value stored in Agtype as a long.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as an
+     *                                boolean.
+     * @see AgtypeUtil#getBoolean(Object)
+     */
+    public boolean getBoolean() throws InvalidAgtypeException {
+        return AgtypeUtil.getBoolean(obj);
+    }
+
+    /**
+     * Returns the value stored in Agtype as an AgtypeList.
+     *
+     * @return value stored in Agtype as an AgtypeList.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as an
+     *                                AgtypeList.
+     * @see AgtypeUtil#getList(Object)
+     */
+    public AgtypeList getList() throws InvalidAgtypeException {
+        return AgtypeUtil.getList(obj);
+    }
+
+    /**
+     * Returns the value stored in Agtype as an AgtypeMap.
+     *
+     * @return value stored in Agtype as an AgtypeMap.
+     * @throws InvalidAgtypeException Throws if the stored Agtype value cannot 
be represented as an
+     *                                AgtypeMap.
+     * @see AgtypeUtil#getMap(Object)
+     */
+    public AgtypeMap getMap() throws InvalidAgtypeException {
+        return AgtypeUtil.getMap(obj);
+    }
+
+    /**
+     * Returns whether stored is Agtype Null.
+     *
+     * @return true if the value is Agtype null, false otherwise.
+     */
+    public boolean isNull() {
+        return obj == null;
+    }
+
+    /**
+     * Returns a string representation of this Agtype object.
+     *
+     * @return a string representation of this Agtype object.
+     */
+    @Override
+    public String toString() {
+        if (obj != null && obj instanceof AgtypeAnnotation) {
+            return obj
+                + (type != null ? "::" + ((AgtypeAnnotation) 
obj).getAnnotation() : "");
+        }
+        return (obj != null ? obj.toString() : "null")
+            + (type != null ? "::" + type : "");
+    }
+
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeFactory.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeFactory.java
new file mode 100644
index 0000000..24320c9
--- /dev/null
+++ b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeFactory.java
@@ -0,0 +1,44 @@
+package org.apache.age.jdbc.base;
+
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+
+/**
+ * Factory for creating Agtype objects.
+ *
+ * @see Agtype
+ */
+public class AgtypeFactory {
+
+    /**
+     * Creates an Agtype object.
+     *
+     * @param obj Object to store in the an Agtype Object.
+     * @return new Agtype Object
+     * @throws InvalidAgtypeException Thrown if the object passed is not a 
{@link Agtype valid
+     *                                Agtype}
+     */
+    public static Agtype create(Object obj) throws InvalidAgtypeException {
+        if (obj == null) {
+            return new Agtype(null);
+        } else if (obj instanceof Integer) {
+            return new Agtype(((Integer) obj).longValue());
+        } else if (obj instanceof Long) {
+            return new Agtype(obj);
+        } else if (obj instanceof String) {
+            return new Agtype(obj);
+        } else if (obj instanceof Boolean) {
+            return new Agtype(obj);
+        } else if (obj instanceof Double) {
+            return new Agtype(obj);
+        } else if (obj instanceof AgtypeList) {
+            return new Agtype(obj);
+        } else if (obj instanceof AgtypeMap) {
+            return new Agtype(obj);
+        } else {
+            String s = String
+                .format("%s is not a valid Agtype value", 
obj.getClass().getSimpleName());
+            throw new InvalidAgtypeException(s);
+        }
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeListener.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeListener.java
new file mode 100644
index 0000000..5aeb772
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeListener.java
@@ -0,0 +1,187 @@
+package org.apache.age.jdbc.base;
+
+import java.util.Stack;
+import org.apache.age.jdbc.AgtypeUnrecognizedList;
+import org.apache.age.jdbc.AgtypeUnrecognizedMap;
+import org.apache.age.jdbc.antlr4.AgtypeBaseListener;
+import org.apache.age.jdbc.antlr4.AgtypeParser.AgTypeContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.ArrayValueContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.FalseBooleanContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.FloatValueContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.IntegerValueContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.NullValueContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.ObjectValueContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.PairContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.StringValueContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.TrueBooleanContext;
+import org.apache.age.jdbc.antlr4.AgtypeParser.TypeAnnotationContext;
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeListImpl;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+import org.apache.age.jdbc.base.type.AgtypeMapImpl;
+import org.apache.age.jdbc.base.type.AgtypeObject;
+import org.apache.age.jdbc.base.type.UnrecognizedObject;
+import org.apache.commons.text.StringEscapeUtils;
+
+public class AgtypeListener extends AgtypeBaseListener {
+
+    // Will have List or Map
+    private final Stack<AgtypeObject> objectStack = new Stack<>();
+    private final Stack<String> annotationMap = new Stack<>();
+    Object rootObject;
+    Object lastValue;
+    boolean lastValueUndefined = true;
+
+    private long objectStackLength = 0;
+
+    private void pushObjectStack(AgtypeObject o) {
+        objectStackLength++;
+        this.objectStack.push(o);
+    }
+
+    private AgtypeObject popObjectStack() {
+        objectStackLength--;
+        return objectStack.pop();
+    }
+
+    private AgtypeObject peekObjectStack() {
+        return objectStack.peek();
+    }
+
+    private void mergeObjectIfTargetIsArray() {
+        if (objectStackLength >= 2) {
+            AgtypeObject firstObject = popObjectStack();
+            AgtypeObject secondObject = popObjectStack();
+            if (secondObject instanceof AgtypeListImpl) {
+                ((AgtypeListImpl) secondObject).add(firstObject);
+                pushObjectStack(secondObject);
+            } else {
+                pushObjectStack(secondObject);
+                pushObjectStack(firstObject);
+            }
+        }
+    }
+
+    private void mergeObjectIfTargetIsMap(String key, Object value) {
+        AgtypeMapImpl agtypeMap = (AgtypeMapImpl) peekObjectStack();
+        agtypeMap.put(key, value);
+    }
+
+    private void addObjectValue() {
+        if (objectStackLength != 0) {
+            AgtypeObject currentObject = peekObjectStack();
+            if (currentObject instanceof AgtypeListImpl) {
+                ((AgtypeListImpl) currentObject).add(this.lastValue);
+                lastValueUndefined = true;
+                return;
+            }
+        }
+        lastValueUndefined = false;
+    }
+
+    @Override
+    public void exitStringValue(StringValueContext ctx) {
+        this.lastValue = identString(ctx.STRING().getText());
+        addObjectValue();
+    }
+
+    @Override
+    public void exitIntegerValue(IntegerValueContext ctx) {
+        this.lastValue = Long.parseLong(ctx.INTEGER().getText());
+        addObjectValue();
+    }
+
+    @Override
+    public void exitFloatValue(FloatValueContext ctx) {
+        this.lastValue = Double.parseDouble(ctx.floatLiteral().getText());
+        addObjectValue();
+    }
+
+    @Override
+    public void exitTrueBoolean(TrueBooleanContext ctx) {
+        this.lastValue = true;
+        addObjectValue();
+    }
+
+    @Override
+    public void exitFalseBoolean(FalseBooleanContext ctx) {
+        this.lastValue = false;
+        addObjectValue();
+    }
+
+    @Override
+    public void exitNullValue(NullValueContext ctx) {
+        this.lastValue = null;
+        addObjectValue();
+    }
+
+    @Override
+    public void enterObjectValue(ObjectValueContext ctx) {
+        AgtypeMap agtypeMap = new AgtypeUnrecognizedMap();
+        pushObjectStack(agtypeMap);
+    }
+
+    @Override
+    public void exitObjectValue(ObjectValueContext ctx) {
+        mergeObjectIfTargetIsArray();
+    }
+
+    @Override
+    public void enterArrayValue(ArrayValueContext ctx) {
+        AgtypeList agtypeList = new AgtypeUnrecognizedList();
+        pushObjectStack(agtypeList);
+    }
+
+    @Override
+    public void exitArrayValue(ArrayValueContext ctx) {
+        mergeObjectIfTargetIsArray();
+    }
+
+    @Override
+    public void exitPair(PairContext ctx) {
+        String name = identString(ctx.STRING().getText());
+        if (!lastValueUndefined) {
+            mergeObjectIfTargetIsMap(name, this.lastValue);
+            lastValueUndefined = true;
+        } else {
+            Object lastValue = popObjectStack();
+            Object currentHeaderObject = peekObjectStack();
+            if (currentHeaderObject instanceof AgtypeListImpl) {
+                ((AgtypeListImpl) currentHeaderObject).add(lastValue);
+            } else {
+                mergeObjectIfTargetIsMap(name, lastValue);
+            }
+        }
+    }
+
+    @Override
+    public void exitAgType(AgTypeContext ctx) {
+        if (objectStack.empty()) {
+            this.rootObject = this.lastValue;
+            return;
+        }
+        this.rootObject = popObjectStack();
+    }
+
+    @Override
+    public void enterTypeAnnotation(TypeAnnotationContext ctx) {
+        annotationMap.push(ctx.IDENT().getText());
+    }
+
+    @Override
+    public void exitTypeAnnotation(TypeAnnotationContext ctx) {
+        String annotation = annotationMap.pop();
+        Object currentObject = peekObjectStack();
+        if (currentObject instanceof UnrecognizedObject) {
+            ((UnrecognizedObject) currentObject).setAnnotation(annotation);
+        }
+    }
+
+    private String identString(String quotesString) {
+        return StringEscapeUtils.unescapeJson(quotesString.substring(1, 
quotesString.length() - 1));
+    }
+
+    public Object getOutput() {
+        return this.rootObject;
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeUtil.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeUtil.java
new file mode 100644
index 0000000..806aa2e
--- /dev/null
+++ b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/AgtypeUtil.java
@@ -0,0 +1,345 @@
+package org.apache.age.jdbc.base;
+
+import java.util.StringJoiner;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.TokenStream;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.apache.age.jdbc.antlr4.AgtypeLexer;
+import org.apache.age.jdbc.antlr4.AgtypeParser;
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeListBuilder;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+import org.apache.age.jdbc.base.type.AgtypeMapBuilder;
+import org.apache.commons.text.StringEscapeUtils;
+
+/**
+ * A set of utility methods to assist in using Agtype.
+ */
+public class AgtypeUtil {
+
+    private static BaseErrorListener baseErrorListener = new 
BaseErrorListener() {
+        @Override
+        public void syntaxError(Recognizer<?, ?> recognizer, Object 
offendingSymbol, int line,
+            int charPositionInLine, String msg, RecognitionException e) {
+            throw new IllegalStateException(
+                "Failed to parse at line " + line + " due to " + msg, e);
+        }
+    };
+
+    /**
+     * Do not instantiate AgtypeUtil, all methods are static.
+     */
+    private AgtypeUtil() {
+    }
+
+    /**
+     * Returns to object as a double, if it is a valid double, otherwise will 
throw an exception.
+     * <br><br>
+     * Rules for converting from other types
+     * <ul>
+     *   <li>Agtype null - Converted to 0.0</li>
+     *   <li>true - Converted to 1.0</li>
+     *   <li>false - Converted to 0.0</li>
+     *   <li>Integer/Long - Converted to it's double value. Follows
+     *   <a 
href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.2";> 
Oracle's
+     *   specifications </a>for widening primitives.</li>
+     *   <li>Strings - Parsed to its double value, if it cannot be converted 
to a double, a
+     *   NumberFormatException will be thrown</li>
+     *   <li>{@link AgtypeList}/{@link AgtypeMap} - Throws 
InvalidAgtypeException</li>
+     *   <li>All other values will throw an InvalidAgtypeException</li>
+     * </ul>
+     *
+     * @param obj Object to parse
+     * @return Object as a double
+     * @throws NumberFormatException  if the given object is a number or a 
string that cannot be
+     *                                parsed to a double
+     * @throws InvalidAgtypeException if the given object is not a number or 
string and cannot be
+     *                                converted to a double
+     */
+    public static double getDouble(Object obj)
+        throws NumberFormatException, InvalidAgtypeException {
+        if (obj == null) {
+            return 0.0;
+        } else if (obj instanceof Double) {
+            return (Double) obj;
+        } else if (obj instanceof Boolean) {
+            return (Boolean) obj ? 1.0 : 0.0;
+        } else if (obj instanceof String) {
+            return Double.parseDouble((String) obj);
+        } else if (obj instanceof Long) {
+            return ((Long) obj).doubleValue();
+        }
+
+        throw new InvalidAgtypeException("Not a double: " + obj);
+    }
+
+    /**
+     * Returns the object as an integer, if it is a valid integer, otherwise 
will throw an
+     * exception.
+     * <br><br>
+     * Rules for converting from other types:
+     * <ul>
+     *   <li>null - Converted to 0</li>
+     *   <li>true - Converted to 1</li>
+     *   <li>false - Converted to 0</li>
+     *   <li>Double - Converted to it's integer value. Follows
+     *   <a 
href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3";> 
Oracle's
+     *   specifications </a>for widening primitives.</li>
+     *   <li>Strings - Parsed to its integer value, an Exception will be 
thrown if its not an
+     *   integer</li>
+     *   <li>All other values will throw an InvalidAgtypeException</li>
+     * </ul>
+     *
+     * @param obj Object to parse
+     * @return Object as an int
+     * @throws NumberFormatException  if the given object is a number or 
string that cannot be
+     *                                parsed into an Integer
+     * @throws InvalidAgtypeException if the given object is not a number of a 
string
+     */
+    public static int getInt(Object obj) throws NumberFormatException, 
InvalidAgtypeException {
+        long l;
+        try {
+            l = getLong(obj);
+            if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) {
+                return (int) l;
+            }
+        } catch (InvalidAgtypeException ex) {
+            throw new InvalidAgtypeException("Not a int: " + obj, ex);
+        }
+        throw new NumberFormatException("Bad value for type int: " + l);
+    }
+
+    /**
+     * Returns to object as a long, if it is a valid long, otherwise will 
throw an exception.
+     * <br><br>
+     * Rules for converting from other types:
+     * <ul>
+     *   <li>null - Converted to 0</li>
+     *   <li>true - Converted to 1</li>
+     *   <li>false - Converted to 0</li>
+     *   <li>Double - Converted to it's integer value. Follows
+     *   <a 
href="https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3";> 
Oracle's
+     *   specifications </a>for widening primitives.</li>
+     *   <li>Strings - Parsed to its integer value, an Exception will be 
thrown if its not an
+     *   integer</li>
+     *   <li>All other values will throw an InvalidAgtypeException</li>
+     * </ul>
+     *
+     * @param obj Object to parse
+     * @return Object as an long
+     * @throws InvalidAgtypeException if the object cannot be converted to a 
Long
+     * @throws NumberFormatException  if the given object is a number or 
string that cannot be
+     *                                parsed into a double
+     */
+    public static long getLong(Object obj) throws NumberFormatException, 
InvalidAgtypeException {
+        if (obj == null) {
+            return 0;
+        } else if (obj instanceof Long) {
+            return (Long) obj;
+        } else if (obj instanceof String) {
+            return (long) Double.parseDouble((String) obj);
+        } else if (obj instanceof Boolean) {
+            return (boolean) obj ? 1 : 0;
+        } else if (obj instanceof Double) {
+            return ((Double) obj).longValue();
+        }
+
+        throw new InvalidAgtypeException("Not a long: " + obj);
+    }
+
+    /**
+     * Returns to object as a string, if it is a valid string, otherwise will 
throw an exception.
+     * <br><br>
+     * Rules for converting from other types:
+     * <ul>
+     *   <li>null - Returns null</li>
+     *   <li>Long - Returns a String representation of the Long</li>
+     *   <li>Double - Returns a String representation of the double</li>
+     *   <li>Boolean - Returns a String representation of the boolean</li>
+     *   <li>All other values will throw an InvalidAgtypeException</li>
+     * </ul>
+     *
+     * @param obj Object to parse to a String
+     * @return Object as an string
+     * @throws InvalidAgtypeException if the object cannot be converted to a 
String
+     */
+    public static String getString(Object obj) throws InvalidAgtypeException {
+        if (obj == null) {
+            return null;
+        } else if (obj instanceof String) {
+            return (String) obj;
+        } else if (obj instanceof Long) {
+            return Long.toString((long) obj);
+        } else if (obj instanceof Double) {
+            return Double.toString((double) obj);
+        } else if (obj instanceof Boolean) {
+            return Boolean.toString((boolean) obj);
+        }
+
+        throw new InvalidAgtypeException("Not a string: " + obj);
+    }
+
+    /**
+     * Returns to object as a boolean, if it is a valid boolean, otherwise 
will throw an exception.
+     * <br><br>
+     * Data Conversions from other types
+     * <ul>
+     *   <li>null - Returns false</li>
+     *   <li>Long - Returns false is the value is 0, returns true 
otherwise.</li>
+     *   <li>Double - Returns false is the value is 0.0, returns true 
otherwise.</li>
+     *   <li>String - Returns false if the length of the String is 0, returns 
true otherwise.</li>
+     *   <li>
+     *     {@link AgtypeList} - Returns false if the size of the list is 0, 
returns true otherwise.
+     *   </li>
+     *   <li>
+     *     {@link AgtypeMap} - Returns false if the size of the map is 0, 
returns true otherwise.
+     *   </li>
+     *   <li>All other values will throw an InvalidAgtypeException</li>
+     * </ul>
+     *
+     * @param obj Object to parse
+     * @return Object as an boolean
+     * @throws InvalidAgtypeException if the object cannot be converted to a 
boolean
+     */
+    public static boolean getBoolean(Object obj) throws InvalidAgtypeException 
{
+        if (obj == null) {
+            return false;
+        } else if (obj instanceof Boolean) {
+            return (Boolean) obj;
+        } else if (obj instanceof String) {
+            return ((String) obj).length() > 0;
+        } else if (obj instanceof Long) {
+            return (Long) obj != 0L;
+        } else if (obj instanceof Double) {
+            return (Double) obj != 0.0;
+        } else if (obj instanceof AgtypeList) {
+            return ((AgtypeList) obj).size() > 0;
+        } else if (obj instanceof AgtypeMap) {
+            return ((AgtypeMap) obj).size() > 0;
+        }
+        throw new InvalidAgtypeException("Not a valid Agtype: " + obj);
+    }
+
+    /**
+     * Returns to object as an {@link AgtypeList}. If this obj is not an 
AgtypeList, an
+     * InvalidAgtypeException will be thrown.
+     *
+     * @param obj Object to parse and return as an AgtypeList
+     * @return Object as an agTypeArray
+     * @throws InvalidAgtypeException if the object cannot be converted to an 
AgtypeList
+     */
+    public static AgtypeList getList(Object obj) throws InvalidAgtypeException 
{
+        if (obj == null) {
+            return null;
+        } else if (obj instanceof AgtypeList) {
+            return (AgtypeList) obj;
+        }
+
+        throw new InvalidAgtypeException("Not an AgtypeList: " + obj);
+    }
+
+    /**
+     * Returns to object as an {@link AgtypeMap}. If this obj is not an 
AgtypeMap, an
+     * InvalidAgtypeException will be thrown.
+     *
+     * @param obj Object to parse and return as an AgtypeMap
+     * @return Object as an AgtypeMap
+     * @throws InvalidAgtypeException if the object cannot be converted to an 
AgtypeMap
+     */
+    public static AgtypeMap getMap(Object obj) throws InvalidAgtypeException {
+        if (obj == null) {
+            return null;
+        } else if (obj instanceof AgtypeMap) {
+            return (AgtypeMap) obj;
+        }
+
+        throw new InvalidAgtypeException("Not an AgtypeMap: " + obj);
+    }
+
+    /**
+     * Creates a new AgtypeMapBuilder.
+     *
+     * @return Newly created AgtypeMapBuilder
+     */
+    public static AgtypeMapBuilder createMapBuilder() {
+        return new AgtypeMapBuilder();
+    }
+
+    /**
+     * Creates a new AgtypeListBuilder.
+     *
+     * @return Newly created AgtypeListBuilder
+     */
+    public static AgtypeListBuilder createListBuilder() {
+        return new AgtypeListBuilder();
+    }
+
+    /**
+     * Converts a serialized Agtype value into it's non-serialized value.
+     *
+     * @param strAgtype Serialized Agtype value to be parsed.
+     * @return Parsed object that can be stored in {@link Agtype}
+     * @throws IllegalStateException if the value cannot be parsed into an 
Agtype.
+     */
+    public static Object parse(String strAgtype) throws IllegalStateException {
+        CharStream charStream = CharStreams.fromString(strAgtype);
+        AgtypeLexer lexer = new AgtypeLexer(charStream);
+        TokenStream tokens = new CommonTokenStream(lexer);
+        AgtypeParser parser = new AgtypeParser(tokens);
+
+        lexer.removeErrorListeners();
+        lexer.addErrorListener(baseErrorListener);
+
+        parser.removeErrorListeners();
+        parser.addErrorListener(baseErrorListener);
+
+        AgtypeListener agtypeListener = new AgtypeListener();
+
+        ParseTreeWalker walker = new ParseTreeWalker();
+        walker.walk(agtypeListener, parser.agType());
+
+        return agtypeListener.getOutput();
+    }
+
+    /**
+     * Converts the passed object into its serialized Agtype form.
+     *
+     * @param obj Agtype object to convert into its serialized form
+     * @return Serialized Agtype object
+     */
+    static String serializeAgtype(Object obj) {
+        if (obj == null) {
+            return "null";
+        } else if (obj instanceof String) {
+            return '"' + StringEscapeUtils.escapeJson((String) obj) + '"';
+        } else if (obj instanceof AgtypeMap) {
+            StringJoiner join = new StringJoiner(",", "{", "}");
+
+            ((AgtypeMap) obj).entrySet()
+                .stream()
+                .map((entry) -> new StringJoiner(":")
+                    .add(serializeAgtype(entry.getKey()))
+                    .add(serializeAgtype(entry.getValue()))
+                )
+                .forEach(join::merge);
+
+            return join.toString();
+        } else if (obj instanceof AgtypeList) {
+            StringJoiner join = new StringJoiner(",", "[", "]");
+
+            ((AgtypeList) obj)
+                .stream()
+                .map(AgtypeUtil::serializeAgtype)
+                .forEach(join::add);
+
+            return join.toString();
+        }
+
+        return String.valueOf(obj);
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/InvalidAgtypeException.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/InvalidAgtypeException.java
new file mode 100644
index 0000000..2e64995
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/InvalidAgtypeException.java
@@ -0,0 +1,15 @@
+package org.apache.age.jdbc.base;
+
+/**
+ * Runtime exception for when there is an invalid use of Agtype.
+ */
+public class InvalidAgtypeException extends RuntimeException {
+
+    public InvalidAgtypeException(String message) {
+        super(message);
+    }
+
+    public InvalidAgtypeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeAnnotation.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeAnnotation.java
new file mode 100644
index 0000000..70a60d6
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeAnnotation.java
@@ -0,0 +1,6 @@
+package org.apache.age.jdbc.base.type;
+
+public interface AgtypeAnnotation {
+
+    String getAnnotation();
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeList.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeList.java
new file mode 100644
index 0000000..058dd29
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeList.java
@@ -0,0 +1,159 @@
+package org.apache.age.jdbc.base.type;
+
+import java.util.Iterator;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import org.apache.age.jdbc.base.Agtype;
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+
+/**
+ * Non-mutable list of Agtype values.
+ *
+ * @see AgtypeListBuilder
+ * @see Agtype
+ */
+public interface AgtypeList extends AgtypeObject {
+
+    /**
+     * Performs the given action for each element of the Iterable until all 
elements have been
+     * processed or the action throws an exception. Unless otherwise specified 
by the implementing
+     * class, actions are performed in the order of iteration (if an iteration 
order is specified).
+     * Exceptions thrown by the action are relayed to the caller.
+     *
+     * @param action The action to be performed for each element
+     * @throws NullPointerException - if the specified action is null
+     */
+    void forEach(Consumer<? super Object> action);
+
+    /**
+     * Returns the String value at the specified position in this list. Throws 
an
+     * InvalidAgtypeException if the element is not a String.
+     *
+     * @param index index of the element to return
+     * @return the String value at the specified position in this list
+     * @throws InvalidAgtypeException    if the value cannot be converted to a 
String
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getString(Object)
+     */
+    String getString(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the int value at the specified position in this list. Throws an
+     * InvalidAgtypeException if the element is not an int.
+     *
+     * @param index index of the element to return
+     * @return the int value at the specified position in this list
+     * @throws InvalidAgtypeException    if the value cannot be converted to 
an int
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getInt(Object)
+     */
+    int getInt(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the long value at the specified position in this list. Throws an
+     * InvalidAgtypeException if the element is not a long.
+     *
+     * @param index index of the element to return
+     * @return the long value at the specified position in this list
+     * @throws InvalidAgtypeException    if the value cannot be converted to a 
long
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getLong(Object)
+     */
+    long getLong(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the double value at the specified position in this list. Throws 
an
+     * InvalidAgtypeException if the element is not a double.
+     *
+     * @param index index of the element to return
+     * @return the double value at the specified position in this list
+     * @throws InvalidAgtypeException    if the value cannot be converted to a 
double
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getDouble(Object)
+     */
+    double getDouble(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the double value at the specified position in this list. Throws 
an
+     * InvalidAgtypeException if the element is not a double.
+     *
+     * @param index index of the element to return
+     * @return the boolean value at the specified position in this list
+     * @throws InvalidAgtypeException    if the value cannot be converted to a 
boolean
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getBoolean(Object)
+     */
+    boolean getBoolean(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the AgtypeList at the specified position in this list. Throws an
+     * InvalidAgtypeException if the element is not an AgtypeList.
+     *
+     * @param index index of the element to return
+     * @return the AgtypeList at the specified position in this list
+     * @throws InvalidAgtypeException    if the value stored at the index is 
not an AgtypeList
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getList(Object)
+     */
+    AgtypeList getList(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the AgtypeMap at the specified position in this list. Throws an
+     * InvalidAgtypeException if the element is not an AgtypeMap.
+     *
+     * @param index index of the element to return
+     * @return the AgtypeList at the specified position in this list
+     * @throws InvalidAgtypeException    if the value stored at the index is 
not an AgtypeMap
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     * @see AgtypeUtil#getMap(Object)
+     */
+    AgtypeMap getMap(int index) throws InvalidAgtypeException;
+
+    /**
+     * Returns the object at the specified position in this list.
+     *
+     * @param index index of the element to return
+     * @return the object at the specified position in this list
+     * @throws IndexOutOfBoundsException if the index is out of range (index 
{@literal <} 0 || index
+     *                                   {@literal >}= size())
+     */
+    Object getObject(int index);
+
+    /**
+     * Returns an iterator over the elements.
+     *
+     * @return Iterator over the elements
+     */
+    Iterator<Object> iterator();
+
+    /**
+     * Returns the size of this AgtypeList.
+     *
+     * @return the size of this AgtypeList
+     */
+    int size();
+
+    /**
+     * Creates a Spliterator over the elements described by this Iterable.
+     *
+     * @return Spliterator over the elements described by this Iterable
+     */
+    Spliterator<Object> spliterator();
+
+    /**
+     * Returns a sequential Stream with this collection as its source.
+     *
+     * @return a sequential Stream with this collection as its source.
+     */
+    Stream<Object> stream();
+
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeListBuilder.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeListBuilder.java
new file mode 100644
index 0000000..cfdf1a4
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeListBuilder.java
@@ -0,0 +1,151 @@
+package org.apache.age.jdbc.base.type;
+
+import org.apache.age.jdbc.base.Agtype;
+
+/**
+ * A builder for creating an AgtypeList object. This class initializes a 
AgtypeList and provides
+ * methods to add values return the resulting AgtypeList. The methods in this 
class can be chained
+ * to add multiple values to the AgtypeList.
+ *
+ * @see AgtypeList
+ */
+public class AgtypeListBuilder {
+
+    private final AgtypeListImpl agtypeList;
+
+    /**
+     * Initializes an empty List BuilderAgtypeList.
+     */
+    public AgtypeListBuilder() {
+        agtypeList = new AgtypeListImpl();
+    }
+
+    /**
+     * Appends the Agtype value to the AglistBuilder.
+     *
+     * @param value the Agtype value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(Agtype value) {
+        agtypeList.add(value.getObject());
+        return this;
+    }
+
+    /**
+     * Appends the int to the AglistBuilder.
+     *
+     * @param value the int value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(int value) {
+        agtypeList.add((long) value);
+        return this;
+    }
+
+    /**
+     * Appends the String to the AglistBuilder.
+     *
+     * @param value the String value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(String value) {
+        agtypeList.add(value);
+        return this;
+    }
+
+    /**
+     * Appends the double to the AglistBuilder.
+     *
+     * @param value the double value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(double value) {
+        agtypeList.add(value);
+        return this;
+    }
+
+    /**
+     * Appends the long to the AglistBuilder.
+     *
+     * @param value the long value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(long value) {
+        agtypeList.add(value);
+        return this;
+    }
+
+    /**
+     * Appends the boolean to the AglistBuilder.
+     *
+     * @param value the boolean value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(boolean value) {
+        agtypeList.add(value);
+        return this;
+    }
+
+    /**
+     * Appends the AgtypeList to the AglistBuilder.
+     *
+     * @param value the AgtypeList value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(AgtypeList value) {
+        agtypeList.add(value);
+        return this;
+    }
+
+    /**
+     * Appends the AgtypeMap to the AglistBuilder.
+     *
+     * @param value the AgtypeMap value to be added to the end of the list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(AgtypeMap value) {
+        agtypeList.add(value);
+        return this;
+    }
+
+    /**
+     * Appends the AglistBuilder to the AglistBuilder.
+     *
+     * @param value the AgtypeListBuilder value to be added to the end of the 
list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(AgtypeListBuilder value) {
+        agtypeList.add(value.build());
+        return this;
+    }
+
+    /**
+     * Appends the AgmapBuilder to the AglistBuilder.
+     *
+     * @param value the AgtypeMapBuilder value to be added to the end of the 
list
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder add(AgtypeMapBuilder value) {
+        agtypeList.add(value.build());
+        return this;
+    }
+
+    /**
+     * Appends the null to the AglistBuilder.
+     *
+     * @return a reference to this object.
+     */
+    public AgtypeListBuilder addNull() {
+        agtypeList.add(null);
+        return this;
+    }
+
+    /**
+     * Returns the AgtypeList object associated with this object builder.
+     *
+     * @return the AgtypeList object that is being built
+     */
+    public AgtypeList build() {
+        return agtypeList;
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeListImpl.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeListImpl.java
new file mode 100644
index 0000000..281c75f
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeListImpl.java
@@ -0,0 +1,55 @@
+package org.apache.age.jdbc.base.type;
+
+import java.util.ArrayList;
+import java.util.stream.Stream;
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+
+public class AgtypeListImpl extends ArrayList<Object> implements Cloneable,
+    AgtypeList {
+
+    @Override
+    public String getString(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getString(get(index));
+    }
+
+    @Override
+    public int getInt(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getInt(get(index));
+    }
+
+    @Override
+    public long getLong(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getLong(get(index));
+    }
+
+    @Override
+    public double getDouble(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getDouble(get(index));
+    }
+
+    @Override
+    public boolean getBoolean(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getBoolean(get(index));
+    }
+
+    @Override
+    public AgtypeList getList(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getList(get(index));
+    }
+
+    @Override
+    public AgtypeMap getMap(int index) throws InvalidAgtypeException {
+        return AgtypeUtil.getMap(get(index));
+    }
+
+    @Override
+    public Stream<Object> stream() {
+        return super.stream();
+    }
+
+    @Override
+    public Object getObject(int index) {
+        return get(index);
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMap.java 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMap.java
new file mode 100644
index 0000000..043ec1c
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMap.java
@@ -0,0 +1,199 @@
+package org.apache.age.jdbc.base.type;
+
+import java.util.Map;
+import java.util.Set;
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+
+/**
+ * Non-Mutable Map of Agtype values. This implementation provides partial 
implementation of map
+ * operations, and permits null values, but not null keys. This class makes no 
guarantees as to the
+ * order of the map; in particular, it does not guarantee that the order will 
remain constant over
+ * time.
+ *
+ * @see AgtypeMapBuilder
+ */
+public interface AgtypeMap extends AgtypeObject {
+
+    /**
+     * Returns a set of keys.
+     *
+     * @return a set of keys
+     */
+    Set<String> keySet();
+
+    /**
+     * Returns true if the given key is contained.
+     *
+     * @param key the given key
+     * @return true if the given key is contained
+     */
+    boolean containsKey(String key);
+
+    /**
+     * Returns the String value to which the specified key is mapped, or null 
if this AgtypeMap
+     * contains no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the string value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a String
+     * @see AgtypeUtil#getString(Object)
+     */
+    String getString(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the String value to which the specified key is mapped, or 
defaultValue if this
+     * AgtypeMap contains no mapping for the key.
+     *
+     * @param key          the key whose associated value is to be returned
+     * @param defaultValue the default mapping of the key
+     * @return the string value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a String
+     * @see AgtypeUtil#getString(Object)
+     */
+    String getString(String key, String defaultValue) throws 
InvalidAgtypeException;
+
+    /**
+     * Returns the int value to which the specified key is mapped, or 0 if 
this AgtypeMap contains
+     * no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the int value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to an int
+     * @see AgtypeUtil#getInt(Object)
+     */
+    int getInt(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the int value to which the specified key is mapped, or 
defaultValue if this AgtypeMap
+     * contains no mapping for the key.
+     *
+     * @param key          the key whose associated value is to be returned
+     * @param defaultValue the default mapping of the key
+     * @return the int value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to an int
+     * @see AgtypeUtil#getInt(Object)
+     */
+    int getInt(String key, int defaultValue) throws InvalidAgtypeException;
+
+    /**
+     * Returns the long value to which the specified key is mapped, or 0 if 
this AgtypeMap contains
+     * no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the long value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a long
+     * @see AgtypeUtil#getLong(Object)
+     */
+    long getLong(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the long value to which the specified key is mapped, or 
defaultValue if this
+     * AgtypeMap contains no mapping for the key.
+     *
+     * @param key          the key whose associated value is to be returned
+     * @param defaultValue the default mapping of the key
+     * @return the long value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a long
+     * @see AgtypeUtil#getLong(Object)
+     */
+    long getLong(String key, long defaultValue) throws InvalidAgtypeException;
+
+    /**
+     * Returns the double value to which the specified key is mapped, or 0.0 
if this AgtypeMap
+     * contains no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the double value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a double
+     * @see AgtypeUtil#getDouble(Object)
+     */
+    double getDouble(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the double value to which the specified key is mapped, or 
defaultValue if this
+     * AgtypeMap contains no mapping for the key.
+     *
+     * @param key          the key whose associated value is to be returned
+     * @param defaultValue the default mapping of the key
+     * @return the double value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a double
+     * @see AgtypeUtil#getDouble(Object)
+     */
+    double getDouble(String key, double defaultValue) throws 
InvalidAgtypeException;
+
+    /**
+     * Returns the boolean value to which the specified key is mapped, or 
false if this AgtypeMap
+     * contains no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the boolean value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a boolean
+     * @see AgtypeUtil#getBoolean(Object)
+     */
+    boolean getBoolean(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the boolean value to which the specified key is mapped, or 
defaultValue if this
+     * AgtypeMap contains no mapping for the key.
+     *
+     * @param key          the key whose associated value is to be returned
+     * @param defaultValue the default mapping of the key
+     * @return the boolean value stored at the key
+     * @throws InvalidAgtypeException Throws if the value cannot be converted 
to a boolean
+     * @see AgtypeUtil#getBoolean(Object)
+     */
+    boolean getBoolean(String key, boolean defaultValue) throws 
InvalidAgtypeException;
+
+    /**
+     * Returns the AgtypeList value to which the specified key is mapped, or 
null if this AgtypeMap
+     * contains no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the AgtypeList value stored at the key
+     * @throws InvalidAgtypeException Throws if the value is not an AgtypeList
+     * @see AgtypeUtil#getList(Object)
+     */
+    AgtypeList getList(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the AgtypeMap value to which the specified key is mapped, or 
null if this AgtypeMap
+     * contains no mapping for the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the AgtypeMap value stored at the key
+     * @throws InvalidAgtypeException Throws if the value is not a AgtypeMap
+     * @see AgtypeUtil#getMap(Object)
+     */
+    AgtypeMap getMap(String key) throws InvalidAgtypeException;
+
+    /**
+     * Returns the value stored at the key.
+     *
+     * @param key the key whose associated value is to be returned
+     * @return the object value stored at the key
+     */
+    Object getObject(String key);
+
+    /**
+     * Returns true if the value stored at the key is null.
+     *
+     * @param key the given key
+     * @return true if the value stored at the key is null
+     */
+    boolean isNull(String key);
+
+    /**
+     * Returns the size of this AgtypeMap.
+     *
+     * @return the size of this AgtypeMap
+     */
+    int size();
+
+    /**
+     * Returns a Set view of the mappings contained in this map.
+     *
+     * @return a set view of the mappings contained in this map
+     */
+    Set<Map.Entry<String, Object>> entrySet();
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMapBuilder.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMapBuilder.java
new file mode 100644
index 0000000..d31dd26
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMapBuilder.java
@@ -0,0 +1,171 @@
+package org.apache.age.jdbc.base.type;
+
+import org.apache.age.jdbc.base.Agtype;
+
+/**
+ * A builder for creating an AgtypeMap object. This class initializes a 
AgtypeMap object, provides
+ * methods to add name/value pairs and return the resulting object. The 
methods in this class can be
+ * chained to add multiple name/value pairs to the AgtypeMap.
+ *
+ * @see AgtypeMap
+ * @see Agtype
+ */
+public class AgtypeMapBuilder {
+
+    private final AgtypeMapImpl agtypeMap;
+
+    /**
+     * Initializes an empty AgtypeMap.
+     */
+    public AgtypeMapBuilder() {
+        agtypeMap = new AgtypeMapImpl();
+    }
+
+    /**
+     * Adds the name/int pair to the Agtype object associated with this object 
builder. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * int.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, int value) {
+        agtypeMap.put(name, (long) value);
+        return this;
+    }
+
+    /**
+     * Adds the name/long pair to the Agtype object associated with this 
object builder. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * long.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, long value) {
+        agtypeMap.put(name, value);
+        return this;
+    }
+
+    /**
+     * Adds the name/double pair to the Agtype object associated with this 
object builder. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * double.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, double value) {
+        agtypeMap.put(name, value);
+        return this;
+    }
+
+    /**
+     * Adds the name/String pair to the Agtype object associated with this 
object builder. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * String.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, String value) {
+        agtypeMap.put(name, value);
+        return this;
+    }
+
+    /**
+     * Adds the name/boolean pair to the Agtype object associated with this 
object builder. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * boolean.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, boolean value) {
+        agtypeMap.put(name, value);
+        return this;
+    }
+
+    /**
+     * Adds the name/AgtypeMap pair to the Agtype object associated with this 
object builder. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * AgtypeMap.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, AgtypeMap value) {
+        agtypeMap.put(name, value);
+        return this;
+    }
+
+    /**
+     * Adds the name/AgtypeList pair to the Agtype object associated with this 
object builder. If
+     * the object contains a mapping for the specified name, this method 
replaces the old value with
+     * AgtypeList.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, AgtypeList value) {
+        agtypeMap.put(name, value);
+        return this;
+    }
+
+    /**
+     * Adds the name/AgmapBuilder pair to the Agtype object associated with 
this object builder. If
+     * the object contains a mapping for the specified name, this method 
replaces the old value with
+     * AgtypeMap.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, AgtypeMapBuilder value) {
+        agtypeMap.put(name, value.build());
+        return this;
+    }
+
+    /**
+     * Adds the name/AglistBuilder pair to the Agtype object associated with 
this object builder. If
+     * the object contains a mapping for the specified name, this method 
replaces the old value with
+     * AgtypeList.
+     *
+     * @param name  name in the name/value pair
+     * @param value the value is the object associated with this builder
+     * @return this object builder
+     */
+    public AgtypeMapBuilder add(String name, AgtypeListBuilder value) {
+        agtypeMap.put(name, value.build());
+        return this;
+    }
+
+    /**
+     * Adds null to the Agtype object associated with this object builder at 
the given name. If the
+     * object contains a mapping for the specified name, this method replaces 
the old value with
+     * AgtypeList.
+     *
+     * @param name Name where null is to be placed
+     * @return this object builder
+     */
+    public AgtypeMapBuilder addNull(String name) {
+        agtypeMap.put(name, null);
+        return this;
+    }
+
+    /**
+     * Returns the AgtypeMap object associated with this object builder.
+     *
+     * @return the AgtypeMap object that is being built
+     */
+    public AgtypeMap build() {
+        return agtypeMap;
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMapImpl.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMapImpl.java
new file mode 100644
index 0000000..b8e9229
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeMapImpl.java
@@ -0,0 +1,95 @@
+package org.apache.age.jdbc.base.type;
+
+import java.util.HashMap;
+import java.util.Set;
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+
+public class AgtypeMapImpl extends HashMap<String, Object> implements 
Cloneable,
+    AgtypeMap {
+
+    @Override
+    public Set<Entry<String, Object>> entrySet() {
+        return super.entrySet();
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return super.keySet();
+    }
+
+    @Override
+    public boolean containsKey(String key) {
+        return super.containsKey(key);
+    }
+
+    @Override
+    public String getString(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getString(get(key));
+    }
+
+    @Override
+    public String getString(String key, String defaultValue) throws 
InvalidAgtypeException {
+        return containsKey(key) ? getString(key) : defaultValue;
+    }
+
+    @Override
+    public int getInt(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getInt(get(key));
+    }
+
+    @Override
+    public int getInt(String key, int defaultValue) throws 
InvalidAgtypeException {
+        return containsKey(key) ? getInt(key) : defaultValue;
+    }
+
+    @Override
+    public long getLong(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getLong(get(key));
+    }
+
+    @Override
+    public long getLong(String key, long defaultValue) throws 
InvalidAgtypeException {
+        return containsKey(key) ? getLong(key) : defaultValue;
+    }
+
+    @Override
+    public double getDouble(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getDouble(get(key));
+    }
+
+    @Override
+    public double getDouble(String key, double defaultValue) throws 
InvalidAgtypeException {
+        return containsKey(key) ? getDouble(key) : defaultValue;
+    }
+
+    @Override
+    public boolean getBoolean(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getBoolean(get(key));
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defaultValue) throws 
InvalidAgtypeException {
+        return containsKey(key) ? getBoolean(key) : defaultValue;
+    }
+
+    @Override
+    public AgtypeList getList(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getList(get(key));
+    }
+
+    @Override
+    public AgtypeMap getMap(String key) throws InvalidAgtypeException {
+        return AgtypeUtil.getMap(get(key));
+    }
+
+    @Override
+    public Object getObject(String key) {
+        return get(key);
+    }
+
+    @Override
+    public boolean isNull(String key) {
+        return get(key) == null;
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeObject.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeObject.java
new file mode 100644
index 0000000..ed8176f
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/AgtypeObject.java
@@ -0,0 +1,5 @@
+package org.apache.age.jdbc.base.type;
+
+public interface AgtypeObject {
+
+}
diff --git 
a/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/UnrecognizedObject.java
 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/UnrecognizedObject.java
new file mode 100644
index 0000000..aca0eb0
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/main/java/org/apache/age/jdbc/base/type/UnrecognizedObject.java
@@ -0,0 +1,6 @@
+package org.apache.age.jdbc.base.type;
+
+public interface UnrecognizedObject {
+
+    void setAnnotation(String annotation);
+}
diff --git 
a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeFactoryTest.java 
b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeFactoryTest.java
new file mode 100644
index 0000000..dda78f1
--- /dev/null
+++ b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeFactoryTest.java
@@ -0,0 +1,106 @@
+package org.apache.age.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.age.jdbc.base.AgtypeFactory;
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+import org.junit.jupiter.api.Test;
+
+class AgtypeFactoryTest {
+
+  @Test
+  void agTypeInvalidTypes() {
+    //float
+    assertThrows(InvalidAgtypeException.class,
+        () -> AgtypeFactory.create(Float.parseFloat("3.14")));
+    //char
+    assertThrows(InvalidAgtypeException.class, () -> 
AgtypeFactory.create('c'));
+    //Object
+    assertThrows(InvalidAgtypeException.class, () -> AgtypeFactory.create(new 
Object()));
+    //StringBuilder
+    assertThrows(InvalidAgtypeException.class,
+        () -> AgtypeFactory.create(new StringBuilder().append("Hello")));
+  }
+
+  @Test
+  void agTypeFactoryGetInteger() throws InvalidAgtypeException {
+    assertTrue(AgtypeFactory.create(1).getObject() instanceof Long);
+
+    assertEquals(Integer.MAX_VALUE, AgtypeFactory.create(2147483647).getInt());
+    assertEquals(Integer.MIN_VALUE, 
AgtypeFactory.create(-2147483648).getInt());
+    assertThrows(NumberFormatException.class, () -> 
AgtypeFactory.create(Long.MAX_VALUE).getInt());
+  }
+
+  @Test
+  void agTypeFactoryGetLong() throws InvalidAgtypeException {
+    assertTrue(AgtypeFactory.create(1L).getObject() instanceof Long);
+
+    assertEquals(Long.MAX_VALUE, 
AgtypeFactory.create(9223372036854775807L).getLong());
+    assertEquals(Long.MIN_VALUE, 
AgtypeFactory.create(-9223372036854775808L).getLong());
+    assertEquals(-0L, AgtypeFactory.create(-0).getLong());
+  }
+
+  @Test
+  void agTypeFactoryDouble() throws InvalidAgtypeException {
+    assertTrue(AgtypeFactory.create(1.0).getObject() instanceof Double);
+
+    assertEquals(Math.PI, AgtypeFactory.create(Math.PI).getDouble());
+    assertEquals(Double.POSITIVE_INFINITY,
+            AgtypeFactory.create(Double.POSITIVE_INFINITY).getDouble());
+    assertEquals(Double.NEGATIVE_INFINITY,
+            AgtypeFactory.create(Double.NEGATIVE_INFINITY).getDouble());
+    assertEquals(Double.NaN, AgtypeFactory.create(Double.NaN).getDouble());
+  }
+
+  @Test
+  void agTypeFactoryString() throws InvalidAgtypeException {
+    assertTrue(AgtypeFactory.create("Hello World").getObject() instanceof 
String);
+
+    assertEquals("Hello World", AgtypeFactory.create("Hello 
World").getString());
+    assertEquals("\n", AgtypeFactory.create("\n").getString());
+    assertEquals("\t", AgtypeFactory.create("\t").getString());
+    assertEquals("\b", AgtypeFactory.create("\b").getString());
+    assertEquals("\f", AgtypeFactory.create("\f").getString());
+    assertEquals("\r", AgtypeFactory.create("\r").getString());
+    assertEquals("\\", AgtypeFactory.create("\\").getString());
+    assertEquals("/", AgtypeFactory.create("/").getString());
+    assertEquals("\t", AgtypeFactory.create("\t").getString());
+    //GREEK CAPITAL LETTER OMEGA, U+03A9
+    assertEquals("Ω", AgtypeFactory.create("\u03A9").getString());
+    //MATHEMATICAL ITALIC CAPITAL OMICRON, U+1D6F0
+    assertEquals("\uD835\uDEF0", 
AgtypeFactory.create("\ud835\uDEF0").getString());
+
+  }
+
+  @Test
+  void agTypeFactoryBoolean() throws InvalidAgtypeException {
+    assertTrue(AgtypeFactory.create(true).getObject() instanceof Boolean);
+
+    assertTrue(AgtypeFactory.create(true).getBoolean());
+    assertFalse(AgtypeFactory.create(false).getBoolean());
+  }
+
+  @Test
+  void agTypeFactoryMap() throws InvalidAgtypeException {
+    AgtypeMap map = AgtypeUtil.createMapBuilder().add("key","value").build();
+
+    assertTrue(AgtypeFactory.create(map).getObject() instanceof AgtypeMap);
+
+    assertEquals("value", AgtypeFactory.create(map).getMap().getString("key"));
+  }
+
+  @Test
+  void agTypeFactoryList() throws InvalidAgtypeException {
+    AgtypeList list = AgtypeUtil.createListBuilder().add("value").build();
+
+    assertTrue(AgtypeFactory.create(list).getObject() instanceof AgtypeList);
+
+    assertEquals("value", AgtypeFactory.create(list).getList().getString(0));
+  }
+}
diff --git 
a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java 
b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java
new file mode 100644
index 0000000..597be82
--- /dev/null
+++ 
b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeStatementTest.java
@@ -0,0 +1,183 @@
+package org.apache.age.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.apache.age.jdbc.base.Agtype;
+import org.apache.age.jdbc.base.AgtypeFactory;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.postgresql.jdbc.PgConnection;
+
+/**
+ * Tests the different combinations that are possible when running Statements 
and Prepared
+ * Statements with the AgType and shows how the JDBC needs to be setup to 
convert values to the
+ * AgType.
+ */
+class AgtypeStatementTest {
+
+    BaseDockerizedTest baseDockerizedTest = new BaseDockerizedTest();
+
+    @BeforeEach
+    public void setup() throws Exception {
+        baseDockerizedTest.beforeAll();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        baseDockerizedTest.afterAll();
+    }
+
+    /**
+     * When a statement is run first, "ag_catalog"."agtype" needs to be added 
to the connection.
+     *
+     * @throws SQLException Throws an SQL Exepction if the driver is unable to 
parse Agtype.
+     */
+    @Test
+    void agTypeInStatementAsString() throws SQLException, 
InvalidAgtypeException {
+        
baseDockerizedTest.getConnection().addDataType("\"ag_catalog\".\"agtype\"", 
Agtype.class);
+        //Step 1: Run a statement
+        runStatementString(baseDockerizedTest.getConnection());
+    }
+
+    /**
+     * When a Prepared statement is run first and the agtype is a parameter, 
agtype needs to be
+     * added to the connection.
+     *
+     * @throws SQLException Throws an SQL Exepction if the driver is unable to 
parse Agtype.
+     */
+    @Test
+    void asTypeInPreparedStatementAsParameter() throws SQLException, 
InvalidAgtypeException {
+        baseDockerizedTest.getConnection().addDataType("agtype", Agtype.class);
+        //Step 1: Run a Prepared Statement
+        runPreparedStatementParameter(baseDockerizedTest.getConnection());
+    }
+
+    /**
+     * When a Prepared statement is run first and the agtype is not a 
parameter, but in the string,
+     * "ag_catalog"."agtype" needs to be added to the connection.
+     *
+     * @throws SQLException Throws an SQL Exepction if the driver is unable to 
parse Agtype.
+     */
+    @Test
+    void asTypeInPreparedStatementAsString() throws SQLException, 
InvalidAgtypeException {
+        
baseDockerizedTest.getConnection().addDataType("\"ag_catalog\".\"agtype\"", 
Agtype.class);
+
+        runPreparedStatementString(baseDockerizedTest.getConnection());
+    }
+
+    /**
+     * When a Prepared statement is run and agType is both a string and a 
parameter, agtype needs to
+     * be added to the connection, but "ag_catalog."agtype" does not need to 
be added.
+     *
+     * @throws SQLException Throws an SQL Exepction if the driver is unable to 
parse Agtype.
+     */
+    @Test
+    void agTypeInPreparedStatementAsStringAndParam() throws SQLException, 
InvalidAgtypeException {
+
+        baseDockerizedTest.getConnection().addDataType("agtype", Agtype.class);
+
+        //Step 1 Run a Prepared Statement when AgType is a String and a 
Parameter.
+        
runPreparedStatementParameterAndString(baseDockerizedTest.getConnection());
+
+    }
+
+    /**
+     * When a statement is run first, "ag_catalog"."agType" needs to be added 
to the connection, no
+     * need to add agtype for running a Prepared Statement afterward.
+     *
+     * @throws SQLException Throws an SQL Exepction if the driver is unable to 
parse Agtype.
+     */
+    @Test
+    void asTypeInStatementThenPreparedStatement() throws SQLException, 
InvalidAgtypeException {
+        
baseDockerizedTest.getConnection().addDataType("\"ag_catalog\".\"agtype\"", 
Agtype.class);
+        //Step 1: Run a statement
+        runStatementString(baseDockerizedTest.getConnection());
+        //Step 2: Run a Prepared Statement, where AgType is a parameter
+        runPreparedStatementParameter(baseDockerizedTest.getConnection());
+    }
+
+    /**
+     * When a Prepared statement is run first, agtype needs to be added to the 
connection, no need
+     * to add "ag_catalog"."agType" for running a Statement afterward.
+     *
+     * @throws SQLException Throws an SQL Exepction if the driver is unable to 
parse Agtype.
+     */
+    @Test
+    void asTypeInPreparedStatementThenStatement() throws SQLException, 
InvalidAgtypeException {
+        //Add the agtype Data Type.
+        baseDockerizedTest.getConnection().addDataType("agtype", Agtype.class);
+        //Step 1: Run a Prepared Statement
+        runPreparedStatementParameter(baseDockerizedTest.getConnection());
+        //Step 2: Run a Statement
+        runStatementString(baseDockerizedTest.getConnection());
+    }
+
+    /*
+     *     Helper Methods
+     */
+    private void runStatementString(PgConnection conn) throws SQLException, 
InvalidAgtypeException {
+        ResultSet rs;
+        Statement stmt = conn.createStatement();
+        stmt.execute("SELECT '1'::ag_catalog.agtype");
+        rs = stmt.getResultSet();
+        assertTrue(rs.next());
+        Agtype returnedAgtype = (Agtype) rs.getObject(1);
+        assertEquals(1, returnedAgtype.getInt());
+    }
+
+    private void runPreparedStatementParameter(PgConnection conn) throws 
SQLException,
+        InvalidAgtypeException {
+        PreparedStatement ps = conn.prepareStatement("SELECT ?");
+
+        ps.setObject(1, AgtypeFactory.create(1));
+
+        ps.executeQuery();
+        ResultSet rs = ps.getResultSet();
+
+        assertTrue(rs.next());
+
+        Agtype returnedAgtype = (Agtype) rs.getObject(1);
+        assertEquals(1, returnedAgtype.getInt());
+    }
+
+    private void runPreparedStatementParameterAndString(PgConnection conn) 
throws SQLException,
+        InvalidAgtypeException {
+        PreparedStatement ps = conn
+            .prepareStatement("SELECT ?, '1'::ag_catalog.agtype");
+        Agtype agType = new Agtype();
+
+        agType.setValue("1");
+        ps.setObject(1, agType);
+
+        ps.executeQuery();
+        ResultSet rs = ps.getResultSet();
+        assertTrue(rs.next());
+
+        Agtype returnedAgtype = (Agtype) rs.getObject(1);
+        assertEquals(1, returnedAgtype.getInt());
+
+        returnedAgtype = (Agtype) rs.getObject(2);
+        assertEquals(1, returnedAgtype.getInt());
+    }
+
+    private void runPreparedStatementString(PgConnection conn) throws 
SQLException,
+        InvalidAgtypeException {
+        PreparedStatement ps = conn
+            .prepareStatement("SELECT ?, '1'::ag_catalog.agtype");
+
+        ps.setInt(1, 1);
+        ps.executeQuery();
+        ResultSet rs = ps.getResultSet();
+        assertTrue(rs.next());
+        Agtype returnedAgtype = (Agtype) rs.getObject(2);
+
+        assertEquals(1, returnedAgtype.getInt());
+    }
+}
diff --git a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeTest.java 
b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeTest.java
new file mode 100644
index 0000000..95b5411
--- /dev/null
+++ b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeTest.java
@@ -0,0 +1,416 @@
+package org.apache.age.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import org.apache.age.jdbc.base.Agtype;
+import org.apache.age.jdbc.base.AgtypeFactory;
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.InvalidAgtypeException;
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+import org.junit.jupiter.api.Test;
+
+class AgtypeTest extends BaseDockerizedTest {
+
+    private Agtype getAgType(Object fieldValue) throws SQLException {
+        Statement stmt = getConnection().createStatement();
+        String str = "SELECT '" + AgtypeFactory.create(fieldValue).getValue() 
+ "'::agtype;";
+        ResultSet rs = stmt.executeQuery(str);
+        assertTrue(rs.next());
+        return (Agtype) rs.getObject(1);
+    }
+
+    /*
+      Cypher Return Statement should be SQL Null, not Agtype Null
+    */
+    @Test
+    void agTypeCypherReturnNull() throws SQLException {
+        Statement stmt = getConnection().createStatement();
+        String str = "SELECT i from cypher('cypher', $$RETURN null$$) as t(i 
agtype);";
+        ResultSet rs = stmt.executeQuery(str);
+        assertTrue(rs.next());
+        assertNull(rs.getObject(1));
+    }
+
+    /*
+      Get String Unit Tests
+    */
+    @Test
+    void agTypeGetString() throws SQLException, InvalidAgtypeException {
+        assertEquals("Hello World", getAgType("Hello World").getString());
+        assertEquals("\n", getAgType("\n").getString());
+        assertEquals("\b", getAgType("\b").getString());
+        assertEquals("\f", getAgType("\f").getString());
+        assertEquals("\r", getAgType("\r").getString());
+        assertEquals("\\", getAgType("\\").getString());
+        assertEquals("/", getAgType("/").getString());
+        assertEquals("\t", getAgType("\t").getString());
+        //GREEK CAPITAL LETTER OMEGA, U+03A9
+        assertEquals("Ω", getAgType("\u03A9").getString());
+        //MATHEMATICAL ITALIC CAPITAL OMICRON, U+1D6F0
+        assertEquals("\uD835\uDEF0", getAgType("\ud835\uDEF0").getString());
+    }
+
+    @Test
+    void agTypeGetStringConvertAgtypeNull() throws SQLException, 
InvalidAgtypeException {
+        assertNull(getAgType(null).getString());
+    }
+
+    @Test
+    void agTypeGetStringConvertInt() throws SQLException, 
InvalidAgtypeException {
+        assertEquals("1", getAgType(1).getString());
+    }
+
+    @Test
+    void agTypeGetStringConvertBoolean() throws SQLException, 
InvalidAgtypeException {
+        assertEquals("true", getAgType(true).getString());
+        assertEquals("false", getAgType(false).getString());
+    }
+
+    @Test
+    void agTypeGetStringConvertDouble() throws SQLException, 
InvalidAgtypeException {
+        assertEquals("3.141592653589793", 
getAgType(3.141592653589793).getString());
+        assertEquals("Infinity", 
getAgType(Double.POSITIVE_INFINITY).getString());
+        assertEquals("-Infinity", 
getAgType(Double.NEGATIVE_INFINITY).getString());
+    }
+
+    @Test
+    void agTypeGetStringConvertMap() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> 
getAgType(AgtypeUtil.createMapBuilder().build()).getString());
+    }
+
+    @Test
+    void agTypeGetStringConvertList() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> 
getAgType(AgtypeUtil.createListBuilder().build()).getString());
+    }
+
+    /*
+      Get Integer Unit Tests
+     */
+    @Test
+    void agTypeGetInteger() throws SQLException, InvalidAgtypeException {
+        //Agtype is made in SELECT clause
+        assertEquals(Integer.MAX_VALUE, getAgType(2147483647).getInt());
+        assertEquals(Integer.MIN_VALUE, getAgType(-2147483648).getInt());
+        assertThrows(NumberFormatException.class, () -> 
getAgType(2147483648L).getInt());
+        assertThrows(NumberFormatException.class, () -> 
getAgType(-2147483649L).getInt());
+    }
+
+    @Test
+    void agTypeGetIntConvertAgtypeNull() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(0, getAgType(null).getInt());
+    }
+
+    @Test
+    void agTypeGetIntConvertString() throws SQLException, 
InvalidAgtypeException {
+        assertThrows(NumberFormatException.class, () -> getAgType("Not A 
Number").getInt());
+        assertEquals(1, getAgType("1").getInt());
+        assertEquals(1, getAgType("1.1").getInt());
+    }
+
+    @Test
+    void agTypeGetIntConvertDouble() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(1, getAgType(1.1).getInt());
+    }
+
+    @Test
+    void agTypeGetIntConvertBoolean() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(1, getAgType(true).getInt());
+        assertEquals(0, getAgType(false).getInt());
+    }
+
+    @Test
+    void agTypeGetIntConvertMap() {
+        assertThrows(InvalidAgtypeException.class, () ->
+            getAgType(AgtypeUtil.createMapBuilder().build()).getInt());
+    }
+
+    @Test
+    void agTypeGetIntConvertList() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> getAgType(AgtypeUtil.createListBuilder().build()).getInt());
+    }
+
+    /*
+      Get Long Unit Tests
+     */
+    @Test
+    void agTypeGetLong() throws SQLException, InvalidAgtypeException {
+        assertEquals(Long.MAX_VALUE, getAgType(Long.MAX_VALUE).getLong());
+        assertEquals(Long.MIN_VALUE, getAgType(Long.MIN_VALUE).getLong());
+        assertEquals(-0L, getAgType(-0).getLong());
+    }
+
+    @Test
+    void agTypeGetLongConvertAgtypeNull() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(0L, getAgType(null).getLong());
+    }
+
+    @Test
+    void agTypeGetLongConvertString() throws SQLException, 
InvalidAgtypeException {
+        assertThrows(NumberFormatException.class, () -> getAgType("Not a 
Number").getLong());
+        assertEquals(1L, getAgType("1").getLong());
+    }
+
+    @Test
+    void agTypeGetLongConvertDouble() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(3L, getAgType(Math.PI).getLong());
+        assertEquals(1L, getAgType(1.6).getLong());
+    }
+
+    @Test
+    void agTypeGetLongConvertMap() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> getAgType(AgtypeUtil.createMapBuilder().build()).getLong());
+    }
+
+    @Test
+    void agTypeGetLongConvertList() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> getAgType(AgtypeUtil.createListBuilder().build()).getLong());
+    }
+
+    /*
+      Get Double Unit Tests
+     */
+    @Test
+    void agTypeGetDouble() throws SQLException, InvalidAgtypeException {
+        assertEquals(Math.PI, getAgType(Math.PI).getDouble());
+        assertEquals(-Math.PI, getAgType(-Math.PI).getDouble());
+        assertEquals(Double.POSITIVE_INFINITY, 
getAgType(Double.POSITIVE_INFINITY).getDouble());
+        assertEquals(Double.NEGATIVE_INFINITY, 
getAgType(Double.NEGATIVE_INFINITY).getDouble());
+        assertEquals(Double.NaN, getAgType(Double.NaN).getDouble());
+        assertEquals(Double.MIN_NORMAL, 
getAgType(Double.MIN_NORMAL).getDouble());
+        assertEquals(Double.MIN_VALUE, 
getAgType(Double.MIN_VALUE).getDouble());
+        assertEquals(Double.MAX_VALUE, 
getAgType(Double.MAX_VALUE).getDouble());
+    }
+
+    @Test
+    void agTypeGetDoubleConvertAgtypeNull() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(0L, getAgType(null).getDouble());
+    }
+
+    @Test
+    void agTypeGetDoubleConvertString() throws SQLException, 
InvalidAgtypeException {
+        assertThrows(NumberFormatException.class, () -> getAgType("Not a 
Number").getDouble());
+        assertEquals(1.0, getAgType("1").getDouble());
+        assertEquals(1.1, getAgType("1.1").getDouble());
+        assertEquals(1e9, getAgType("1e9").getDouble());
+    }
+
+    @Test
+    void agTypeGetDoubleConvertLong() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(1.0, getAgType(1).getDouble());
+    }
+
+    @Test
+    void agTypeGetDoubleConvertBoolean() throws SQLException, 
InvalidAgtypeException {
+        assertEquals(1.0, getAgType(true).getDouble());
+        assertEquals(0.0, getAgType(false).getDouble());
+    }
+
+    @Test
+    void agTypeGetDoubleConvertMap() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> 
getAgType(AgtypeUtil.createMapBuilder().build()).getDouble());
+    }
+
+    @Test
+    void agTypeGetDoubleConvertList() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> 
getAgType(AgtypeUtil.createListBuilder().build()).getDouble());
+    }
+
+    /*
+      Get Boolean Unit Tests
+     */
+    @Test
+    void agTypeGetBoolean() throws SQLException, InvalidAgtypeException {
+        assertTrue(getAgType(true).getBoolean());
+        assertFalse(getAgType(false).getBoolean());
+    }
+
+    @Test
+    void agTypeGetBooleanConvertAgtypeNull() throws SQLException, 
InvalidAgtypeException {
+        assertFalse(getAgType(null).getBoolean());
+    }
+
+    @Test
+    void agTypeGetBooleanConvertString() throws SQLException, 
InvalidAgtypeException {
+        assertTrue(getAgType("Non-Empty String").getBoolean());
+        assertFalse(getAgType("").getBoolean());
+    }
+
+    @Test
+    void agTypeGetBooleanConvertLong() throws SQLException, 
InvalidAgtypeException {
+        assertFalse(getAgType(0).getBoolean());
+        assertTrue(getAgType(1).getBoolean());
+    }
+
+    @Test
+    void agTypeGetBooleanConvertDouble() throws SQLException, 
InvalidAgtypeException {
+        assertTrue(getAgType(Math.PI).getBoolean());
+        assertFalse(getAgType(0.0).getBoolean());
+    }
+
+    @Test
+    void agTypeGetBooleanConvertMap() throws SQLException, 
InvalidAgtypeException {
+        
assertFalse(getAgType(AgtypeUtil.createMapBuilder().build()).getBoolean());
+        assertTrue(
+            getAgType(AgtypeUtil.createMapBuilder().add("key", 
"hello").build()).getBoolean());
+    }
+
+    @Test
+    void agTypeGetBooleanConvertList() throws SQLException, 
InvalidAgtypeException {
+        
assertFalse(getAgType(AgtypeUtil.createListBuilder().build()).getBoolean());
+        
assertTrue(getAgType(AgtypeUtil.createListBuilder().add("Hello").build()).getBoolean());
+    }
+
+    /*
+      Get Map Unit Tests
+     */
+    @Test
+    void agTypeGetMap() throws SQLException, InvalidAgtypeException {
+        AgtypeMap agtypeMap = AgtypeUtil.createMapBuilder()
+            .add("i", 1)
+            .add("f", 3.14)
+            .add("s", "Hello World")
+            .add("m", AgtypeUtil.createMapBuilder().add("i", 1))
+            .add("l", AgtypeUtil.createListBuilder().add(1).add(2).add(3))
+            .add("bt", true)
+            .add("bf", false)
+            .addNull("z")
+            .add("pinf", Double.POSITIVE_INFINITY)
+            .add("ninf", Double.NEGATIVE_INFINITY)
+            .add("n", Double.NaN)
+            .build();
+
+        AgtypeMap m = getAgType(agtypeMap).getMap();
+
+        assertEquals(1L, m.getLong("i"));
+        assertEquals(3.14, m.getDouble("f"));
+        assertEquals("Hello World", m.getString("s"));
+        assertTrue(m.getBoolean("bt"));
+        assertFalse(m.getBoolean("bf"));
+        assertTrue(m.isNull("z"));
+        assertEquals(Double.POSITIVE_INFINITY, m.getDouble("pinf"));
+        assertTrue(Double.isNaN(m.getDouble("n")));
+        assertEquals(Double.NEGATIVE_INFINITY, m.getDouble("ninf"));
+
+        AgtypeMap subMap = m.getMap("m");
+        assertEquals(1, subMap.getInt("i"));
+
+        AgtypeList list = m.getList("l");
+        for (int i = 0; i < list.size(); i++) {
+            assertEquals(i + 1, list.getLong(i));
+        }
+    }
+
+    @Test
+    void agTypeGetMapConvertAgtypeNull() {
+        assertDoesNotThrow(() -> getAgType(null).getMap());
+    }
+
+    @Test
+    void agTypeGetMapConvertString() {
+        assertThrows(InvalidAgtypeException.class, () -> getAgType("Non-Empty 
String").getMap());
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType("").getMap());
+    }
+
+    @Test
+    void agTypeGetMapConvertLong() {
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(0L).getMap());
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(1L).getMap());
+    }
+
+    @Test
+    void agTypeGetMapConvertDouble() {
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(Math.PI).getMap());
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(0.0).getMap());
+    }
+
+    @Test
+    void agTypeGetMapConvertList() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> getAgType(AgtypeUtil.createListBuilder().build()).getMap());
+        assertThrows(InvalidAgtypeException.class,
+            () -> 
getAgType(AgtypeUtil.createListBuilder().add("Hello").build()).getMap());
+    }
+
+    /*
+      Get List Unit Tests
+     */
+    @Test
+    void agTypeGetList() throws SQLException, InvalidAgtypeException {
+        AgtypeList agArray = AgtypeUtil.createListBuilder()
+            .add(1)
+            .add("Hello World")
+            .add(3.14)
+            .add(AgtypeUtil.createMapBuilder().add("key0", 1))
+            .add(AgtypeUtil.createListBuilder().add(1).add(2))
+            .add(true)
+            .add(false)
+            .addNull()
+            .add(Double.NaN)
+            .add(Double.POSITIVE_INFINITY)
+            .add(Double.NEGATIVE_INFINITY)
+            .build();
+
+        AgtypeList l = getAgType(agArray).getList();
+
+        assertEquals("Hello World", l.getString(1));
+        assertEquals(1, l.getInt(0));
+        assertEquals(1L, l.getLong(0));
+        assertEquals(3.14, l.getDouble(2));
+        assertEquals(Double.NaN, l.getDouble(8));
+        assertEquals(Double.POSITIVE_INFINITY, l.getDouble(9));
+        assertEquals(Double.NEGATIVE_INFINITY, l.getDouble(10));
+        assertTrue(l.getBoolean(5));
+        assertFalse(l.getBoolean(6));
+        assertNull(l.getObject(7));
+        assertEquals(1L, l.getList(4).getLong(0));
+        assertEquals(2L, l.getList(4).getLong(1));
+        assertEquals(1L, l.getMap(3).getLong("key0"));
+    }
+
+    @Test
+    void agTypeGetListConvertAgtypeNull() throws SQLException {
+        assertNull(getAgType(null).getList());
+    }
+
+    @Test
+    void agTypeGetListConvertString() {
+        assertThrows(InvalidAgtypeException.class, () -> getAgType("Non-Empty 
String").getList());
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType("").getList());
+    }
+
+    @Test
+    void agTypeGetListConvertLong() {
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(0).getList());
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(1).getList());
+    }
+
+    @Test
+    void agTypeGetListConvertDouble() {
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(Math.PI).getList());
+        assertThrows(InvalidAgtypeException.class, () -> 
getAgType(0.0).getList());
+    }
+
+    @Test
+    void agTypeGetListConvertMap() {
+        assertThrows(InvalidAgtypeException.class,
+            () -> getAgType(AgtypeUtil.createMapBuilder().build()).getList());
+        assertThrows(InvalidAgtypeException.class,
+            () -> getAgType(AgtypeUtil.createMapBuilder().add("key", 
"hello").build()).getList());
+    }
+}
diff --git 
a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeUtilTest.java 
b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeUtilTest.java
new file mode 100644
index 0000000..36b5b01
--- /dev/null
+++ b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/AgtypeUtilTest.java
@@ -0,0 +1,127 @@
+package org.apache.age.jdbc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.age.jdbc.base.AgtypeUtil;
+import org.apache.age.jdbc.base.type.AgtypeList;
+import org.apache.age.jdbc.base.type.AgtypeMap;
+import org.apache.commons.text.StringEscapeUtils;
+import org.junit.jupiter.api.Test;
+
+class AgtypeUtilTest {
+
+  @Test
+  void parseEmptyString() {
+    assertEquals("", AgtypeUtil.parse("\"\""));
+  }
+
+  @Test
+  void parseString() {
+    assertEquals("Hello World", AgtypeUtil.parse("\"Hello World\""));
+  }
+
+  @Test
+  void parseEscapedSequences() {
+    assertEquals("\"", AgtypeUtil.parse("\"\\\"\""));
+    assertEquals("\\", AgtypeUtil.parse("\"\\\\\""));
+    assertEquals("/", AgtypeUtil.parse("\"\\/\""));
+    assertEquals("\b", AgtypeUtil.parse("\"\\b\""));
+    assertEquals("\f", AgtypeUtil.parse("\"\\f\""));
+    assertEquals("\n", AgtypeUtil.parse("\"\\n\""));
+    assertEquals("\r", AgtypeUtil.parse("\"\\r\""));
+    assertEquals("\t", AgtypeUtil.parse("\"\\t\""));
+  }
+
+  @Test
+  void parseEscapedUnicodeSequences() {
+    //GREEK CAPITAL LETTER OMEGA, U+03A9
+    assertEquals("Ω",
+        
StringEscapeUtils.unescapeJson((String)AgtypeUtil.parse("\"\\u03A9\"")));
+    //MATHEMATICAL ITALIC CAPITAL OMICRON, U+1D6F0
+    assertEquals("\uD835\uDEF0",
+        
StringEscapeUtils.unescapeJson((String)AgtypeUtil.parse("\"\\ud835\\uDEF0\"")));
+  }
+
+  @Test
+  void parseInvalidStrings() {
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse("\"Hello 
World"));
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse("Hello 
World\""));
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse("\\a"));
+    assertThrows(IllegalStateException.class, () -> 
AgtypeUtil.parse("\\u03A"));
+  }
+
+  @Test
+  void parseInteger() {
+    assertEquals(0x7FFFFFFFFFFFFFFFL, AgtypeUtil.parse("9223372036854775807"));
+    assertEquals(0x8000000000000000L, 
AgtypeUtil.parse("-9223372036854775808"));
+    assertEquals(-0L, AgtypeUtil.parse("-0"));
+  }
+
+  @Test
+  void parseInvalidIntegerValues() {
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse("01"));
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse("00"));
+    assertThrows(NumberFormatException.class, () -> 
AgtypeUtil.parse("9223372036854775808"));
+    assertThrows(NumberFormatException.class, () -> 
AgtypeUtil.parse("-9223372036854775809"));
+  }
+
+  @Test
+  void parseDouble() {
+    assertEquals(Math.PI, AgtypeUtil.parse(Double.toString(Math.PI)));
+    assertEquals(-Math.PI, AgtypeUtil.parse(Double.toString(-Math.PI)));
+    assertEquals(1e09, AgtypeUtil.parse("1e09"));
+    assertEquals(3.14e-1, AgtypeUtil.parse("3.14e-1"));
+    assertEquals(Double.POSITIVE_INFINITY, AgtypeUtil.parse("Infinity"));
+    assertEquals(Double.NEGATIVE_INFINITY, AgtypeUtil.parse("-Infinity"));
+    assertEquals(Double.NaN, AgtypeUtil.parse("NaN"));
+  }
+
+  @Test
+  void parseInvalidFloatValues() {
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse("1."));
+    assertThrows(IllegalStateException.class, () -> AgtypeUtil.parse(".1"));
+  }
+
+  @Test
+  void parseFalseBoolean() {
+    assertFalse((Boolean) AgtypeUtil.parse("false"));
+  }
+
+  @Test
+  void parseTrueBoolean() {
+    assertTrue((Boolean) AgtypeUtil.parse("true"));
+  }
+
+  @Test
+  void parseNull() {
+    assertNull(AgtypeUtil.parse("null"));
+  }
+
+  @Test
+  void parseEmptyArray() {
+    AgtypeList agArray = (AgtypeList) AgtypeUtil.parse("[]");
+    assertEquals(0, agArray.size());
+  }
+
+  @Test
+  void parseArray() {
+    AgtypeList agArray = (AgtypeList) AgtypeUtil.parse("[1]");
+    assertEquals(1, agArray.size());
+  }
+
+  @Test
+  void parseObject() {
+    AgtypeMap agObject = (AgtypeMap) AgtypeUtil.parse("{\"i\":1}");
+    assertEquals(1, agObject.size());
+  }
+
+  @Test
+  void parseEmptyObject() {
+    AgtypeMap agObject = (AgtypeMap) AgtypeUtil.parse("{}");
+    assertEquals(0, agObject.size());
+  }
+}
diff --git 
a/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java 
b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java
new file mode 100644
index 0000000..25776ea
--- /dev/null
+++ b/drivers/jdbc/lib/src/test/java/org/apache/age/jdbc/BaseDockerizedTest.java
@@ -0,0 +1,54 @@
+package org.apache.age.jdbc;
+
+import java.sql.DriverManager;
+import java.sql.Statement;
+import org.apache.age.jdbc.base.Agtype;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.postgresql.jdbc.PgConnection;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.utility.DockerImageName;
+
+@TestInstance(Lifecycle.PER_CLASS)
+public class BaseDockerizedTest {
+
+    private PgConnection connection;
+    private GenericContainer<?> agensGraphContainer;
+
+
+    public PgConnection getConnection() {
+        return connection;
+    }
+
+    @AfterAll
+    public void afterAll() throws Exception {
+        connection.close();
+        agensGraphContainer.stop();
+    }
+
+    @BeforeAll
+    public void beforeAll() throws Exception {
+        String CORRECT_DB_PASSWORDS = "postgres";
+        agensGraphContainer = new GenericContainer<>(DockerImageName
+            .parse("sorrell/agensgraph-extension-alpine:latest"))
+            .withEnv("POSTGRES_PASSWORD", CORRECT_DB_PASSWORDS)
+            .withExposedPorts(5432);
+        agensGraphContainer.start();
+
+        int mappedPort = agensGraphContainer.getMappedPort(5432);
+        String jdbcUrl = String
+            .format("jdbc:postgresql://%s:%d/%s", "localhost", mappedPort, 
"postgres");
+
+        connection = DriverManager.getConnection(jdbcUrl, "postgres", 
CORRECT_DB_PASSWORDS)
+            .unwrap(PgConnection.class);
+        connection.addDataType("agtype", Agtype.class);
+        try (Statement statement = connection.createStatement()) {
+            statement.execute("CREATE EXTENSION IF NOT EXISTS age;");
+            statement.execute("LOAD 'age'");
+            statement.execute("SET search_path = ag_catalog, \"$user\", 
public;");
+            statement.execute("SELECT create_graph('cypher');");
+        }
+    }
+}
diff --git a/drivers/jdbc/settings.gradle.kts b/drivers/jdbc/settings.gradle.kts
new file mode 100644
index 0000000..ffcfd16
--- /dev/null
+++ b/drivers/jdbc/settings.gradle.kts
@@ -0,0 +1,11 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your 
build.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can 
be found
+ * in the user manual at 
https://docs.gradle.org/7.0/userguide/multi_project_builds.html
+ */
+
+rootProject.name = "age-jdbc"
+include("lib")

Reply via email to