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")