You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by dl...@apache.org on 2021/10/29 23:37:50 UTC

[asterixdb-clients] branch master updated: [NO ISSUE] Support driver/database version check

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

dlych pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb-clients.git


The following commit(s) were added to refs/heads/master by this push:
     new fb6e923  [NO ISSUE] Support driver/database version check
fb6e923 is described below

commit fb6e9234e4375d1b6a1c6bd49a48a2134ac2903c
Author: Dmitry Lychagin <dm...@couchbase.com>
AuthorDate: Fri Oct 29 11:41:25 2021 -0700

    [NO ISSUE] Support driver/database version check
    
    Details:
    - Introduce 2 new driver properties:
      'minDriverVersion' and 'minDatabaseVersion'
      that allow user to enforce minimal driver and database versions
    - Remove driver property handling from ADBDriverContext
    
    Change-Id: Icdca92fbc7a394aa91bfde14cae8f9e726cd51d5
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb-clients/+/13863
    Tested-by: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Reviewed-by: Dmitry Lychagin <dm...@couchbase.com>
    Reviewed-by: Ian Maxon <im...@uci.edu>
---
 .../apache/asterix/jdbc/core/ADBConnection.java    |   7 +-
 .../asterix/jdbc/core/ADBDatabaseMetaData.java     |  33 ++---
 .../apache/asterix/jdbc/core/ADBDriverBase.java    | 142 +++++++++++++++------
 .../apache/asterix/jdbc/core/ADBDriverContext.java |  22 +---
 .../asterix/jdbc/core/ADBDriverProperty.java       |  23 +++-
 .../apache/asterix/jdbc/core/ADBErrorReporter.java |  13 ++
 .../asterix/jdbc/core/ADBProductVersion.java       |  60 +--------
 .../apache/asterix/jdbc/core/ADBProtocolBase.java  |  36 ++++++
 .../main/java/org/apache/asterix/jdbc/Driver.java  |  23 ++++
 9 files changed, 221 insertions(+), 138 deletions(-)

diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBConnection.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBConnection.java
index 23b98af..2a068f1 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBConnection.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBConnection.java
@@ -50,7 +50,7 @@ public class ADBConnection extends ADBWrapperSupport implements Connection {
 
     protected final ADBProtocolBase protocol;
     protected final String url;
-    protected final String databaseVersion;
+    protected final ADBProductVersion databaseVersion;
     protected final ADBDriverProperty.CatalogDataverseMode catalogDataverseMode;
     protected final boolean catalogIncludesSchemaless;
     protected final boolean sqlCompatMode;
@@ -63,8 +63,9 @@ public class ADBConnection extends ADBWrapperSupport implements Connection {
 
     // Lifecycle
 
-    public ADBConnection(ADBProtocolBase protocol, String url, String databaseVersion, String dataverseCanonicalName,
-            Map<ADBDriverProperty, Object> properties, SQLWarning connectWarning) throws SQLException {
+    public ADBConnection(ADBProtocolBase protocol, String url, ADBProductVersion databaseVersion,
+            String dataverseCanonicalName, Map<ADBDriverProperty, Object> properties, SQLWarning connectWarning)
+            throws SQLException {
         this.url = Objects.requireNonNull(url);
         this.protocol = Objects.requireNonNull(protocol);
         this.databaseVersion = databaseVersion;
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDatabaseMetaData.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDatabaseMetaData.java
index 81e42eb..7068182 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDatabaseMetaData.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDatabaseMetaData.java
@@ -35,35 +35,36 @@ public class ADBDatabaseMetaData extends ADBWrapperSupport implements DatabaseMe
 
     protected final ADBMetaStatement metaStatement;
 
-    protected final String databaseVersionText;
+    protected final ADBProductVersion driverVersion;
 
-    private volatile ADBProductVersion databaseVersion;
+    protected final ADBProductVersion databaseVersion;
 
-    public ADBDatabaseMetaData(ADBMetaStatement metaStatement, String databaseVersionText) {
+    public ADBDatabaseMetaData(ADBMetaStatement metaStatement, ADBProductVersion databaseVersion) {
         this.metaStatement = Objects.requireNonNull(metaStatement);
-        this.databaseVersionText = databaseVersionText;
+        this.driverVersion = metaStatement.connection.protocol.getDriverContext().getDriverVersion();
+        this.databaseVersion = databaseVersion;
     }
 
     // Driver name and version
 
     @Override
     public String getDriverName() {
-        return metaStatement.connection.protocol.getDriverContext().getDriverVersion().getProductName();
+        return driverVersion.getProductName();
     }
 
     @Override
     public String getDriverVersion() {
-        return metaStatement.connection.protocol.getDriverContext().getDriverVersion().getProductVersion();
+        return driverVersion.getProductVersion();
     }
 
     @Override
     public int getDriverMajorVersion() {
-        return metaStatement.connection.protocol.getDriverContext().getDriverVersion().getMajorVersion();
+        return driverVersion.getMajorVersion();
     }
 
     @Override
     public int getDriverMinorVersion() {
-        return metaStatement.connection.protocol.getDriverContext().getDriverVersion().getMinorVersion();
+        return driverVersion.getMinorVersion();
     }
 
     @Override
@@ -80,30 +81,22 @@ public class ADBDatabaseMetaData extends ADBWrapperSupport implements DatabaseMe
 
     @Override
     public String getDatabaseProductName() {
-        return getDatabaseVersion().getProductName();
+        return databaseVersion.getProductName();
     }
 
     @Override
     public String getDatabaseProductVersion() {
-        return getDatabaseVersion().getProductVersion();
+        return databaseVersion.getProductVersion();
     }
 
     @Override
     public int getDatabaseMajorVersion() {
-        return getDatabaseVersion().getMajorVersion();
+        return databaseVersion.getMajorVersion();
     }
 
     @Override
     public int getDatabaseMinorVersion() {
-        return getDatabaseVersion().getMinorVersion();
-    }
-
-    protected ADBProductVersion getDatabaseVersion() {
-        ADBProductVersion result = databaseVersion;
-        if (result == null) {
-            databaseVersion = result = ADBProductVersion.parseDatabaseVersion(databaseVersionText);
-        }
-        return result;
+        return databaseVersion.getMinorVersion();
     }
 
     // Database objects
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverBase.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverBase.java
index 613c3a9..63dc5a8 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverBase.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverBase.java
@@ -29,8 +29,10 @@ import java.sql.SQLWarning;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -53,11 +55,18 @@ public abstract class ADBDriverBase {
 
     protected final int defaultApiPort;
 
+    protected final ADBErrorReporter errorReporter;
+
+    private volatile ADBProductVersion driverVersion;
+
+    private volatile Map<String, ADBDriverProperty> supportedPropertiesIndex;
+
     private volatile ADBDriverContext context;
 
     public ADBDriverBase(String driverScheme, int defaultApiPort) {
         this.urlScheme = JDBC_SCHEME + Objects.requireNonNull(driverScheme);
         this.defaultApiPort = defaultApiPort;
+        this.errorReporter = createErrorReporter();
     }
 
     protected static void registerDriver(java.sql.Driver driver) {
@@ -68,23 +77,23 @@ public abstract class ADBDriverBase {
         }
     }
 
-    protected static void parseConnectionProperties(Properties inProps, ADBDriverContext driverContext,
-            Map<ADBDriverProperty, Object> outProperties, SQLWarning outWarning) throws SQLException {
+    protected void parseConnectionProperties(Properties inProps, Map<ADBDriverProperty, Object> outProperties,
+            Map<String, ADBDriverProperty> supportedProperties, SQLWarning outWarning) throws SQLException {
         if (inProps != null) {
             for (Enumeration<?> en = inProps.propertyNames(); en.hasMoreElements();) {
                 String name = en.nextElement().toString();
                 String value = inProps.getProperty(name);
-                parseConnectionProperty(name, value, driverContext, outProperties, outWarning);
+                parseConnectionProperty(name, value, supportedProperties, outProperties, outWarning);
             }
         }
     }
 
-    protected static void parseConnectionProperty(String name, String textValue, ADBDriverContext driverContext,
-            Map<ADBDriverProperty, Object> outProperties, SQLWarning outWarning) throws SQLException {
-        ADBDriverProperty property = driverContext.getSupportedProperties().get(name);
+    protected void parseConnectionProperty(String name, String textValue,
+            Map<String, ADBDriverProperty> supportedProperties, Map<ADBDriverProperty, Object> outProperties,
+            SQLWarning outWarning) throws SQLException {
+        ADBDriverProperty property = supportedProperties.get(name);
         if (property == null) {
-            outWarning.setNextWarning(
-                    new SQLWarning(driverContext.getErrorReporter().warningParameterNotSupported(name)));
+            outWarning.setNextWarning(new SQLWarning(errorReporter.warningParameterNotSupported(name)));
             return;
         }
         if (textValue == null || textValue.isEmpty()) {
@@ -94,7 +103,7 @@ public abstract class ADBDriverBase {
         try {
             value = Objects.requireNonNull(property.getValueParser().apply(textValue));
         } catch (RuntimeException e) {
-            throw driverContext.getErrorReporter().errorParameterValueNotSupported(name);
+            throw errorReporter.errorParameterValueNotSupported(name);
         }
         outProperties.put(property, value);
     }
@@ -135,34 +144,34 @@ public abstract class ADBDriverBase {
         try {
             subUri = new URI(url.substring(JDBC_SCHEME.length()));
         } catch (URISyntaxException e) {
-            throw createErrorReporter().errorParameterValueNotSupported("URL");
+            throw errorReporter.errorParameterValueNotSupported("URL");
         }
         String host = subUri.getHost();
         if (host == null) {
-            throw createErrorReporter().errorParameterValueNotSupported("URL");
+            throw errorReporter.errorParameterValueNotSupported("URL");
         }
         int port = subUri.getPort();
         if (port <= 0) {
             port = defaultApiPort;
         }
 
-        Properties uriParams = getURIParameters(subUri);
-
-        ADBDriverContext driverContext = getOrCreateDriverContext();
-        SQLWarning warning = new SQLWarning();
         Map<ADBDriverProperty, Object> properties = new HashMap<>();
-        parseConnectionProperties(uriParams, driverContext, properties, warning);
-        parseConnectionProperties(info, driverContext, properties, warning);
-
+        Map<String, ADBDriverProperty> supportedProperties = getOrCreateSupportedPropertiesIndex();
+        SQLWarning warning = new SQLWarning();
+        parseConnectionProperties(getURIParameters(subUri), properties, supportedProperties, warning);
+        parseConnectionProperties(info, properties, supportedProperties, warning);
         warning = warning.getNextWarning() != null ? warning.getNextWarning() : null;
 
-        String path = subUri.getPath();
-        String dataverseCanonicalName =
-                path != null && path.length() > 1 && path.startsWith("/") ? path.substring(1) : null;
+        checkDriverVersion(properties);
+
+        String dataverseCanonicalName = getDataverseCanonicalNameFromURI(subUri);
 
+        ADBDriverContext driverContext = getOrCreateDriverContext();
         ADBProtocolBase protocol = createProtocol(host, port, properties, driverContext);
         try {
-            String databaseVersion = protocol.connect();
+            String serverVersion = protocol.connect();
+            ADBProductVersion databaseVersion = protocol.parseDatabaseVersion(serverVersion);
+            checkDatabaseVersion(properties, databaseVersion);
             return createConnection(protocol, url, databaseVersion, dataverseCanonicalName, properties, warning);
         } catch (SQLException e) {
             try {
@@ -174,9 +183,13 @@ public abstract class ADBDriverBase {
         }
     }
 
+    protected String getDataverseCanonicalNameFromURI(URI subUri) {
+        String path = subUri.getPath();
+        return path != null && path.length() > 1 && path.startsWith("/") ? path.substring(1) : null;
+    }
+
     public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) {
-        Collection<ADBDriverProperty> supportedProperties =
-                getOrCreateDriverContext().getSupportedProperties().values();
+        Collection<ADBDriverProperty> supportedProperties = getOrCreateSupportedPropertiesIndex().values();
         List<DriverPropertyInfo> result = new ArrayList<>(supportedProperties.size());
         for (ADBDriverProperty property : supportedProperties) {
             if (property.isHidden()) {
@@ -191,11 +204,11 @@ public abstract class ADBDriverBase {
     }
 
     public int getMajorVersion() {
-        return getOrCreateDriverContext().getDriverVersion().getMajorVersion();
+        return getOrCreateDriverVersion().getMajorVersion();
     }
 
     public int getMinorVersion() {
-        return getOrCreateDriverContext().getDriverVersion().getMinorVersion();
+        return getOrCreateDriverVersion().getMinorVersion();
     }
 
     public boolean jdbcCompliant() {
@@ -211,31 +224,84 @@ public abstract class ADBDriverBase {
     }
 
     protected ADBDriverContext getOrCreateDriverContext() {
-        ADBDriverContext ctx = context;
-        if (ctx == null) {
+        ADBDriverContext result = context;
+        if (result == null) {
             synchronized (this) {
-                ctx = context;
-                if (ctx == null) {
-                    context = ctx = createDriverContext();
+                result = context;
+                if (result == null) {
+                    context = result = createDriverContext();
                 }
             }
         }
-        return ctx;
+        return result;
     }
 
     protected ADBDriverContext createDriverContext() {
-        return new ADBDriverContext(getDriverVersion(), getDriverSupportedProperties(), createErrorReporter(),
-                getLogger());
+        return new ADBDriverContext(getOrCreateDriverVersion(), errorReporter, getLogger());
     }
 
-    protected ADBProductVersion getDriverVersion() {
-        return ADBProductVersion.parseDriverVersion(getClass().getPackage());
+    protected ADBProductVersion getOrCreateDriverVersion() {
+        ADBProductVersion result = driverVersion;
+        if (result == null) {
+            synchronized (this) {
+                result = driverVersion;
+                if (result == null) {
+                    driverVersion = result = getDriverVersion();
+                }
+            }
+        }
+        return result;
     }
 
-    protected Collection<ADBDriverProperty> getDriverSupportedProperties() {
+    protected abstract ADBProductVersion getDriverVersion();
+
+    protected Collection<ADBDriverProperty> getSupportedProperties() {
         return Arrays.asList(ADBDriverProperty.Common.values());
     }
 
+    private Map<String, ADBDriverProperty> getOrCreateSupportedPropertiesIndex() {
+        Map<String, ADBDriverProperty> result = supportedPropertiesIndex;
+        if (result == null) {
+            synchronized (this) {
+                result = supportedPropertiesIndex;
+                if (result == null) {
+                    supportedPropertiesIndex = result = createPropertyIndexByName(getSupportedProperties());
+                }
+            }
+        }
+        return result;
+    }
+
+    private static Map<String, ADBDriverProperty> createPropertyIndexByName(Collection<ADBDriverProperty> properties) {
+        Map<String, ADBDriverProperty> m = new LinkedHashMap<>();
+        for (ADBDriverProperty p : properties) {
+            m.put(p.getPropertyName(), p);
+        }
+        return Collections.unmodifiableMap(m);
+    }
+
+    public void checkDriverVersion(Map<ADBDriverProperty, Object> properties) throws SQLException {
+        ADBProductVersion minExpectedVersion =
+                (ADBProductVersion) ADBDriverProperty.Common.MIN_DRIVER_VERSION.fetchPropertyValue(properties);
+        if (minExpectedVersion != null) {
+            ADBProductVersion driverVersion = getOrCreateDriverVersion();
+            if (!driverVersion.isAtLeast(minExpectedVersion)) {
+                throw errorReporter.errorUnexpectedDriverVersion(driverVersion, minExpectedVersion);
+            }
+        }
+    }
+
+    public void checkDatabaseVersion(Map<ADBDriverProperty, Object> properties, ADBProductVersion databaseVersion)
+            throws SQLException {
+        ADBProductVersion minExpectedVersion =
+                (ADBProductVersion) ADBDriverProperty.Common.MIN_DATABASE_VERSION.fetchPropertyValue(properties);
+        if (minExpectedVersion != null) {
+            if (!databaseVersion.isAtLeast(minExpectedVersion)) {
+                throw errorReporter.errorUnexpectedDatabaseVersion(databaseVersion, minExpectedVersion);
+            }
+        }
+    }
+
     protected ADBErrorReporter createErrorReporter() {
         return new ADBErrorReporter();
     }
@@ -243,7 +309,7 @@ public abstract class ADBDriverBase {
     protected abstract ADBProtocolBase createProtocol(String host, int port, Map<ADBDriverProperty, Object> properties,
             ADBDriverContext driverContext) throws SQLException;
 
-    protected ADBConnection createConnection(ADBProtocolBase protocol, String url, String databaseVersion,
+    protected ADBConnection createConnection(ADBProtocolBase protocol, String url, ADBProductVersion databaseVersion,
             String dataverseCanonicalName, Map<ADBDriverProperty, Object> properties, SQLWarning connectWarning)
             throws SQLException {
         return new ADBConnection(protocol, url, databaseVersion, dataverseCanonicalName, properties, connectWarning);
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverContext.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverContext.java
index f81bb9f..45f64b2 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverContext.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverContext.java
@@ -19,10 +19,6 @@
 
 package org.apache.asterix.jdbc.core;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
 import java.util.Objects;
 import java.util.logging.Logger;
 
@@ -40,8 +36,6 @@ public class ADBDriverContext {
 
     private final ADBProductVersion driverVersion;
 
-    private final Map<String, ADBDriverProperty> supportedProperties;
-
     private final ADBErrorReporter errorReporter;
 
     private final Logger logger;
@@ -54,12 +48,10 @@ public class ADBDriverContext {
 
     private final ObjectWriter admFormatObjectWriter;
 
-    public ADBDriverContext(ADBProductVersion driverVersion, Collection<ADBDriverProperty> driverSupportedProperties,
-            ADBErrorReporter errorReporter, Logger logger) {
+    public ADBDriverContext(ADBProductVersion driverVersion, ADBErrorReporter errorReporter, Logger logger) {
         this.driverVersion = Objects.requireNonNull(driverVersion);
         this.errorReporter = Objects.requireNonNull(errorReporter);
         this.logger = Objects.requireNonNull(logger);
-        this.supportedProperties = createPropertyIndexByName(driverSupportedProperties);
 
         ObjectMapper genericObjectMapper = createGenericObjectMapper();
         this.genericObjectReader = genericObjectMapper.reader();
@@ -90,14 +82,6 @@ public class ADBDriverContext {
         return mapper;
     }
 
-    private Map<String, ADBDriverProperty> createPropertyIndexByName(Collection<ADBDriverProperty> properties) {
-        Map<String, ADBDriverProperty> m = new LinkedHashMap<>();
-        for (ADBDriverProperty p : properties) {
-            m.put(p.getPropertyName(), p);
-        }
-        return Collections.unmodifiableMap(m);
-    }
-
     public ADBErrorReporter getErrorReporter() {
         return errorReporter;
     }
@@ -125,8 +109,4 @@ public class ADBDriverContext {
     public ADBProductVersion getDriverVersion() {
         return driverVersion;
     }
-
-    public Map<String, ADBDriverProperty> getSupportedProperties() {
-        return supportedProperties;
-    }
 }
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverProperty.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverProperty.java
index 4d454e6..11c762d 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverProperty.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBDriverProperty.java
@@ -43,8 +43,11 @@ public interface ADBDriverProperty {
         CATALOG_DATAVERSE_MODE("catalogDataverseMode", Integer::parseInt, 1, false), // 1 -> CATALOG, 2 -> CATALOG_SCHEMA
         CATALOG_INCLUDES_SCHEMALESS("catalogIncludesSchemaless", Boolean::parseBoolean, false, false),
         SQL_COMPAT_MODE("sqlCompatMode", Boolean::parseBoolean, true, false), // Whether user statements are executed in 'SQL-compat' mode
-        ACTIVE_REQUESTS_PATH("activeRequestsPath", Function.identity(), null, true),
-        SSL("ssl", Boolean::parseBoolean, false, false);
+        SSL("ssl", Boolean::parseBoolean, false, false),
+        // Hidden properties
+        MIN_DRIVER_VERSION("minDriverVersion", Common::parseMinVersion, null, true),
+        MIN_DATABASE_VERSION("minDatabaseVersion", Common::parseMinVersion, null, true),
+        ACTIVE_REQUESTS_PATH("activeRequestsPath", Function.identity(), null, true);
 
         private final String propertyName;
 
@@ -89,6 +92,22 @@ public interface ADBDriverProperty {
         public Object fetchPropertyValue(Map<ADBDriverProperty, Object> properties) {
             return properties.getOrDefault(this, defaultValue);
         }
+
+        public static ADBProductVersion parseMinVersion(String text) {
+            String[] parts = text.split("\\.");
+            int major, minor = 0;
+            switch (parts.length) {
+                case 2:
+                    minor = Integer.parseInt(parts[1]);
+                    // fall thru to 1
+                case 1:
+                    major = Integer.parseInt(parts[0]);
+                    break;
+                default:
+                    throw new IllegalArgumentException(text);
+            }
+            return new ADBProductVersion(null, text, major, minor);
+        }
     }
 
     enum CatalogDataverseMode {
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBErrorReporter.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBErrorReporter.java
index 8ae76f3..07abd87 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBErrorReporter.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBErrorReporter.java
@@ -67,6 +67,19 @@ public class ADBErrorReporter {
         return String.format("Ignored unsupported or invalid value of %s parameter", parameterName);
     }
 
+    public SQLException errorUnexpectedDriverVersion(ADBProductVersion version, ADBProductVersion minExpectedVersion) {
+        return new SQLException(
+                String.format("Unexpected driver version %s. Expected at least %s.%s", version.getProductVersion(),
+                        minExpectedVersion.getMajorVersion(), minExpectedVersion.getMinorVersion()));
+    }
+
+    public SQLException errorUnexpectedDatabaseVersion(ADBProductVersion version,
+            ADBProductVersion minExpectedVersion) {
+        return new SQLException(
+                String.format("Unexpected database version %s. Expected at least %s.%s", version.getProductVersion(),
+                        minExpectedVersion.getMajorVersion(), minExpectedVersion.getMinorVersion()));
+    }
+
     public SQLException errorIncompatibleMode(String mode) {
         return new SQLException(String.format("Operation cannot be performed in %s mode", mode));
     }
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProductVersion.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProductVersion.java
index a49a209..dd16812 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProductVersion.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProductVersion.java
@@ -19,16 +19,10 @@
 
 package org.apache.asterix.jdbc.core;
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 public class ADBProductVersion {
 
     public static final String ASTERIXDB = "Apache AsterixDB";
 
-    private static final Pattern DATABASE_VERSION_PATTERN =
-            Pattern.compile("(?<name>[^/]+)(?:/(?<ver>(?:(?<vermj>\\d+)(?:\\.(?<vermn>\\d+))?)?.*))?");
-
     private final String productName;
 
     private final String productVersion;
@@ -60,55 +54,13 @@ public class ADBProductVersion {
         return minorVersion;
     }
 
-    public static ADBProductVersion parseDriverVersion(Package driverPackage) {
-        int majorVersion = 0, minorVersion = 0;
-        String productName = driverPackage.getImplementationTitle();
-        if (productName == null) {
-            productName = ASTERIXDB;
-        }
-        String productVersion = driverPackage.getImplementationVersion();
-        if (productVersion != null) {
-            String[] v = productVersion.split("\\.");
-            try {
-                majorVersion = Integer.parseInt(v[0]);
-                if (v.length > 1) {
-                    minorVersion = Integer.parseInt(v[1]);
-                }
-            } catch (NumberFormatException e) {
-                // ignore
-            }
-        }
-        return new ADBProductVersion(productName, productVersion, majorVersion, minorVersion);
+    public boolean isAtLeast(ADBProductVersion otherVersion) {
+        return majorVersion == otherVersion.majorVersion ? minorVersion >= otherVersion.minorVersion
+                : majorVersion > otherVersion.majorVersion;
     }
 
-    public static ADBProductVersion parseDatabaseVersion(String serverVersion) {
-        String dbProductName = null;
-        String dbProductVersion = null;
-        int dbMajorVersion = 0;
-        int dbMinorVersion = 0;
-        if (serverVersion != null) {
-            Matcher m = DATABASE_VERSION_PATTERN.matcher(serverVersion);
-            if (m.matches()) {
-                dbProductName = m.group("name");
-                dbProductVersion = m.group("ver");
-                String vermj = m.group("vermj");
-                String vermn = m.group("vermn");
-                if (vermj != null) {
-                    try {
-                        dbMajorVersion = Integer.parseInt(vermj);
-                    } catch (NumberFormatException e) {
-                        // ignore (overflow)
-                    }
-                }
-                if (vermn != null) {
-                    try {
-                        dbMinorVersion = Integer.parseInt(vermn);
-                    } catch (NumberFormatException e) {
-                        // ignore (overflow)
-                    }
-                }
-            }
-        }
-        return new ADBProductVersion(dbProductName, dbProductVersion, dbMajorVersion, dbMinorVersion);
+    @Override
+    public String toString() {
+        return productName + '/' + productVersion;
     }
 }
diff --git a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProtocolBase.java b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProtocolBase.java
index 39db835..6bf6c1f 100644
--- a/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProtocolBase.java
+++ b/asterixdb-jdbc/asterix-jdbc-core/src/main/java/org/apache/asterix/jdbc/core/ADBProtocolBase.java
@@ -33,6 +33,8 @@ import java.util.Objects;
 import java.util.UUID;
 import java.util.function.Function;
 import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -63,6 +65,9 @@ public abstract class ADBProtocolBase {
     private static final String OPTIONAL_TYPE_SUFFIX = "?";
     private static final String EXPLAIN_ONLY_RESULT_COLUMN_NAME = "$1";
 
+    private static final Pattern DATABASE_VERSION_PATTERN =
+            Pattern.compile("(?<name>[^/]+)(?:/(?<ver>(?:(?<vermj>\\d+)(?:\\.(?<vermn>\\d+))?)?.*))?");
+
     protected final ADBDriverContext driverContext;
     protected final String user;
     protected final int maxWarnings;
@@ -218,6 +223,37 @@ public abstract class ADBProtocolBase {
         return paramPos;
     }
 
+    public ADBProductVersion parseDatabaseVersion(String serverVersion) {
+        String dbProductName = null;
+        String dbProductVersion = null;
+        int dbMajorVersion = 0;
+        int dbMinorVersion = 0;
+        if (serverVersion != null) {
+            Matcher m = DATABASE_VERSION_PATTERN.matcher(serverVersion);
+            if (m.matches()) {
+                dbProductName = m.group("name");
+                dbProductVersion = m.group("ver");
+                String vermj = m.group("vermj");
+                String vermn = m.group("vermn");
+                if (vermj != null) {
+                    try {
+                        dbMajorVersion = Integer.parseInt(vermj);
+                    } catch (NumberFormatException e) {
+                        // ignore (overflow)
+                    }
+                }
+                if (vermn != null) {
+                    try {
+                        dbMinorVersion = Integer.parseInt(vermn);
+                    } catch (NumberFormatException e) {
+                        // ignore (overflow)
+                    }
+                }
+            }
+        }
+        return new ADBProductVersion(dbProductName, dbProductVersion, dbMajorVersion, dbMinorVersion);
+    }
+
     public String getDefaultDataverse() {
         return DEFAULT_DATAVERSE;
     }
diff --git a/asterixdb-jdbc/asterix-jdbc-driver/src/main/java/org/apache/asterix/jdbc/Driver.java b/asterixdb-jdbc/asterix-jdbc-driver/src/main/java/org/apache/asterix/jdbc/Driver.java
index 97fb256..ac1701f 100644
--- a/asterixdb-jdbc/asterix-jdbc-driver/src/main/java/org/apache/asterix/jdbc/Driver.java
+++ b/asterixdb-jdbc/asterix-jdbc-driver/src/main/java/org/apache/asterix/jdbc/Driver.java
@@ -31,6 +31,7 @@ import org.apache.asterix.jdbc.core.ADBDriverBase;
 import org.apache.asterix.jdbc.core.ADBDriverContext;
 import org.apache.asterix.jdbc.core.ADBDriverProperty;
 import org.apache.asterix.jdbc.core.ADBErrorReporter;
+import org.apache.asterix.jdbc.core.ADBProductVersion;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.utils.URLEncodedUtils;
 
@@ -82,4 +83,26 @@ public class Driver extends ADBDriverBase implements java.sql.Driver {
             }
         };
     }
+
+    @Override
+    protected ADBProductVersion getDriverVersion() {
+        Package driverPackage = getClass().getPackage();
+        return parseDriverVersion(driverPackage.getImplementationTitle(), driverPackage.getImplementationVersion());
+    }
+
+    private static ADBProductVersion parseDriverVersion(String productName, String productVersion) {
+        int majorVersion = 0, minorVersion = 0;
+        if (productVersion != null) {
+            String[] v = productVersion.split("\\.");
+            try {
+                majorVersion = Integer.parseInt(v[0]);
+                if (v.length > 1) {
+                    minorVersion = Integer.parseInt(v[1]);
+                }
+            } catch (NumberFormatException e) {
+                // ignore
+            }
+        }
+        return new ADBProductVersion(productName, productVersion, majorVersion, minorVersion);
+    }
 }