GUACAMOLE-220: Dynamically detect whether the MariaDB / MySQL server supports 
recursive CTEs.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/dec7b3c3
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/dec7b3c3
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/dec7b3c3

Branch: refs/heads/staging/1.0.0
Commit: dec7b3c340dfe1ccd76292fc8e99ae4ec42dcc03
Parents: 204b6a4
Author: Michael Jumper <mjum...@apache.org>
Authored: Sat Sep 8 18:11:36 2018 -0700
Committer: Michael Jumper <mjum...@apache.org>
Committed: Wed Sep 19 23:56:53 2018 -0700

----------------------------------------------------------------------
 .../guacamole/auth/jdbc/JDBCEnvironment.java    |   6 +-
 .../guacamole/auth/jdbc/base/EntityMapper.java  |  11 +-
 .../guacamole/auth/jdbc/base/EntityService.java |  16 +-
 .../guacamole/auth/mysql/MySQLEnvironment.java  |  52 ++++++-
 .../guacamole/auth/mysql/MySQLVersion.java      | 153 +++++++++++++++++++
 .../guacamole/auth/jdbc/base/EntityMapper.xml   |  99 ++++++++----
 .../auth/postgresql/PostgreSQLEnvironment.java  |   3 +-
 .../auth/sqlserver/SQLServerEnvironment.java    |   3 +-
 8 files changed, 300 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java
index 93cc7f7..9158afb 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/JDBCEnvironment.java
@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.environment.LocalEnvironment;
 import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
+import org.apache.ibatis.session.SqlSession;
 
 /**
  * A JDBC-specific implementation of Environment that defines generic 
properties
@@ -143,9 +144,12 @@ public abstract class JDBCEnvironment extends 
LocalEnvironment {
      * not supported, queries that are intended to be recursive may need to be
      * invoked multiple times to retrieve the same data.
      *
+     * @param session
+     *     The SqlSession provided by MyBatis for the current transaction.
+     *
      * @return
      *     true if the database supports recursive queries, false otherwise.
      */
-    public abstract boolean isRecursiveQuerySupported();
+    public abstract boolean isRecursiveQuerySupported(SqlSession session);
 
 }

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityMapper.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityMapper.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityMapper.java
index 53b0290..dbe7cb4 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityMapper.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityMapper.java
@@ -60,12 +60,21 @@ public interface EntityMapper {
      *     The identifiers of any known effective groups that should be taken
      *     into account, such as those defined externally to the database.
      *
+     * @param recursive
+     *     Whether the query should leverage database engine features to return
+     *     absolutely all effective groups, including those inherited through
+     *     group membership. If false, this query will return only one level of
+     *     depth and may need to be executed multiple times. If it is known
+     *     that the database engine in question will always support (or always
+     *     not support) recursive queries, this parameter may be ignored.
+     *
      * @return
      *     The set of identifiers of all groups that the given entity is a
      *     member of, including those where membership is inherited through
      *     membership in other groups.
      */
     Set<String> selectEffectiveGroupIdentifiers(@Param("entity") EntityModel 
entity,
-            @Param("effectiveGroups") Collection<String> effectiveGroups);
+            @Param("effectiveGroups") Collection<String> effectiveGroups,
+            @Param("recursive") boolean recursive);
 
 }

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityService.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityService.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityService.java
index 1e40bb0..cc2a9aa 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityService.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/base/EntityService.java
@@ -23,6 +23,8 @@ import com.google.inject.Inject;
 import java.util.Collection;
 import java.util.Set;
 import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
+import org.apache.ibatis.session.SqlSession;
+import org.mybatis.guice.transactional.Transactional;
 
 /**
  * Service which provides convenience methods for creating, retrieving, and
@@ -43,6 +45,12 @@ public class EntityService {
     private EntityMapper entityMapper;
 
     /**
+     * The current SQL session used by MyBatis.
+     */
+    @Inject
+    private SqlSession sqlSession;
+
+    /**
      * Returns the set of all group identifiers of which the given entity is a
      * member, taking into account the given collection of known group
      * memberships which are not necessarily defined within the database.
@@ -64,20 +72,22 @@ public class EntityService {
      *     member of, including those where membership is inherited through
      *     membership in other groups.
      */
+    @Transactional
     public Set<String> retrieveEffectiveGroups(ModeledPermissions<? extends 
EntityModel> entity,
             Collection<String> effectiveGroups) {
 
         // Retrieve the effective user groups of the given entity, recursively 
if possible
-        Set<String> identifiers = 
entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), 
effectiveGroups);
+        boolean recursive = environment.isRecursiveQuerySupported(sqlSession);
+        Set<String> identifiers = 
entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), 
effectiveGroups, recursive);
 
         // If the set of user groups retrieved was not produced recursively,
         // manually repeat the query to expand the set until all effective
         // groups have been found
