mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUACAMOLE-220: Dynamically detect whether the MariaDB / MySQL server supports recursive CTEs.
This commit is contained in:
@@ -22,6 +22,7 @@ package org.apache.guacamole.auth.jdbc;
|
|||||||
import org.apache.guacamole.GuacamoleException;
|
import org.apache.guacamole.GuacamoleException;
|
||||||
import org.apache.guacamole.environment.LocalEnvironment;
|
import org.apache.guacamole.environment.LocalEnvironment;
|
||||||
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A JDBC-specific implementation of Environment that defines generic properties
|
* 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
|
* not supported, queries that are intended to be recursive may need to be
|
||||||
* invoked multiple times to retrieve the same data.
|
* invoked multiple times to retrieve the same data.
|
||||||
*
|
*
|
||||||
|
* @param session
|
||||||
|
* The SqlSession provided by MyBatis for the current transaction.
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
* true if the database supports recursive queries, false otherwise.
|
* true if the database supports recursive queries, false otherwise.
|
||||||
*/
|
*/
|
||||||
public abstract boolean isRecursiveQuerySupported();
|
public abstract boolean isRecursiveQuerySupported(SqlSession session);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -60,12 +60,21 @@ public interface EntityMapper {
|
|||||||
* The identifiers of any known effective groups that should be taken
|
* The identifiers of any known effective groups that should be taken
|
||||||
* into account, such as those defined externally to the database.
|
* 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
|
* @return
|
||||||
* The set of identifiers of all groups that the given entity is a
|
* The set of identifiers of all groups that the given entity is a
|
||||||
* member of, including those where membership is inherited through
|
* member of, including those where membership is inherited through
|
||||||
* membership in other groups.
|
* membership in other groups.
|
||||||
*/
|
*/
|
||||||
Set<String> selectEffectiveGroupIdentifiers(@Param("entity") EntityModel entity,
|
Set<String> selectEffectiveGroupIdentifiers(@Param("entity") EntityModel entity,
|
||||||
@Param("effectiveGroups") Collection<String> effectiveGroups);
|
@Param("effectiveGroups") Collection<String> effectiveGroups,
|
||||||
|
@Param("recursive") boolean recursive);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,8 @@ import com.google.inject.Inject;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
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
|
* Service which provides convenience methods for creating, retrieving, and
|
||||||
@@ -42,6 +44,12 @@ public class EntityService {
|
|||||||
@Inject
|
@Inject
|
||||||
private EntityMapper entityMapper;
|
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
|
* Returns the set of all group identifiers of which the given entity is a
|
||||||
* member, taking into account the given collection of known group
|
* member, taking into account the given collection of known group
|
||||||
@@ -64,20 +72,22 @@ public class EntityService {
|
|||||||
* member of, including those where membership is inherited through
|
* member of, including those where membership is inherited through
|
||||||
* membership in other groups.
|
* membership in other groups.
|
||||||
*/
|
*/
|
||||||
|
@Transactional
|
||||||
public Set<String> retrieveEffectiveGroups(ModeledPermissions<? extends EntityModel> entity,
|
public Set<String> retrieveEffectiveGroups(ModeledPermissions<? extends EntityModel> entity,
|
||||||
Collection<String> effectiveGroups) {
|
Collection<String> effectiveGroups) {
|
||||||
|
|
||||||
// Retrieve the effective user groups of the given entity, recursively if possible
|
// 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,
|
// If the set of user groups retrieved was not produced recursively,
|
||||||
// manually repeat the query to expand the set until all effective
|
// manually repeat the query to expand the set until all effective
|
||||||
// groups have been found
|
// groups have been found
|
||||||
if (!environment.isRecursiveQuerySupported() && !identifiers.isEmpty()) {
|
if (!recursive && !identifiers.isEmpty()) {
|
||||||
Set<String> previousIdentifiers;
|
Set<String> previousIdentifiers;
|
||||||
do {
|
do {
|
||||||
previousIdentifiers = identifiers;
|
previousIdentifiers = identifiers;
|
||||||
identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), previousIdentifiers);
|
identifiers = entityMapper.selectEffectiveGroupIdentifiers(entity.getModel(), previousIdentifiers, false);
|
||||||
} while (identifiers.size() > previousIdentifiers.size());
|
} while (identifiers.size() > previousIdentifiers.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,11 +19,16 @@
|
|||||||
|
|
||||||
package org.apache.guacamole.auth.mysql;
|
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.GuacamoleException;
|
||||||
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
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
|
* A MySQL-specific implementation of JDBCEnvironment provides database
|
||||||
@@ -35,7 +40,17 @@ public class MySQLEnvironment extends JDBCEnvironment {
|
|||||||
* Logger for this class.
|
* Logger for this class.
|
||||||
*/
|
*/
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MySQLEnvironment.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.
|
* The default host to connect to, if MYSQL_HOSTNAME is not specified.
|
||||||
*/
|
*/
|
||||||
@@ -227,8 +242,39 @@ public class MySQLEnvironment extends JDBCEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRecursiveQuerySupported() {
|
public boolean isRecursiveQuerySupported(SqlSession session) {
|
||||||
return false; // Only very recent versions of MySQL / MariaDB support recursive queries through CTEs
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -65,43 +65,76 @@
|
|||||||
<!-- Select names of all effective groups (including inherited) -->
|
<!-- Select names of all effective groups (including inherited) -->
|
||||||
<select id="selectEffectiveGroupIdentifiers" resultType="string">
|
<select id="selectEffectiveGroupIdentifiers" resultType="string">
|
||||||
|
|
||||||
WITH RECURSIVE related_entity(entity_id) AS (
|
<if test="!recursive">
|
||||||
SELECT
|
SELECT
|
||||||
guacamole_user_group.entity_id
|
guacamole_entity.name
|
||||||
FROM guacamole_user_group
|
FROM guacamole_user_group
|
||||||
JOIN guacamole_user_group_member ON guacamole_user_group.user_group_id = guacamole_user_group_member.user_group_id
|
JOIN guacamole_entity ON guacamole_user_group.entity_id = guacamole_entity.entity_id
|
||||||
WHERE
|
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}
|
guacamole_user_group_member.member_entity_id = #{entity.entityID}
|
||||||
AND guacamole_user_group.disabled = false
|
<if test="!effectiveGroups.isEmpty()">
|
||||||
<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_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
|
||||||
|
guacamole_user_group_member.member_entity_id = #{entity.entityID}
|
||||||
|
AND guacamole_user_group.disabled = false
|
||||||
|
<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
|
UNION
|
||||||
SELECT
|
SELECT
|
||||||
guacamole_entity.entity_id
|
guacamole_user_group.entity_id
|
||||||
FROM guacamole_entity
|
FROM related_entity
|
||||||
JOIN guacamole_user_group ON guacamole_user_group.entity_id = guacamole_entity.entity_id
|
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
|
WHERE
|
||||||
type = 'USER_GROUP'
|
guacamole_user_group.disabled = false
|
||||||
AND name IN
|
)
|
||||||
<foreach collection="effectiveGroups" item="effectiveGroup"
|
SELECT name
|
||||||
open="(" separator="," close=")">
|
FROM related_entity
|
||||||
#{effectiveGroup,jdbcType=VARCHAR}
|
JOIN guacamole_entity ON related_entity.entity_id = guacamole_entity.entity_id
|
||||||
</foreach>
|
WHERE
|
||||||
AND guacamole_user_group.disabled = false
|
guacamole_entity.type = 'USER_GROUP';
|
||||||
</if>
|
</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';
|
|
||||||
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A PostgreSQL-specific implementation of JDBCEnvironment provides database
|
* A PostgreSQL-specific implementation of JDBCEnvironment provides database
|
||||||
@@ -244,7 +245,7 @@ public class PostgreSQLEnvironment extends JDBCEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRecursiveQuerySupported() {
|
public boolean isRecursiveQuerySupported(SqlSession session) {
|
||||||
return true; // All versions of PostgreSQL support recursive queries through CTEs
|
return true; // All versions of PostgreSQL support recursive queries through CTEs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@ import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
import org.apache.guacamole.auth.jdbc.security.PasswordPolicy;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQLServer-specific implementation of JDBCEnvironment provides database
|
* A SQLServer-specific implementation of JDBCEnvironment provides database
|
||||||
@@ -252,7 +253,7 @@ public class SQLServerEnvironment extends JDBCEnvironment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isRecursiveQuerySupported() {
|
public boolean isRecursiveQuerySupported(SqlSession session) {
|
||||||
return true; // All versions of SQL Server support recursive queries through CTEs
|
return true; // All versions of SQL Server support recursive queries through CTEs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user