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 }