-        if (!environment.isRecursiveQuerySupported() && 
!identifiers.isEmpty()) {
+        if (!recursive && !identifiers.isEmpty()) {
             Set<String> previousIdentifiers;
             do {
                 previousIdentifiers = identifiers;
-                identifiers = 
entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), 
previousIdentifiers);
+                identifiers = 
entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), 
previousIdentifiers, false);
             } while (identifiers.size() > previousIdentifiers.size());
         }
 

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLEnvironment.java
index 062d6df..7a93151 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLEnvironment.java
@@ -19,11 +19,16 @@
 
 package org.apache.guacamole.auth.mysql;
 
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
+import org.apache.ibatis.exceptions.PersistenceException;
+import org.apache.ibatis.session.SqlSession;
 
 /**
  * A MySQL-specific implementation of JDBCEnvironment provides database
@@ -35,7 +40,17 @@ public class MySQLEnvironment extends JDBCEnvironment {
      * Logger for this class.
      */
     private static final Logger logger = 
LoggerFactory.getLogger(MySQLEnvironment.class);
-    
+
+    /**
+     * The earliest version of MariaDB that supported recursive CTEs.
+     */
+    private static final MySQLVersion MARIADB_SUPPORTS_CTE = new 
MySQLVersion(10, 2, 2, true);
+
+    /**
+     * The earliest version of MySQL that supported recursive CTEs.
+     */
+    private static final MySQLVersion MYSQL_SUPPORTS_CTE = new MySQLVersion(8, 
0, 1, false);
+
     /**
      * The default host to connect to, if MYSQL_HOSTNAME is not specified.
      */
@@ -227,8 +242,39 @@ public class MySQLEnvironment extends JDBCEnvironment {
     }
 
     @Override
-    public boolean isRecursiveQuerySupported() {
-        return false; // Only very recent versions of MySQL / MariaDB support 
recursive queries through CTEs
+    public boolean isRecursiveQuerySupported(SqlSession session) {
+
+        // Retrieve database version string from JDBC connection
+        String versionString;
+        try {
+            Connection connection = session.getConnection();
+            DatabaseMetaData metaData = connection.getMetaData();
+            versionString = metaData.getDatabaseProductVersion();
+        }
+        catch (SQLException e) {
+            throw new PersistenceException("Cannot determine whether "
+                    + "MySQL / MariaDB supports recursive queries.", e);
+        }
+
+        try {
+
+            // Parse MySQL / MariaDB version from version string
+            MySQLVersion version = new MySQLVersion(versionString);
+            logger.debug("Database recognized as {}.", version);
+
+            // Recursive queries are supported for MariaDB 10.2.2+ and
+            // MySQL 8.0.1+
+            return version.isAtLeast(MARIADB_SUPPORTS_CTE)
+                || version.isAtLeast(MYSQL_SUPPORTS_CTE);
+
+        }
+        catch (IllegalArgumentException e) {
+            logger.debug("Unrecognized MySQL / MariaDB version string: "
+                    + "\"{}\". Assuming database engine does not support "
+                    + "recursive queries.", session);
+            return false;
+        }
+
     }
 
 }

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLVersion.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLVersion.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLVersion.java
new file mode 100644
index 0000000..577506e
--- /dev/null
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/java/org/apache/guacamole/auth/mysql/MySQLVersion.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.mysql;
+
+import com.google.common.collect.ComparisonChain;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The specific version of a MySQL or MariaDB server.
+ */
+public class MySQLVersion {
+
+    /**
+     * Pattern which matches the version string returned by a MariaDB server,
+     * extracting the major, minor, and patch numbers.
+     */
+    private final Pattern MARIADB_VERSION = 
Pattern.compile("^.*-([0-9]+)\\.([0-9]+)\\.([0-9]+)-MariaDB$");
+
+    /**
+     * Pattern which matches the version string returned by a non-MariaDB
+     * server (including MySQL and Aurora), extracting the major, minor, and
+     * patch numbers. All non-MariaDB servers use normal MySQL version numbers.
+     */
+    private final Pattern MYSQL_VERSION = 
Pattern.compile("^([0-9]+)\\.([0-9]+)\\.([0-9]+).*$");
+
+    /**
+     * Whether the associated server is a MariaDB server. All non-MariaDB
+     * servers use normal MySQL version numbers and are comparable against each
+     * other.
+     */
+    private final boolean isMariaDB;
+
+    /**
+     * The major component of the MAJOR.MINOR.PATCH version number.
+     */
+    private final int major;
+
+    /**
+     * The minor component of the MAJOR.MINOR.PATCH version number.
+     */
+    private final int minor;
+
+    /**
+     * The patch component of the MAJOR.MINOR.PATCH version number.
+     */
+    private final int patch;
+
+    /**
+     * Creates a new MySQLVersion having the specified major, minor, and patch
+     * components.
+     *
+     * @param major
+     *     The major component of the MAJOR.MINOR.PATCH version number of the
+     *     MariaDB / MySQL server.
+     *
+     * @param minor
+     *     The minor component of the MAJOR.MINOR.PATCH version number of the
+     *     MariaDB / MySQL server.
+     *
+     * @param patch
+     *     The patch component of the MAJOR.MINOR.PATCH version number of the
+     *     MariaDB / MySQL server.
+     *
+     * @param isMariaDB
+     *     Whether the associated server is a MariaDB server.
+     */
+    public MySQLVersion(int major, int minor, int patch, boolean isMariaDB) {
+        this.major = major;
+        this.minor = minor;
+        this.patch = patch;
+        this.isMariaDB = isMariaDB;
+    }
+
+    public MySQLVersion(String version) throws IllegalArgumentException {
+
+        // Extract MariaDB version number if version string appears to be
+        // a MariaDB version string
+        Matcher mariadb = MARIADB_VERSION.matcher(version);
+        if (mariadb.matches()) {
+            this.major = Integer.parseInt(mariadb.group(1));
+            this.minor = Integer.parseInt(mariadb.group(2));
+            this.patch = Integer.parseInt(mariadb.group(3));
+            this.isMariaDB = true;
+            return;
+        }
+
+        // If not MariaDB, assume version string is a MySQL version string
+        // and attempt to extract the version number
+        Matcher mysql = MYSQL_VERSION.matcher(version);
+        if (mysql.matches()) {
+            this.major = Integer.parseInt(mysql.group(1));
+            this.minor = Integer.parseInt(mysql.group(2));
+            this.patch = Integer.parseInt(mysql.group(3));
+            this.isMariaDB = false;
+            return;
+        }
+
+        throw new IllegalArgumentException("Unrecognized MySQL / MariaDB 
version string.");
+
+    }
+
+    /**
+     * Returns whether this version is at least as recent as the given version.
+     *
+     * @param version
+     *     The version to compare against.
+     *
+     * @return
+     *     true if the versions are associated with the same database server
+     *     type (MariaDB vs. MySQL) and this version is at least as recent as
+     *     the given version, false otherwise.
+     */
+    public boolean isAtLeast(MySQLVersion version) {
+
+        // If the databases use different version numbering schemes, the
+        // version numbers are not comparable
+        if (isMariaDB != version.isMariaDB)
+            return false;
+
+        // Compare major, minor, and patch number in order of precedence
+        return ComparisonChain.start()
+                .compare(major, version.major)
+                .compare(minor, version.minor)
+                .compare(patch, version.patch)
+                .result() >= 0;
+
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s %d.%d.%d", isMariaDB ? "MariaDB" : "MySQL",
+                major, minor, patch);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
index eb7a771..21efb99 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-mysql/src/main/resources/org/apache/guacamole/auth/jdbc/base/EntityMapper.xml
@@ -65,43 +65,76 @@
     <!-- Select names of all effective groups (including inherited) -->
     <select id="selectEffectiveGroupIdentifiers" resultType="string">
 
-        WITH RECURSIVE related_entity(entity_id) AS (
-                SELECT
-                    guacamole_user_group.entity_id
-                FROM guacamole_user_group
-                JOIN guacamole_user_group_member ON 
guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
-                WHERE
+        <if test="!recursive">
+            SELECT
+                guacamole_entity.name
+            FROM guacamole_user_group
+            JOIN guacamole_entity ON guacamole_user_group.entity_id = 
guacamole_entity.entity_id
+            JOIN guacamole_user_group_member ON 
guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
+            WHERE
+                guacamole_user_group.disabled = false
+                AND (
                     guacamole_user_group_member.member_entity_id = 
#{entity.entityID}
-                    AND guacamole_user_group.disabled = false
-            <if test="!effectiveGroups.isEmpty()">
-                UNION
+                    <if test="!effectiveGroups.isEmpty()">
+                        OR guacamole_user_group_member.member_entity_id IN (
+                            SELECT entity_id FROM guacamole_entity
+                            WHERE type = 'USER_GROUP' AND name IN
+                                <foreach collection="effectiveGroups" 
item="effectiveGroup"
+                                         open="(" separator="," close=")">
+                                    #{effectiveGroup,jdbcType=VARCHAR}
+                                </foreach>
+                        )
+                        OR guacamole_user_group.entity_id IN (
+                            SELECT entity_id FROM guacamole_entity
+                            WHERE type = 'USER_GROUP' AND name IN
+                                <foreach collection="effectiveGroups" 
item="effectiveGroup"
+                                         open="(" separator="," close=")">
+                                    #{effectiveGroup,jdbcType=VARCHAR}
+                                </foreach>
+                        )
+                    </if>
+                )
+        </if>
+
+        <if test="recursive">
+            WITH RECURSIVE related_entity(entity_id) AS (
                     SELECT
-                        guacamole_entity.entity_id
-                    FROM guacamole_entity
-                    JOIN guacamole_user_group ON 
guacamole_user_group.entity_id = guacamole_entity.entity_id
+                        guacamole_user_group.entity_id
+                    FROM guacamole_user_group
+                    JOIN guacamole_user_group_member ON 
guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
                     WHERE
-                        type = 'USER_GROUP'
-                        AND name IN
-                            <foreach collection="effectiveGroups" 
item="effectiveGroup"
-                                     open="(" separator="," close=")">
-                                #{effectiveGroup,jdbcType=VARCHAR}
-                            </foreach>
+                        guacamole_user_group_member.member_entity_id = 
#{entity.entityID}
                         AND guacamole_user_group.disabled = false
-            </if>
-            UNION
-                SELECT
-                    guacamole_user_group.entity_id
-                FROM related_entity
-                JOIN guacamole_user_group_member ON related_entity.entity_id = 
guacamole_user_group_member.member_entity_id
-                JOIN guacamole_user_group ON 
guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
-                WHERE
-                    guacamole_user_group.disabled = false
-        )
-        SELECT name
-        FROM related_entity
-        JOIN guacamole_entity ON related_entity.entity_id = 
guacamole_entity.entity_id
-        WHERE
-            guacamole_entity.type = 'USER_GROUP';
+                <if test="!effectiveGroups.isEmpty()">
+                    UNION
+                        SELECT
+                            guacamole_entity.entity_id
+                        FROM guacamole_entity
+                        JOIN guacamole_user_group ON 
guacamole_user_group.entity_id = guacamole_entity.entity_id
+                        WHERE
+                            type = 'USER_GROUP'
+                            AND name IN
+                                <foreach collection="effectiveGroups" 
item="effectiveGroup"
+                                         open="(" separator="," close=")">
+                                    #{effectiveGroup,jdbcType=VARCHAR}
+                                </foreach>
+                            AND guacamole_user_group.disabled = false
+                </if>
+                UNION
+                    SELECT
+                        guacamole_user_group.entity_id
+                    FROM related_entity
+                    JOIN guacamole_user_group_member ON 
related_entity.entity_id = guacamole_user_group_member.member_entity_id
+                    JOIN guacamole_user_group ON 
guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
+                    WHERE
+                        guacamole_user_group.disabled = false
+            )
+            SELECT name
+            FROM related_entity
+            JOIN guacamole_entity ON related_entity.entity_id = 
guacamole_entity.entity_id
+            WHERE
+                guacamole_entity.type = 'USER_GROUP';
+        </if>
 
     </select>
 

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLEnvironment.java
index d5d259e..4ac99e8 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-postgresql/src/main/java/org/apache/guacamole/auth/postgresql/PostgreSQLEnvironment.java
@@ -24,6 +24,7 @@ import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
+import org.apache.ibatis.session.SqlSession;
 
 /**
  * A PostgreSQL-specific implementation of JDBCEnvironment provides database
@@ -244,7 +245,7 @@ public class PostgreSQLEnvironment extends JDBCEnvironment {
     }
 
     @Override
-    public boolean isRecursiveQuerySupported() {
+    public boolean isRecursiveQuerySupported(SqlSession session) {
         return true; // All versions of PostgreSQL support recursive queries 
through CTEs
     }
     

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/dec7b3c3/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
----------------------------------------------------------------------
diff --git 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
index 03f2cf8..db068b9 100644
--- 
a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
+++ 
b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-sqlserver/src/main/java/org/apache/guacamole/auth/sqlserver/SQLServerEnvironment.java
@@ -24,6 +24,7 @@ import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
+import org.apache.ibatis.session.SqlSession;
 
 /**
  * A SQLServer-specific implementation of JDBCEnvironment provides database
@@ -252,7 +253,7 @@ public class SQLServerEnvironment extends JDBCEnvironment {
     }
 
     @Override
-    public boolean isRecursiveQuerySupported() {
+    public boolean isRecursiveQuerySupported(SqlSession session) {
         return true; // All versions of SQL Server support recursive queries 
through CTEs
     }
 

Reply via email to