Merge branch 'master' into complex_key_direct

This commit is contained in:
alexey-milovidov 2020-05-17 11:55:01 +03:00 committed by GitHub
commit 6d34f24605
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
385 changed files with 14974 additions and 4366 deletions

3
.gitmodules vendored
View File

@ -154,3 +154,6 @@
[submodule "contrib/libcpuid"]
path = contrib/libcpuid
url = https://github.com/ClickHouse-Extras/libcpuid.git
[submodule "contrib/openldap"]
path = contrib/openldap
url = https://github.com/openldap/openldap.git

View File

@ -331,6 +331,7 @@ include (cmake/lib_name.cmake)
find_contrib_lib(double-conversion) # Must be before parquet
include (cmake/find/ssl.cmake)
include (cmake/find/ldap.cmake) # after ssl
include (cmake/find/icu.cmake)
include (cmake/find/boost.cmake)
include (cmake/find/zlib.cmake)

View File

@ -1,42 +1,62 @@
#pragma once
#include <type_traits>
#include <boost/range/counting_range.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <type_traits>
namespace ext
{
/// For loop adaptor which is used to iterate through a half-closed interval [begin, end).
template <typename BeginType, typename EndType>
inline auto range(BeginType begin, EndType end)
namespace internal
{
template <typename ResultType, typename CountingType, typename BeginType, typename EndType>
auto rangeImpl(BeginType begin, EndType end)
{
using CommonType = typename std::common_type<BeginType, EndType>::type;
return boost::counting_range<CommonType>(begin, end);
}
template <typename Type>
inline auto range(Type end)
{
return range<Type, Type>(static_cast<Type>(0), end);
}
/// The same as range(), but every value is casted statically to a specified `ValueType`.
/// This is useful to iterate through all constants of a enum.
template <typename ValueType, typename BeginType, typename EndType>
inline auto range_with_static_cast(BeginType begin, EndType end)
{
using CommonType = typename std::common_type<BeginType, EndType>::type;
if constexpr (std::is_same_v<ValueType, CommonType>)
return boost::counting_range<CommonType>(begin, end);
if constexpr (std::is_same_v<ResultType, CountingType>)
return boost::counting_range<CountingType>(static_cast<CountingType>(begin), static_cast<CountingType>(end));
else
return boost::counting_range<CommonType>(begin, end)
| boost::adaptors::transformed([](CommonType x) -> ValueType { return static_cast<ValueType>(x); });
}
template <typename ValueType, typename EndType>
inline auto range_with_static_cast(EndType end)
{
return range_with_static_cast<ValueType, EndType, EndType>(static_cast<EndType>(0), end);
return boost::counting_range<CountingType>(static_cast<CountingType>(begin), static_cast<CountingType>(end))
| boost::adaptors::transformed([](CountingType x) { return static_cast<ResultType>(x); });
}
}
/// For loop adaptor which is used to iterate through a half-closed interval [begin, end).
/// The parameters `begin` and `end` can have any integral or enum types.
template <typename BeginType,
typename EndType,
typename = std::enable_if_t<
(std::is_integral_v<BeginType> || std::is_enum_v<BeginType>) &&
(std::is_integral_v<EndType> || std::is_enum_v<EndType>) &&
(!std::is_enum_v<BeginType> || !std::is_enum_v<EndType> || std::is_same_v<BeginType, EndType>), void>>
inline auto range(BeginType begin, EndType end)
{
if constexpr (std::is_integral_v<BeginType> && std::is_integral_v<EndType>)
{
using CommonType = std::common_type_t<BeginType, EndType>;
return internal::rangeImpl<CommonType, CommonType>(begin, end);
}
else if constexpr (std::is_enum_v<BeginType>)
{
return internal::rangeImpl<BeginType, std::underlying_type_t<BeginType>>(begin, end);
}
else
{
return internal::rangeImpl<EndType, std::underlying_type_t<EndType>>(begin, end);
}
}
/// For loop adaptor which is used to iterate through a half-closed interval [0, end).
/// The parameter `end` can have any integral or enum type.
/// The same as range(0, end).
template <typename Type,
typename = std::enable_if_t<std::is_integral_v<Type> || std::is_enum_v<Type>, void>>
inline auto range(Type end)
{
if constexpr (std::is_integral_v<Type>)
return internal::rangeImpl<Type, Type>(0, end);
else
return internal::rangeImpl<Type, std::underlying_type_t<Type>>(0, end);
}
}

View File

@ -0,0 +1,55 @@
# Find OpenLDAP libraries.
#
# Can be configured with:
# OPENLDAP_ROOT_DIR - path to the OpenLDAP installation prefix
# OPENLDAP_USE_STATIC_LIBS - look for static version of the libraries
# OPENLDAP_USE_REENTRANT_LIBS - look for thread-safe version of the libraries
#
# Sets values of:
# OPENLDAP_FOUND - TRUE if found
# OPENLDAP_INCLUDE_DIR - path to the include directory
# OPENLDAP_LIBRARIES - paths to the libldap and liblber libraries
# OPENLDAP_LDAP_LIBRARY - paths to the libldap library
# OPENLDAP_LBER_LIBRARY - paths to the liblber library
#
if(OPENLDAP_USE_STATIC_LIBS)
set(_orig_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
if(WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a" ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
endif()
endif()
set(_r_suffix)
if(OPENLDAP_USE_REENTRANT_LIBS)
set(_r_suffix "_r")
endif()
if(OPENLDAP_ROOT_DIR)
find_path(OPENLDAP_INCLUDE_DIR NAMES "ldap.h" "lber.h" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "include" NO_DEFAULT_PATH)
find_library(OPENLDAP_LDAP_LIBRARY NAMES "ldap${_r_suffix}" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "lib" NO_DEFAULT_PATH)
find_library(OPENLDAP_LBER_LIBRARY NAMES "lber" PATHS "${OPENLDAP_ROOT_DIR}" PATH_SUFFIXES "lib" NO_DEFAULT_PATH)
else()
find_path(OPENLDAP_INCLUDE_DIR NAMES "ldap.h" "lber.h")
find_library(OPENLDAP_LDAP_LIBRARY NAMES "ldap${_r_suffix}")
find_library(OPENLDAP_LBER_LIBRARY NAMES "lber")
endif()
unset(_r_suffix)
set(OPENLDAP_LIBRARIES ${OPENLDAP_LDAP_LIBRARY} ${OPENLDAP_LBER_LIBRARY})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
OpenLDAP DEFAULT_MSG
OPENLDAP_INCLUDE_DIR OPENLDAP_LDAP_LIBRARY OPENLDAP_LBER_LIBRARY
)
mark_as_advanced(OPENLDAP_INCLUDE_DIR OPENLDAP_LIBRARIES OPENLDAP_LDAP_LIBRARY OPENLDAP_LBER_LIBRARY)
if(OPENLDAP_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_orig_CMAKE_FIND_LIBRARY_SUFFIXES})
unset(_orig_CMAKE_FIND_LIBRARY_SUFFIXES)
endif()

76
cmake/find/ldap.cmake Normal file
View File

@ -0,0 +1,76 @@
option (ENABLE_LDAP "Enable LDAP" ${ENABLE_LIBRARIES})
if (ENABLE_LDAP)
option (USE_INTERNAL_LDAP_LIBRARY "Set to FALSE to use system *LDAP library instead of bundled" ${NOT_UNBUNDLED})
if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/openldap/README")
if (USE_INTERNAL_LDAP_LIBRARY)
message (WARNING "Submodule contrib/openldap is missing. To fix try running:\n git submodule update --init --recursive")
endif ()
set (USE_INTERNAL_LDAP_LIBRARY 0)
set (MISSING_INTERNAL_LDAP_LIBRARY 1)
endif ()
set (OPENLDAP_USE_STATIC_LIBS ${USE_STATIC_LIBRARIES})
set (OPENLDAP_USE_REENTRANT_LIBS 1)
if (NOT USE_INTERNAL_LDAP_LIBRARY)
if (APPLE AND NOT OPENLDAP_ROOT_DIR)
set (OPENLDAP_ROOT_DIR "/usr/local/opt/openldap")
endif ()
find_package (OpenLDAP)
endif ()
if (NOT OPENLDAP_FOUND AND NOT MISSING_INTERNAL_LDAP_LIBRARY)
string (TOLOWER "${CMAKE_SYSTEM_NAME}" _system_name)
string (TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _system_processor)
if (
"${_system_processor}" STREQUAL "amd64" OR
"${_system_processor}" STREQUAL "x64"
)
set (_system_processor "x86_64")
elseif (
"${_system_processor}" STREQUAL "arm64"
)
set (_system_processor "aarch64")
endif ()
if (
( "${_system_name}" STREQUAL "linux" AND "${_system_processor}" STREQUAL "x86_64" ) OR
( "${_system_name}" STREQUAL "linux" AND "${_system_processor}" STREQUAL "aarch64" ) OR
( "${_system_name}" STREQUAL "freebsd" AND "${_system_processor}" STREQUAL "x86_64" ) OR
( "${_system_name}" STREQUAL "darwin" AND "${_system_processor}" STREQUAL "x86_64" )
)
set (_ldap_supported_platform TRUE)
endif ()
if (NOT _ldap_supported_platform)
message (WARNING "LDAP support using the bundled library is not implemented for ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR} platform.")
elseif (NOT USE_SSL)
message (WARNING "LDAP support using the bundled library is not possible if SSL is not used.")
else ()
set (USE_INTERNAL_LDAP_LIBRARY 1)
set (OPENLDAP_ROOT_DIR "${ClickHouse_SOURCE_DIR}/contrib/openldap")
set (OPENLDAP_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/openldap/include")
# Below, 'ldap'/'ldap_r' and 'lber' will be resolved to
# the targets defined in contrib/openldap-cmake/CMakeLists.txt
if (OPENLDAP_USE_REENTRANT_LIBS)
set (OPENLDAP_LDAP_LIBRARY "ldap_r")
else ()
set (OPENLDAP_LDAP_LIBRARY "ldap")
endif()
set (OPENLDAP_LBER_LIBRARY "lber")
set (OPENLDAP_LIBRARIES ${OPENLDAP_LDAP_LIBRARY} ${OPENLDAP_LBER_LIBRARY})
set (OPENLDAP_FOUND 1)
endif ()
endif ()
if (OPENLDAP_FOUND)
set (USE_LDAP 1)
endif ()
endif ()
message (STATUS "Using ldap=${USE_LDAP}: ${OPENLDAP_INCLUDE_DIR} : ${OPENLDAP_LIBRARIES}")

View File

@ -1,8 +1,8 @@
set (CMAKE_SYSTEM_NAME "FreeBSD")
set (CMAKE_SYSTEM_PROCESSOR "x86_64")
set (CMAKE_C_COMPILER_TARGET "x86_64-pc-freebsd12.1")
set (CMAKE_CXX_COMPILER_TARGET "x86_64-pc-freebsd12.1")
set (CMAKE_ASM_COMPILER_TARGET "x86_64-pc-freebsd12.1")
set (CMAKE_C_COMPILER_TARGET "x86_64-pc-freebsd11")
set (CMAKE_CXX_COMPILER_TARGET "x86_64-pc-freebsd11")
set (CMAKE_ASM_COMPILER_TARGET "x86_64-pc-freebsd11")
set (CMAKE_SYSROOT "${CMAKE_CURRENT_LIST_DIR}/../toolchain/freebsd-x86_64")
set (CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # disable linkage check - it doesn't work in CMake

View File

@ -21,7 +21,7 @@ set(CMAKE_C_STANDARD_LIBRARIES ${DEFAULT_LIBS})
# glibc-compatibility library relies to fixed version of libc headers
# (because minor changes in function attributes between different glibc versions will introduce incompatibilities)
# This is for x86_64. For other architectures we have separate toolchains.
if (ARCH_AMD64)
if (ARCH_AMD64 AND NOT_UNBUNDLED)
set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES ${ClickHouse_SOURCE_DIR}/contrib/libc-headers/x86_64-linux-gnu ${ClickHouse_SOURCE_DIR}/contrib/libc-headers)
set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${ClickHouse_SOURCE_DIR}/contrib/libc-headers/x86_64-linux-gnu ${ClickHouse_SOURCE_DIR}/contrib/libc-headers)
endif ()

View File

@ -110,6 +110,10 @@ if (USE_INTERNAL_SSL_LIBRARY)
add_library(OpenSSL::SSL ALIAS ${OPENSSL_SSL_LIBRARY})
endif ()
if (ENABLE_LDAP AND USE_INTERNAL_LDAP_LIBRARY)
add_subdirectory (openldap-cmake)
endif ()
function(mysql_support)
set(CLIENT_PLUGIN_CACHING_SHA2_PASSWORD STATIC)
set(CLIENT_PLUGIN_SHA256_PASSWORD STATIC)

1
contrib/openldap vendored Submodule

@ -0,0 +1 @@
Subproject commit 34b9ba94b30319ed6389a4e001d057f7983fe363

View File

@ -0,0 +1,202 @@
set(OPENLDAP_SOURCE_DIR ${ClickHouse_SOURCE_DIR}/contrib/openldap)
# How these lists were generated?
# I compiled the original OpenLDAP with it's original build system and copied the list of source files from build commands.
set(_libs_type SHARED)
if(OPENLDAP_USE_STATIC_LIBS)
set(_libs_type STATIC)
endif()
set(OPENLDAP_VERSION_STRING "2.5.X")
macro(mkversion _lib_name)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_lib_name}-version.c
COMMAND ${CMAKE_COMMAND} -E env bash -c "${OPENLDAP_SOURCE_DIR}/build/mkversion -v '${OPENLDAP_VERSION_STRING}' liblber.la > ${CMAKE_CURRENT_BINARY_DIR}/${_lib_name}-version.c"
MAIN_DEPENDENCY ${OPENLDAP_SOURCE_DIR}/build/mkversion
WORKING_DIRECTORY ${OPENLDAP_SOURCE_DIR}
VERBATIM
)
endmacro()
string(TOLOWER "${CMAKE_SYSTEM_NAME}" _system_name)
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _system_processor)
if(
"${_system_processor}" STREQUAL "amd64" OR
"${_system_processor}" STREQUAL "x64"
)
set(_system_processor "x86_64")
elseif(
"${_system_processor}" STREQUAL "arm64"
)
set (_system_processor "aarch64")
endif()
set(_extra_build_dir "${CMAKE_CURRENT_SOURCE_DIR}/${_system_name}_${_system_processor}")
set(_lber_srcs
${OPENLDAP_SOURCE_DIR}/libraries/liblber/assert.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/decode.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/encode.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/io.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/bprint.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/debug.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/memory.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/options.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/sockbuf.c
${OPENLDAP_SOURCE_DIR}/libraries/liblber/stdio.c
)
mkversion(lber)
add_library(lber ${_libs_type}
${_lber_srcs}
${CMAKE_CURRENT_BINARY_DIR}/lber-version.c
)
target_link_libraries(lber
PRIVATE ${OPENSSL_LIBRARIES}
)
target_include_directories(lber
PRIVATE ${_extra_build_dir}/include
PRIVATE ${OPENLDAP_SOURCE_DIR}/include
PRIVATE ${OPENLDAP_SOURCE_DIR}/libraries/liblber
PRIVATE ${OPENSSL_INCLUDE_DIR}
)
target_compile_definitions(lber
PRIVATE LBER_LIBRARY
)
set(_ldap_srcs
${OPENLDAP_SOURCE_DIR}/libraries/libldap/bind.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/open.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/result.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/error.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/compare.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/search.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/controls.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/messages.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/references.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/extended.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/cyrus.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/modify.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/add.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/modrdn.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/delete.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/abandon.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/sasl.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/sbind.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/unbind.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/cancel.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/filter.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/free.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/sort.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/passwd.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/whoami.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/vc.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/getdn.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/getentry.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/getattr.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/getvalues.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/addentry.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/request.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/os-ip.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/url.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/pagectrl.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/sortctrl.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/vlvctrl.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/init.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/options.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/print.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/string.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/util-int.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/schema.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/charray.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/os-local.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/dnssrv.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/utf-8.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/utf-8-conv.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/tls2.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/tls_o.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/tls_g.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/turn.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/ppolicy.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/dds.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/txn.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/ldap_sync.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/stctrl.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/assertion.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/deref.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/ldifutil.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/ldif.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/fetch.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/lbase64.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/msctrl.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap/psearchctrl.c
)
mkversion(ldap)
add_library(ldap ${_libs_type}
${_ldap_srcs}
${CMAKE_CURRENT_BINARY_DIR}/ldap-version.c
)
target_link_libraries(ldap
PRIVATE lber
PRIVATE ${OPENSSL_LIBRARIES}
)
target_include_directories(ldap
PRIVATE ${_extra_build_dir}/include
PRIVATE ${OPENLDAP_SOURCE_DIR}/include
PRIVATE ${OPENLDAP_SOURCE_DIR}/libraries/libldap
PRIVATE ${OPENSSL_INCLUDE_DIR}
)
target_compile_definitions(ldap
PRIVATE LDAP_LIBRARY
)
set(_ldap_r_specific_srcs
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/threads.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/rdwr.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/tpool.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/rq.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/thr_posix.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/thr_thr.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/thr_nt.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/thr_pth.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/thr_stub.c
${OPENLDAP_SOURCE_DIR}/libraries/libldap_r/thr_debug.c
)
mkversion(ldap_r)
add_library(ldap_r ${_libs_type}
${_ldap_r_specific_srcs}
${_ldap_srcs}
${CMAKE_CURRENT_BINARY_DIR}/ldap_r-version.c
)
target_link_libraries(ldap_r
PRIVATE lber
PRIVATE ${OPENSSL_LIBRARIES}
)
target_include_directories(ldap_r
PRIVATE ${_extra_build_dir}/include
PRIVATE ${OPENLDAP_SOURCE_DIR}/include
PRIVATE ${OPENLDAP_SOURCE_DIR}/libraries/libldap_r
PRIVATE ${OPENLDAP_SOURCE_DIR}/libraries/libldap
PRIVATE ${OPENSSL_INCLUDE_DIR}
)
target_compile_definitions(ldap_r
PRIVATE LDAP_R_COMPILE
PRIVATE LDAP_LIBRARY
)

View File

@ -0,0 +1,63 @@
/* include/lber_types.h. Generated from lber_types.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LBER types
*/
#ifndef _LBER_TYPES_H
#define _LBER_TYPES_H
#include <ldap_cdefs.h>
LDAP_BEGIN_DECL
/* LBER boolean, enum, integers (32 bits or larger) */
#define LBER_INT_T int
/* LBER tags (32 bits or larger) */
#define LBER_TAG_T long
/* LBER socket descriptor */
#define LBER_SOCKET_T int
/* LBER lengths (32 bits or larger) */
#define LBER_LEN_T long
/* ------------------------------------------------------------ */
/* booleans, enumerations, and integers */
typedef LBER_INT_T ber_int_t;
/* signed and unsigned versions */
typedef signed LBER_INT_T ber_sint_t;
typedef unsigned LBER_INT_T ber_uint_t;
/* tags */
typedef unsigned LBER_TAG_T ber_tag_t;
/* "socket" descriptors */
typedef LBER_SOCKET_T ber_socket_t;
/* lengths */
typedef unsigned LBER_LEN_T ber_len_t;
/* signed lengths */
typedef signed LBER_LEN_T ber_slen_t;
LDAP_END_DECL
#endif /* _LBER_TYPES_H */

View File

@ -0,0 +1,74 @@
/* include/ldap_config.h. Generated from ldap_config.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* This file works in conjunction with OpenLDAP configure system.
* If you do no like the values below, adjust your configure options.
*/
#ifndef _LDAP_CONFIG_H
#define _LDAP_CONFIG_H
/* directory separator */
#ifndef LDAP_DIRSEP
#ifndef _WIN32
#define LDAP_DIRSEP "/"
#else
#define LDAP_DIRSEP "\\"
#endif
#endif
/* directory for temporary files */
#if defined(_WIN32)
# define LDAP_TMPDIR "C:\\." /* we don't have much of a choice */
#elif defined( _P_tmpdir )
# define LDAP_TMPDIR _P_tmpdir
#elif defined( P_tmpdir )
# define LDAP_TMPDIR P_tmpdir
#elif defined( _PATH_TMPDIR )
# define LDAP_TMPDIR _PATH_TMPDIR
#else
# define LDAP_TMPDIR LDAP_DIRSEP "tmp"
#endif
/* directories */
#ifndef LDAP_BINDIR
#define LDAP_BINDIR "/tmp/ldap-prefix/bin"
#endif
#ifndef LDAP_SBINDIR
#define LDAP_SBINDIR "/tmp/ldap-prefix/sbin"
#endif
#ifndef LDAP_DATADIR
#define LDAP_DATADIR "/tmp/ldap-prefix/share/openldap"
#endif
#ifndef LDAP_SYSCONFDIR
#define LDAP_SYSCONFDIR "/tmp/ldap-prefix/etc/openldap"
#endif
#ifndef LDAP_LIBEXECDIR
#define LDAP_LIBEXECDIR "/tmp/ldap-prefix/libexec"
#endif
#ifndef LDAP_MODULEDIR
#define LDAP_MODULEDIR "/tmp/ldap-prefix/libexec/openldap"
#endif
#ifndef LDAP_RUNDIR
#define LDAP_RUNDIR "/tmp/ldap-prefix/var"
#endif
#ifndef LDAP_LOCALEDIR
#define LDAP_LOCALEDIR ""
#endif
#endif /* _LDAP_CONFIG_H */

View File

@ -0,0 +1,61 @@
/* include/ldap_features.h. Generated from ldap_features.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LDAP Features
*/
#ifndef _LDAP_FEATURES_H
#define _LDAP_FEATURES_H 1
/* OpenLDAP API version macros */
#define LDAP_VENDOR_VERSION 20501
#define LDAP_VENDOR_VERSION_MAJOR 2
#define LDAP_VENDOR_VERSION_MINOR 5
#define LDAP_VENDOR_VERSION_PATCH X
/*
** WORK IN PROGRESS!
**
** OpenLDAP reentrancy/thread-safeness should be dynamically
** checked using ldap_get_option().
**
** The -lldap implementation is not thread-safe.
**
** The -lldap_r implementation is:
** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety)
** but also be:
** LDAP_API_FEATURE_SESSION_THREAD_SAFE
** LDAP_API_FEATURE_OPERATION_THREAD_SAFE
**
** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
** can be used to determine if -lldap_r is available at compile
** time. You must define LDAP_THREAD_SAFE if and only if you
** link with -lldap_r.
**
** If you fail to define LDAP_THREAD_SAFE when linking with
** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap,
** provided header definitions and declarations may be incorrect.
**
*/
/* is -lldap_r available or not */
#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1
/* LDAP v2 Referrals */
/* #undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
#endif /* LDAP_FEATURES */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
/* include/lber_types.h. Generated from lber_types.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LBER types
*/
#ifndef _LBER_TYPES_H
#define _LBER_TYPES_H
#include <ldap_cdefs.h>
LDAP_BEGIN_DECL
/* LBER boolean, enum, integers (32 bits or larger) */
#define LBER_INT_T int
/* LBER tags (32 bits or larger) */
#define LBER_TAG_T long
/* LBER socket descriptor */
#define LBER_SOCKET_T int
/* LBER lengths (32 bits or larger) */
#define LBER_LEN_T long
/* ------------------------------------------------------------ */
/* booleans, enumerations, and integers */
typedef LBER_INT_T ber_int_t;
/* signed and unsigned versions */
typedef signed LBER_INT_T ber_sint_t;
typedef unsigned LBER_INT_T ber_uint_t;
/* tags */
typedef unsigned LBER_TAG_T ber_tag_t;
/* "socket" descriptors */
typedef LBER_SOCKET_T ber_socket_t;
/* lengths */
typedef unsigned LBER_LEN_T ber_len_t;
/* signed lengths */
typedef signed LBER_LEN_T ber_slen_t;
LDAP_END_DECL
#endif /* _LBER_TYPES_H */

View File

@ -0,0 +1,74 @@
/* include/ldap_config.h. Generated from ldap_config.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* This file works in conjunction with OpenLDAP configure system.
* If you do no like the values below, adjust your configure options.
*/
#ifndef _LDAP_CONFIG_H
#define _LDAP_CONFIG_H
/* directory separator */
#ifndef LDAP_DIRSEP
#ifndef _WIN32
#define LDAP_DIRSEP "/"
#else
#define LDAP_DIRSEP "\\"
#endif
#endif
/* directory for temporary files */
#if defined(_WIN32)
# define LDAP_TMPDIR "C:\\." /* we don't have much of a choice */
#elif defined( _P_tmpdir )
# define LDAP_TMPDIR _P_tmpdir
#elif defined( P_tmpdir )
# define LDAP_TMPDIR P_tmpdir
#elif defined( _PATH_TMPDIR )
# define LDAP_TMPDIR _PATH_TMPDIR
#else
# define LDAP_TMPDIR LDAP_DIRSEP "tmp"
#endif
/* directories */
#ifndef LDAP_BINDIR
#define LDAP_BINDIR "/tmp/ldap-prefix/bin"
#endif
#ifndef LDAP_SBINDIR
#define LDAP_SBINDIR "/tmp/ldap-prefix/sbin"
#endif
#ifndef LDAP_DATADIR
#define LDAP_DATADIR "/tmp/ldap-prefix/share/openldap"
#endif
#ifndef LDAP_SYSCONFDIR
#define LDAP_SYSCONFDIR "/tmp/ldap-prefix/etc/openldap"
#endif
#ifndef LDAP_LIBEXECDIR
#define LDAP_LIBEXECDIR "/tmp/ldap-prefix/libexec"
#endif
#ifndef LDAP_MODULEDIR
#define LDAP_MODULEDIR "/tmp/ldap-prefix/libexec/openldap"
#endif
#ifndef LDAP_RUNDIR
#define LDAP_RUNDIR "/tmp/ldap-prefix/var"
#endif
#ifndef LDAP_LOCALEDIR
#define LDAP_LOCALEDIR ""
#endif
#endif /* _LDAP_CONFIG_H */

View File

@ -0,0 +1,61 @@
/* include/ldap_features.h. Generated from ldap_features.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LDAP Features
*/
#ifndef _LDAP_FEATURES_H
#define _LDAP_FEATURES_H 1
/* OpenLDAP API version macros */
#define LDAP_VENDOR_VERSION 20501
#define LDAP_VENDOR_VERSION_MAJOR 2
#define LDAP_VENDOR_VERSION_MINOR 5
#define LDAP_VENDOR_VERSION_PATCH X
/*
** WORK IN PROGRESS!
**
** OpenLDAP reentrancy/thread-safeness should be dynamically
** checked using ldap_get_option().
**
** The -lldap implementation is not thread-safe.
**
** The -lldap_r implementation is:
** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety)
** but also be:
** LDAP_API_FEATURE_SESSION_THREAD_SAFE
** LDAP_API_FEATURE_OPERATION_THREAD_SAFE
**
** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
** can be used to determine if -lldap_r is available at compile
** time. You must define LDAP_THREAD_SAFE if and only if you
** link with -lldap_r.
**
** If you fail to define LDAP_THREAD_SAFE when linking with
** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap,
** provided header definitions and declarations may be incorrect.
**
*/
/* is -lldap_r available or not */
#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1
/* LDAP v2 Referrals */
/* #undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
#endif /* LDAP_FEATURES */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
/* include/lber_types.h. Generated from lber_types.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LBER types
*/
#ifndef _LBER_TYPES_H
#define _LBER_TYPES_H
#include <ldap_cdefs.h>
LDAP_BEGIN_DECL
/* LBER boolean, enum, integers (32 bits or larger) */
#define LBER_INT_T int
/* LBER tags (32 bits or larger) */
#define LBER_TAG_T long
/* LBER socket descriptor */
#define LBER_SOCKET_T int
/* LBER lengths (32 bits or larger) */
#define LBER_LEN_T long
/* ------------------------------------------------------------ */
/* booleans, enumerations, and integers */
typedef LBER_INT_T ber_int_t;
/* signed and unsigned versions */
typedef signed LBER_INT_T ber_sint_t;
typedef unsigned LBER_INT_T ber_uint_t;
/* tags */
typedef unsigned LBER_TAG_T ber_tag_t;
/* "socket" descriptors */
typedef LBER_SOCKET_T ber_socket_t;
/* lengths */
typedef unsigned LBER_LEN_T ber_len_t;
/* signed lengths */
typedef signed LBER_LEN_T ber_slen_t;
LDAP_END_DECL
#endif /* _LBER_TYPES_H */

View File

@ -0,0 +1,74 @@
/* include/ldap_config.h. Generated from ldap_config.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* This file works in conjunction with OpenLDAP configure system.
* If you do no like the values below, adjust your configure options.
*/
#ifndef _LDAP_CONFIG_H
#define _LDAP_CONFIG_H
/* directory separator */
#ifndef LDAP_DIRSEP
#ifndef _WIN32
#define LDAP_DIRSEP "/"
#else
#define LDAP_DIRSEP "\\"
#endif
#endif
/* directory for temporary files */
#if defined(_WIN32)
# define LDAP_TMPDIR "C:\\." /* we don't have much of a choice */
#elif defined( _P_tmpdir )
# define LDAP_TMPDIR _P_tmpdir
#elif defined( P_tmpdir )
# define LDAP_TMPDIR P_tmpdir
#elif defined( _PATH_TMPDIR )
# define LDAP_TMPDIR _PATH_TMPDIR
#else
# define LDAP_TMPDIR LDAP_DIRSEP "tmp"
#endif
/* directories */
#ifndef LDAP_BINDIR
#define LDAP_BINDIR "/tmp/ldap-prefix/bin"
#endif
#ifndef LDAP_SBINDIR
#define LDAP_SBINDIR "/tmp/ldap-prefix/sbin"
#endif
#ifndef LDAP_DATADIR
#define LDAP_DATADIR "/tmp/ldap-prefix/share/openldap"
#endif
#ifndef LDAP_SYSCONFDIR
#define LDAP_SYSCONFDIR "/tmp/ldap-prefix/etc/openldap"
#endif
#ifndef LDAP_LIBEXECDIR
#define LDAP_LIBEXECDIR "/tmp/ldap-prefix/libexec"
#endif
#ifndef LDAP_MODULEDIR
#define LDAP_MODULEDIR "/tmp/ldap-prefix/libexec/openldap"
#endif
#ifndef LDAP_RUNDIR
#define LDAP_RUNDIR "/tmp/ldap-prefix/var"
#endif
#ifndef LDAP_LOCALEDIR
#define LDAP_LOCALEDIR ""
#endif
#endif /* _LDAP_CONFIG_H */

View File

@ -0,0 +1,61 @@
/* include/ldap_features.h. Generated from ldap_features.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LDAP Features
*/
#ifndef _LDAP_FEATURES_H
#define _LDAP_FEATURES_H 1
/* OpenLDAP API version macros */
#define LDAP_VENDOR_VERSION 20501
#define LDAP_VENDOR_VERSION_MAJOR 2
#define LDAP_VENDOR_VERSION_MINOR 5
#define LDAP_VENDOR_VERSION_PATCH X
/*
** WORK IN PROGRESS!
**
** OpenLDAP reentrancy/thread-safeness should be dynamically
** checked using ldap_get_option().
**
** The -lldap implementation is not thread-safe.
**
** The -lldap_r implementation is:
** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety)
** but also be:
** LDAP_API_FEATURE_SESSION_THREAD_SAFE
** LDAP_API_FEATURE_OPERATION_THREAD_SAFE
**
** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
** can be used to determine if -lldap_r is available at compile
** time. You must define LDAP_THREAD_SAFE if and only if you
** link with -lldap_r.
**
** If you fail to define LDAP_THREAD_SAFE when linking with
** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap,
** provided header definitions and declarations may be incorrect.
**
*/
/* is -lldap_r available or not */
#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1
/* LDAP v2 Referrals */
/* #undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
#endif /* LDAP_FEATURES */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
/* include/lber_types.h. Generated from lber_types.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LBER types
*/
#ifndef _LBER_TYPES_H
#define _LBER_TYPES_H
#include <ldap_cdefs.h>
LDAP_BEGIN_DECL
/* LBER boolean, enum, integers (32 bits or larger) */
#define LBER_INT_T int
/* LBER tags (32 bits or larger) */
#define LBER_TAG_T long
/* LBER socket descriptor */
#define LBER_SOCKET_T int
/* LBER lengths (32 bits or larger) */
#define LBER_LEN_T long
/* ------------------------------------------------------------ */
/* booleans, enumerations, and integers */
typedef LBER_INT_T ber_int_t;
/* signed and unsigned versions */
typedef signed LBER_INT_T ber_sint_t;
typedef unsigned LBER_INT_T ber_uint_t;
/* tags */
typedef unsigned LBER_TAG_T ber_tag_t;
/* "socket" descriptors */
typedef LBER_SOCKET_T ber_socket_t;
/* lengths */
typedef unsigned LBER_LEN_T ber_len_t;
/* signed lengths */
typedef signed LBER_LEN_T ber_slen_t;
LDAP_END_DECL
#endif /* _LBER_TYPES_H */

View File

@ -0,0 +1,74 @@
/* include/ldap_config.h. Generated from ldap_config.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* This file works in conjunction with OpenLDAP configure system.
* If you do no like the values below, adjust your configure options.
*/
#ifndef _LDAP_CONFIG_H
#define _LDAP_CONFIG_H
/* directory separator */
#ifndef LDAP_DIRSEP
#ifndef _WIN32
#define LDAP_DIRSEP "/"
#else
#define LDAP_DIRSEP "\\"
#endif
#endif
/* directory for temporary files */
#if defined(_WIN32)
# define LDAP_TMPDIR "C:\\." /* we don't have much of a choice */
#elif defined( _P_tmpdir )
# define LDAP_TMPDIR _P_tmpdir
#elif defined( P_tmpdir )
# define LDAP_TMPDIR P_tmpdir
#elif defined( _PATH_TMPDIR )
# define LDAP_TMPDIR _PATH_TMPDIR
#else
# define LDAP_TMPDIR LDAP_DIRSEP "tmp"
#endif
/* directories */
#ifndef LDAP_BINDIR
#define LDAP_BINDIR "/tmp/ldap-prefix/bin"
#endif
#ifndef LDAP_SBINDIR
#define LDAP_SBINDIR "/tmp/ldap-prefix/sbin"
#endif
#ifndef LDAP_DATADIR
#define LDAP_DATADIR "/tmp/ldap-prefix/share/openldap"
#endif
#ifndef LDAP_SYSCONFDIR
#define LDAP_SYSCONFDIR "/tmp/ldap-prefix/etc/openldap"
#endif
#ifndef LDAP_LIBEXECDIR
#define LDAP_LIBEXECDIR "/tmp/ldap-prefix/libexec"
#endif
#ifndef LDAP_MODULEDIR
#define LDAP_MODULEDIR "/tmp/ldap-prefix/libexec/openldap"
#endif
#ifndef LDAP_RUNDIR
#define LDAP_RUNDIR "/tmp/ldap-prefix/var"
#endif
#ifndef LDAP_LOCALEDIR
#define LDAP_LOCALEDIR ""
#endif
#endif /* _LDAP_CONFIG_H */

View File

@ -0,0 +1,61 @@
/* include/ldap_features.h. Generated from ldap_features.hin by configure. */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 1998-2020 The OpenLDAP Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/*
* LDAP Features
*/
#ifndef _LDAP_FEATURES_H
#define _LDAP_FEATURES_H 1
/* OpenLDAP API version macros */
#define LDAP_VENDOR_VERSION 20501
#define LDAP_VENDOR_VERSION_MAJOR 2
#define LDAP_VENDOR_VERSION_MINOR 5
#define LDAP_VENDOR_VERSION_PATCH X
/*
** WORK IN PROGRESS!
**
** OpenLDAP reentrancy/thread-safeness should be dynamically
** checked using ldap_get_option().
**
** The -lldap implementation is not thread-safe.
**
** The -lldap_r implementation is:
** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety)
** but also be:
** LDAP_API_FEATURE_SESSION_THREAD_SAFE
** LDAP_API_FEATURE_OPERATION_THREAD_SAFE
**
** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
** can be used to determine if -lldap_r is available at compile
** time. You must define LDAP_THREAD_SAFE if and only if you
** link with -lldap_r.
**
** If you fail to define LDAP_THREAD_SAFE when linking with
** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap,
** provided header definitions and declarations may be incorrect.
**
*/
/* is -lldap_r available or not */
#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1
/* LDAP v2 Referrals */
/* #undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS */
#endif /* LDAP_FEATURES */

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,31 @@ toc_title: Playground
# ClickHouse Playground {#clickhouse-playground}
[ClickHouse Playground](https://play.clickhouse.tech?file=welcome) allows people to experiment with ClickHouse by running queries instantly, without setting up their server or cluster.
Several example datasets are available in the Playground as well as sample queries that show ClickHouse features.
[ClickHouse Playground](https://play.clickhouse.tech) allows people to experiment with ClickHouse by running queries instantly, without setting up their server or cluster.
Several example datasets are available in the Playground as well as sample queries that show ClickHouse features. There's also a selection of ClickHouse LTS releases to experiment with.
ClickHouse Playground gives the experience of m2.small [Managed Service for ClickHouse](https://cloud.yandex.com/services/managed-clickhouse) instance hosted in [Yandex.Cloud](https://cloud.yandex.com/). More information about [cloud providers](../commercial/cloud.md).
You can make queries to playground using any HTTP client, for example [curl](https://curl.haxx.se) or [wget](https://www.gnu.org/software/wget/), or set up a connection using [JDBC](../interfaces/jdbc.md) or [ODBC](../interfaces/odbc.md) drivers. More information about software products that support ClickHouse is available [here](../interfaces/index.md).
## Credentials
| Parameter | Value |
|:------------------|:----------------------------------------|
| HTTPS endpoint | `https://play-api.clickhouse.tech:8443` |
| Native endpoint | `play-api.clickhouse.tech:9440` |
| User | `playground` |
| Password | `clickhouse` |
!!! note "Note"
Note that all endpoints require a secure TLS connection.
There are additional endpoints with specific ClickHouse releases to experiment with their differences (ports and user/password are the same as above):
* 20.3 LTS: `play-api-v20-3.clickhouse.tech`
* 19.14 LTS: `play-api-v19-14.clickhouse.tech`
## Limitations
The queries are executed as a read-only user. It implies some limitations:
@ -14,33 +37,28 @@ The queries are executed as a read-only user. It implies some limitations:
- INSERT queries are not allowed
The following settings are also enforced:
- [`max_result_bytes=10485760`](../operations/settings/query_complexity/#max-result-bytes)
- [`max_result_rows=2000`](../operations/settings/query_complexity/#setting-max_result_rows)
- [`result_overflow_mode=break`](../operations/settings/query_complexity/#result-overflow-mode)
- [`max_execution_time=60000`](../operations/settings/query_complexity/#max-execution-time)
- [max_result_bytes=10485760](../operations/settings/query_complexity/#max-result-bytes)
- [max_result_rows=2000](../operations/settings/query_complexity/#setting-max_result_rows)
- [result_overflow_mode=break](../operations/settings/query_complexity/#result-overflow-mode)
- [max_execution_time=60000](../operations/settings/query_complexity/#max-execution-time)
ClickHouse Playground gives the experience of m2.small
[Managed Service for ClickHouse](https://cloud.yandex.com/services/managed-clickhouse)
instance hosted in [Yandex.Cloud](https://cloud.yandex.com/).
More information about [cloud providers](../commercial/cloud.md).
## Examples
ClickHouse Playground web interface makes requests via ClickHouse [HTTP API](../interfaces/http.md).
The Playground backend is just a ClickHouse cluster without any additional server-side application.
ClickHouse HTTPS endpoint is also available as a part of the Playground.
You can make queries to playground using any HTTP client, for example [curl](https://curl.haxx.se) or [wget](https://www.gnu.org/software/wget/), or set up a connection using [JDBC](../interfaces/jdbc.md) or [ODBC](../interfaces/odbc.md) drivers.
More information about software products that support ClickHouse is available [here](../interfaces/index.md).
| Parameter | Value |
|:----------|:--------------------------------------|
| Endpoint | https://play-api.clickhouse.tech:8443 |
| User | `playground` |
| Password | `clickhouse` |
Note that this endpoint requires a secure connection.
Example:
HTTPS endpoint example with `curl`:
``` bash
curl "https://play-api.clickhouse.tech:8443/?query=SELECT+'Play+ClickHouse!';&user=playground&password=clickhouse&database=datasets"
```
TCP endpoint example with [../interfaces/cli.md]:
``` bash
clickhouse client --secure -h play-api.clickhouse.tech --port 9440 -u playground --password clickhouse -q "SELECT 'Play ClickHouse!'"
```
## Implementation Details
ClickHouse Playground web interface makes requests via ClickHouse [HTTP API](../interfaces/http.md).
The Playground backend is just a ClickHouse cluster without any additional server-side application. As mentioned above, ClickHouse HTTPS and TCP/TLS endpoints are also publicly available as a part of the Playground, both are proxied through [Cloudflare Spectrum](https://www.cloudflare.com/products/cloudflare-spectrum/) to add extra layer of protection and improved global connectivity.
!!! warning "Warning"
Exposing ClickHouse server to public internet in any other situation is **strongly not recommended**. Make sure it listens only on private network and is covered by properly configured firewall.

View File

@ -22,7 +22,7 @@ You can configure access entities using:
- Server [configuration files](configuration-files.md) `users.xml` and `config.xml`.
We recommend using SQL-driven workflow. Both of the configuration methods work simultaneously, so if you use the server configuration files for managing accounts and access rights, you can softly move to SQL-driven workflow.
We recommend using SQL-driven workflow. Both of the configuration methods work simultaneously, so if you use the server configuration files for managing accounts and access rights, you can smoothly switch to SQL-driven workflow.
!!! note "Warning"
You can't manage the same access entity by both configuration methods simultaneously.
@ -30,32 +30,32 @@ We recommend using SQL-driven workflow. Both of the configuration methods work s
## Usage {#access-control-usage}
By default, the ClickHouse server provides the user account `default` which is not allowed using SQL-driven access control and account management but have all the rights and permissions. The `default` user account is used in any cases when the username is not defined, for example, at login from client or in distributed queries. In distributed query processing a default user account is used, if the configuration of the server or cluster doesnt specify the [user and password](../engines/table-engines/special/distributed.md) properties.
By default, the ClickHouse server provides the `default` user account which is not allowed using SQL-driven access control and account management but has all the rights and permissions. The `default` user account is used in any cases when the username is not defined, for example, at login from client or in distributed queries. In distributed query processing a default user account is used, if the configuration of the server or cluster doesnt specify the [user and password](../engines/table-engines/special/distributed.md) properties.
If you just start using ClickHouse, you can use the following scenario:
If you just started using ClickHouse, consider the following scenario:
1. [Enable](#enabling-access-control) SQL-driven access control and account management for the `default` user.
2. Login under the `default` user account and create all the required users. Don't forget to create an administrator account (`GRANT ALL ON *.* WITH GRANT OPTION TO admin_user_account`).
2. Log in to the `default` user account and create all the required users. Don't forget to create an administrator account (`GRANT ALL ON *.* WITH GRANT OPTION TO admin_user_account`).
3. [Restrict permissions](settings/permissions-for-queries.md#permissions_for_queries) for the `default` user and disable SQL-driven access control and account management for it.
### Properties of Current Solution {#access-control-properties}
- You can grant permissions for databases and tables even if they are not exist.
- If a table was deleted, all the privileges that correspond to this table are not revoked. So, if a new table is created later with the same name all the privileges become again actual. To revoke privileges corresponding to the deleted table, you need to perform, for example, the `REVOKE ALL PRIVILEGES ON db.table FROM ALL` query.
- There is no lifetime settings for privileges.
- You can grant permissions for databases and tables even if they do not exist.
- If a table was deleted, all the privileges that correspond to this table are not revoked. This means that even if you create a new table with the same name later, all the privileges remain valid. To revoke privileges corresponding to the deleted table, you need to execute, for example, the `REVOKE ALL PRIVILEGES ON db.table FROM ALL` query.
- There are no lifetime settings for privileges.
## User account {#user-account-management}
A user account is an access entity that allows to authorize someone in ClickHouse. A user account contains:
- Identification information.
- [Privileges](../sql-reference/statements/grant.md#grant-privileges) that define a scope of queries the user can perform.
- Hosts from which connection to the ClickHouse server is allowed.
- Granted and default roles.
- Settings with their constraints that apply by default at the user's login.
- [Privileges](../sql-reference/statements/grant.md#grant-privileges) that define a scope of queries the user can execute.
- Hosts allowed to connect to the ClickHouse server.
- Assigned and default roles.
- Settings with their constraints applied by default at user login.
- Assigned settings profiles.
Privileges to a user account can be granted by the [GRANT](../sql-reference/statements/grant.md) query or by assigning [roles](#role-management). To revoke privileges from a user, ClickHouse provides the [REVOKE](../sql-reference/statements/revoke.md) query. To list privileges for a user, use the - [SHOW GRANTS](../sql-reference/statements/show.md#show-grants-statement) statement.
Privileges can be granted to a user account by the [GRANT](../sql-reference/statements/grant.md) query or by assigning [roles](#role-management). To revoke privileges from a user, ClickHouse provides the [REVOKE](../sql-reference/statements/revoke.md) query. To list privileges for a user, use the [SHOW GRANTS](../sql-reference/statements/show.md#show-grants-statement) statement.
Management queries:
@ -66,11 +66,11 @@ Management queries:
### Settings Applying {#access-control-settings-applying}
Settings can be set by different ways: for a user account, in its granted roles and settings profiles. At a user login, if a setting is set in different access entities, the value and constrains of this setting are applied by the following priorities (from higher to lower):
Settings can be configured differently: for a user account, in its granted roles and in settings profiles. At user login, if a setting is configured for different access entities, the value and constraints of this setting are applied as follows (from higher to lower priority):
1. User account setting.
2. The settings of default roles of the user account. If a setting is set in some roles, then order of the setting applying is undefined.
3. The settings in settings profiles assigned to a user or to its default roles. If a setting is set in some profiles, then order of setting applying is undefined.
1. User account settings.
2. The settings of default roles of the user account. If a setting is configured in some roles, then order of the setting application is undefined.
3. The settings from settings profiles assigned to a user or to its default roles. If a setting is configured in some profiles, then order of setting application is undefined.
4. Settings applied to all the server by default or from the [default profile](server-configuration-parameters/settings.md#default-profile).
@ -82,7 +82,7 @@ Role contains:
- [Privileges](../sql-reference/statements/grant.md#grant-privileges)
- Settings and constraints
- List of granted roles
- List of assigned roles
Management queries:
@ -93,11 +93,11 @@ Management queries:
- [SET DEFAULT ROLE](../sql-reference/statements/misc.md#set-default-role-statement)
- [SHOW CREATE ROLE](../sql-reference/statements/show.md#show-create-role-statement)
Privileges to a role can be granted by the [GRANT](../sql-reference/statements/grant.md) query. To revoke privileges from a role ClickHouse provides the [REVOKE](../sql-reference/statements/revoke.md) query.
Privileges can be granted to a role by the [GRANT](../sql-reference/statements/grant.md) query. To revoke privileges from a role ClickHouse provides the [REVOKE](../sql-reference/statements/revoke.md) query.
## Row Policy {#row-policy-management}
Row policy is a filter that defines which or rows is available for a user or for a role. Row policy contains filters for one specific table and list of roles and/or users which should use this row policy.
Row policy is a filter that defines which of the rows are available to a user or a role. Row policy contains filters for one particular table, as well as a list of roles and/or users which should use this row policy.
Management queries:
@ -109,7 +109,7 @@ Management queries:
## Settings Profile {#settings-profiles-management}
Settings profile is a collection of [settings](settings/index.md). Settings profile contains settings and constraints, and list of roles and/or users to which this quota is applied.
Settings profile is a collection of [settings](settings/index.md). Settings profile contains settings and constraints, as well as a list of roles and/or users to which this profile is applied.
Management queries:
@ -123,7 +123,7 @@ Management queries:
Quota limits resource usage. See [Quotas](quotas.md).
Quota contains a set of limits for some durations, and list of roles and/or users which should use this quota.
Quota contains a set of limits for some durations, as well as a list of roles and/or users which should use this quota.
Management queries:
@ -141,7 +141,7 @@ Management queries:
- Enable SQL-driven access control and account management for at least one user account.
By default, SQL-driven access control and account management is turned off for all users. You need to configure at least one user in the `users.xml` configuration file and assign 1 to the [access_management](settings/settings-users.md#access_management-user-setting) setting.
By default, SQL-driven access control and account management is disabled for all users. You need to configure at least one user in the `users.xml` configuration file and set the value of the [access_management](settings/settings-users.md#access_management-user-setting) setting to 1.
[Original article](https://clickhouse.tech/docs/en/operations/access_rights/) <!--hide-->

View File

@ -207,7 +207,7 @@ If `http_port` is specified, the OpenSSL configuration is ignored even if it is
**Example**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}

View File

@ -11,7 +11,7 @@ A settings profile is a collection of settings grouped under the same name.
ClickHouse also supports [SQL-driven workflow](../access-rights.md#access-control) for managing settings profiles. We recommend using it.
A profile can have any name. The profile can have any name. You can specify the same profile for different users. The most important thing you can write in the settings profile is `readonly=1`, which ensures read-only access.
The profile can have any name. You can specify the same profile for different users. The most important thing you can write in the settings profile is `readonly=1`, which ensures read-only access.
Settings profiles can inherit from each other. To use inheritance, indicate one or multiple `profile` settings before the other settings that are listed in the profile. In case when one setting is defined in different profiles, the latest defined is used.

View File

@ -76,7 +76,7 @@ Password can be specified in plaintext or in SHA256 (hex format).
### access_management {#access_management-user-setting}
This setting enables of disables using of SQL-driven [access control and account management](../access-rights.md#access-control) for the user.
This setting enables or disables using of SQL-driven [access control and account management](../access-rights.md#access-control) for the user.
Possible values:

View File

@ -520,23 +520,23 @@ To use `ALTER USER` you must have the [ALTER USER](grant.md#grant-access-managem
### Examples {#alter-user-examples}
Set granted roles as default:
Set assigned roles as default:
``` sql
ALTER USER user DEFAULT ROLE role1, role2
```
If roles aren't previously granted to a user, ClickHouse throws an exception.
If roles aren't previously assigned to a user, ClickHouse throws an exception.
Set all the granted roles to default:
Set all the assigned roles to default:
``` sql
ALTER USER user DEFAULT ROLE ALL
```
If a role will be granted to a user in the future it will become default automatically.
If a role is assigned to a user in the future, it will become default automatically.
Set all the granted roles to default excepting `role1` and `role2`:
Set all the assigned roles to default, excepting `role1` and `role2`:
``` sql
ALTER USER user DEFAULT ROLE ALL EXCEPT role1, role2
@ -591,7 +591,7 @@ ALTER QUOTA [IF EXISTS] name [ON CLUSTER cluster_name]
## ALTER SETTINGS PROFILE {#alter-settings-profile-statement}
Changes quotas.
Changes settings profiles.
### Syntax {#alter-settings-profile-syntax}

View File

@ -327,23 +327,23 @@ There are multiple ways of user identification:
#### User Host
User host is a host from which a connection to ClickHouse server could be established. Host can be specified in the `HOST` section of query by the following ways:
User host is a host from which a connection to ClickHouse server could be established. The host can be specified in the `HOST` query section in the following ways:
- `HOST IP 'ip_address_or_subnetwork'` — User can connect to ClickHouse server only from the specified IP address or a [subnetwork](https://en.wikipedia.org/wiki/Subnetwork). Examples: `HOST IP '192.168.0.0/16'`, `HOST IP '2001:DB8::/32'`. For use in production, only specify `HOST IP` elements (IP addresses and their masks), since using `host` and `host_regexp` might cause extra latency.
- `HOST ANY` — User can connect from any location. This is default option.
- `HOST ANY` — User can connect from any location. This is a default option.
- `HOST LOCAL` — User can connect only locally.
- `HOST NAME 'fqdn'` — User host can be specified as FQDN. For example, `HOST NAME 'mysite.com'`.
- `HOST NAME REGEXP 'regexp'` — You can use [pcre](http://www.pcre.org/) regular expressions when specifying user hosts. For example, `HOST NAME REGEXP '.*\.mysite\.com'`.
- `HOST LIKE 'template'` — Allows you use the [LIKE](../functions/string-search-functions.md#function-like) operator to filter the user hosts. For example, `HOST LIKE '%'` is equivalent to `HOST ANY`, `HOST LIKE '%.mysite.com'` filters all the hosts in the `mysite.com` domain.
- `HOST LIKE 'template'` — Allows you to use the [LIKE](../functions/string-search-functions.md#function-like) operator to filter the user hosts. For example, `HOST LIKE '%'` is equivalent to `HOST ANY`, `HOST LIKE '%.mysite.com'` filters all the hosts in the `mysite.com` domain.
Another way of specifying host is to use `@` syntax with the user name. Examples:
Another way of specifying host is to use `@` syntax following the username. Examples:
- `CREATE USER mira@'127.0.0.1'` — Equivalent to the `HOST IP` syntax.
- `CREATE USER mira@'localhost'` — Equivalent to the `HOST LOCAL` syntax.
- `CREATE USER mira@'192.168.%.%'` — Equivalent to the `HOST LIKE` syntax.
!!! info "Warning"
ClickHouse treats `user_name@'address'` as a user name as a whole. Thus, technically you can create multiple users with `user_name` and different constructions after `@`. We don't recommend to do so.
ClickHouse treats `user_name@'address'` as a username as a whole. Thus, technically you can create multiple users with the same `user_name` and different constructions after `@`. However, we don't recommend to do so.
### Examples {#create-user-examples}
@ -369,7 +369,7 @@ Create the user account `john` and make all his future roles default:
ALTER USER user DEFAULT ROLE ALL
```
When some role will be assigned to `john` in the future it will become default automatically.
When some role is assigned to `john` in the future, it will become default automatically.
Create the user account `john` and make all his future roles default excepting `role1` and `role2`:
@ -391,15 +391,15 @@ CREATE ROLE [IF NOT EXISTS | OR REPLACE] name
### Description {#create-role-description}
Role is a set of [privileges](grant.md#grant-privileges). A user granted with a role gets all the privileges of this role.
Role is a set of [privileges](grant.md#grant-privileges). A user assigned a role gets all the privileges of this role.
A user can be assigned with multiple roles. Users can apply their granted roles in arbitrary combinations by the [SET ROLE](misc.md#set-role-statement) statement. The final scope of privileges is a combined set of all the privileges of all the applied roles. If a user has privileges granted directly to it's user account, they are also combined with the privileges granted by roles.
A user can be assigned multiple roles. Users can apply their assigned roles in arbitrary combinations by the [SET ROLE](misc.md#set-role-statement) statement. The final scope of privileges is a combined set of all the privileges of all the applied roles. If a user has privileges granted directly to it's user account, they are also combined with the privileges granted by roles.
User can have default roles which apply at user login. To set default roles, use the [SET DEFAULT ROLE](misc.md#set-default-role-statement) statement or the [ALTER USER](alter.md#alter-user-statement) statement.
To revoke a role, use the [REVOKE](revoke.md) statement.
To delete role, use the [DROP ROLE](misc.md#drop-role-statement) statement. The deleted role is being automatically revoked from all the users and roles to which it was granted.
To delete role, use the [DROP ROLE](misc.md#drop-role-statement) statement. The deleted role is being automatically revoked from all the users and roles to which it was assigned.
### Examples {#create-role-examples}
@ -410,13 +410,13 @@ GRANT SELECT ON db.* TO accountant;
This sequence of queries creates the role `accountant` that has the privilege of reading data from the `accounting` database.
Granting the role to the user `mira`:
Assigning the role to the user `mira`:
```sql
GRANT accountant TO mira;
```
After the role is granted, the user can use it and perform the allowed queries. For example:
After the role is assigned, the user can apply it and execute the allowed queries. For example:
```sql
SET ROLE accountant;
@ -443,15 +443,15 @@ Using this section you can create permissive or restrictive policies.
Permissive policy grants access to rows. Permissive policies which apply to the same table are combined together using the boolean `OR` operator. Policies are permissive by default.
Restrictive policy restricts access to row. Restrictive policies which apply to the same table are combined together using the boolean `AND` operator.
Restrictive policy restricts access to rows. Restrictive policies which apply to the same table are combined together using the boolean `AND` operator.
Restrictive policies apply to rows that passed the permissive filters. If you set restrictive policies but no permissive policies, the user can't get any row from the table.
#### Section TO {#create-row-policy-to}
In the section `TO` you can give a mixed list of roles and users, for example, `CREATE ROW POLICY ... TO accountant, john@localhost`.
In the section `TO` you can provide a mixed list of roles and users, for example, `CREATE ROW POLICY ... TO accountant, john@localhost`.
Keyword `ALL` means all the ClickHouse users including current user. Keywords `ALL EXCEPT` allow to to exclude some users from the all users list, for example `CREATE ROW POLICY ... TO ALL EXCEPT accountant, john@localhost`
Keyword `ALL` means all the ClickHouse users including current user. Keywords `ALL EXCEPT` allow to exclude some users from the all users list, for example, `CREATE ROW POLICY ... TO ALL EXCEPT accountant, john@localhost`
### Examples
@ -494,7 +494,7 @@ CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_na
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
```
# Example {#create-settings-profile-syntax}
### Example {#create-settings-profile-syntax}
Create the `max_memory_usage_profile` settings profile with value and constraints for the `max_memory_usage` setting. Assign it to `robin`:

View File

@ -7,9 +7,9 @@ toc_title: GRANT
# GRANT
- Grants [privileges](#grant-privileges) to ClickHouse user accounts or roles.
- Assigns roles to user accounts or to another roles.
- Assigns roles to user accounts or to the other roles.
To revoke privileges, use the [REVOKE](revoke.md) statement. Also you can list granted privileges by the [SHOW GRANTS](show.md#show-grants-statement) statement.
To revoke privileges, use the [REVOKE](revoke.md) statement. Also you can list granted privileges with the [SHOW GRANTS](show.md#show-grants-statement) statement.
## Granting Privilege Syntax {#grant-privigele-syntax}
@ -21,10 +21,10 @@ GRANT [ON CLUSTER cluster_name] privilege[(column_name [,...])] [,...] ON {db.ta
- `role` — ClickHouse user role.
- `user` — ClickHouse user account.
The `WITH GRANT OPTION` clause grants `user` or `role` with permission to perform the `GRANT` query. Users can grant privileges of the same scope they have and less.
The `WITH GRANT OPTION` clause grants `user` or `role` with permission to execute the `GRANT` query. Users can grant privileges of the same scope they have and less.
## Granting Role Syntax {#assign-role-syntax}
## Assigning Role Syntax {#assign-role-syntax}
```sql
GRANT [ON CLUSTER cluster_name] role [,...] TO {user | another_role | CURRENT_USER} [,...] [WITH ADMIN OPTION]
@ -33,7 +33,7 @@ GRANT [ON CLUSTER cluster_name] role [,...] TO {user | another_role | CURRENT_US
- `role` — ClickHouse user role.
- `user` — ClickHouse user account.
The `WITH ADMIN OPTION` clause sets [ADMIN OPTION](#admin-option-privilege) privilege for `user` or `role`.
The `WITH ADMIN OPTION` clause grants [ADMIN OPTION](#admin-option-privilege) privilege to `user` or `role`.
## Usage {#grant-usage}
@ -45,28 +45,28 @@ For example, administrator has granted privileges to the `john` account by the q
GRANT SELECT(x,y) ON db.table TO john WITH GRANT OPTION
```
It means that `john` has the permission to perform:
It means that `john` has the permission to execute:
- `SELECT x,y FROM db.table`.
- `SELECT x FROM db.table`.
- `SELECT y FROM db.table`.
`john` can't perform `SELECT z FROM db.table`. The `SELECT * FROM db.table` also is not available. Processing this query, ClickHouse doesn't return any data, even `x` and `y`. The only exception is if a table contains only `x` and `y` columns, in this case ClickHouse returns all the data.
`john` can't execute `SELECT z FROM db.table`. The `SELECT * FROM db.table` also is not available. Processing this query, ClickHouse doesn't return any data, even `x` and `y`. The only exception is if a table contains only `x` and `y` columns. In this case ClickHouse returns all the data.
Also `john` has the `GRANT OPTION` privilege, so it can grant other users with privileges of the same or the smaller scope.
Also `john` has the `GRANT OPTION` privilege, so it can grant other users with privileges of the same or smaller scope.
Specifying privileges you can use asterisk (`*`) instead of a table or a database name. For example, the `GRANT SELECT ON db.* TO john` query allows `john` to perform the `SELECT` query over all the tables in `db` database. Also, you can omit database name. In this case privileges are granted for current database, for example: `GRANT SELECT ON * TO john` grants the privilege on all the tables in the current database, `GRANT SELECT ON mytable TO john` grants the privilege on the `mytable` table in the current database.
Specifying privileges you can use asterisk (`*`) instead of a table or a database name. For example, the `GRANT SELECT ON db.* TO john` query allows `john` to execute the `SELECT` query over all the tables in `db` database. Also, you can omit database name. In this case privileges are granted for current database. For example, `GRANT SELECT ON * TO john` grants the privilege on all the tables in the current database, `GRANT SELECT ON mytable TO john` grants the privilege on the `mytable` table in the current database.
Access to the `system` database is always allowed (since this database is used for processing queries).
You can grant multiple privileges to multiple accounts in one query. The query `GRANT SELECT, INSERT ON *.* TO john, robin` allows accounts `john` and `robin` to perform the `INSERT` and `SELECT` queries over all the tables in all the databases on the server.
You can grant multiple privileges to multiple accounts in one query. The query `GRANT SELECT, INSERT ON *.* TO john, robin` allows accounts `john` and `robin` to execute the `INSERT` and `SELECT` queries over all the tables in all the databases on the server.
## Privileges {#grant-privileges}
Privilege is a permission to perform specific kind of queries.
Privilege is a permission to execute specific kind of queries.
Privileges have an hierarchical structure. A set of permitted queries depends on the privilege scope.
Privileges have a hierarchical structure. A set of permitted queries depends on the privilege scope.
Hierarchy of privileges:
@ -212,20 +212,20 @@ The special privilege [ALL](#grant-all) grants all the privileges to a user acco
By default, a user account or a role has no privileges.
If a user or role have no privileges it displayed as [NONE](#grant-none) privilege.
If a user or a role has no privileges, it is displayed as [NONE](#grant-none) privilege.
Some queries by their implementation require a set of privileges. For example, to perform the [RENAME](misc.md#misc_operations-rename) query you need the following privileges: `SELECT`, `CREATE TABLE`, `INSERT` and `DROP TABLE`.
Some queries by their implementation require a set of privileges. For example, to execute the [RENAME](misc.md#misc_operations-rename) query you need the following privileges: `SELECT`, `CREATE TABLE`, `INSERT` and `DROP TABLE`.
### SELECT {#grant-select}
Allows to perform [SELECT](select/index.md) queries.
Allows executing [SELECT](select/index.md) queries.
Privilege level: `COLUMN`.
**Description**
User granted with this privilege can perform `SELECT` queries over a specified list of columns in the specified table and database. If user includes other columns then specified a query returns no data.
User granted with this privilege can execute `SELECT` queries over a specified list of columns in the specified table and database. If user includes other columns then specified a query returns no data.
Consider the following privilege:
@ -233,17 +233,17 @@ Consider the following privilege:
GRANT SELECT(x,y) ON db.table TO john
```
This privilege allows `john` to perform any `SELECT` query that involves data from the `x` and/or `y` columns in `db.table`. For example, `SELECT x FROM db.table`. `john` can't perform `SELECT z FROM db.table`. The `SELECT * FROM db.table` also is not available. Processing this query, ClickHouse doesn't return any data, even `x` and `y`. The only exception is if a table contains only `x` and `y` columns, in this case ClickHouse returns all the data.
This privilege allows `john` to execute any `SELECT` query that involves data from the `x` and/or `y` columns in `db.table`, for example, `SELECT x FROM db.table`. `john` can't execute `SELECT z FROM db.table`. The `SELECT * FROM db.table` also is not available. Processing this query, ClickHouse doesn't return any data, even `x` and `y`. The only exception is if a table contains only `x` and `y` columns, in this case ClickHouse returns all the data.
### INSERT {#grant-insert}
Allows performing [INSERT](insert-into.md) queries.
Allows executing [INSERT](insert-into.md) queries.
Privilege level: `COLUMN`.
**Description**
User granted with this privilege can perform `INSERT` queries over a specified list of columns in the specified table and database. If user includes other columns then specified a query doesn't insert any data.
User granted with this privilege can execute `INSERT` queries over a specified list of columns in the specified table and database. If user includes other columns then specified a query doesn't insert any data.
**Example**
@ -255,7 +255,7 @@ The granted privilege allows `john` to insert data to the `x` and/or `y` columns
### ALTER {#grant-alter}
Allows performing [ALTER](alter.md) queries corresponding to the following hierarchy of privileges:
Allows executing [ALTER](alter.md) queries according to the following hierarchy of privileges:
- `ALTER`. Level: `COLUMN`.
- `ALTER TABLE`. Level: `GROUP`
@ -294,14 +294,14 @@ Examples of how this hierarchy is treated:
**Notes**
- The `MODIFY SETTING` privilege allows to modify table engine settings. In doesn't affect settings or server configuration parameters.
- The `MODIFY SETTING` privilege allows modifying table engine settings. It doesn't affect settings or server configuration parameters.
- The `ATTACH` operation needs the [CREATE](#grant-create) privilege.
- The `DETACH` operation needs the [DROP](#grant-drop) privilege.
- To stop mutation by the [KILL MUTATION](misc.md#kill-mutation) query, you need to have a privilege to start this mutation. For example, if you want to stop the `ALTER UPDATE` query, you need the `ALTER UPDATE`, `ALTER TABLE`, or `ALTER` privilege.
### CREATE {#grant-create}
Allows to perform [CREATE](create.md) and [ATTACH](misc.md#attach) DDL-queries corresponding to the following hierarchy of privileges:
Allows executing [CREATE](create.md) and [ATTACH](misc.md#attach) DDL-queries according to the following hierarchy of privileges:
- `CREATE`. Level: `GROUP`
- `CREATE DATABASE`. Level: `DATABASE`
@ -316,7 +316,7 @@ Allows to perform [CREATE](create.md) and [ATTACH](misc.md#attach) DDL-queries c
### DROP {#grant-drop}
Allows to perform [DROP](misc.md#drop) and [DETACH](misc.md#detach) queries corresponding to the following hierarchy of privileges:
Allows executing [DROP](misc.md#drop) and [DETACH](misc.md#detach) queries according to the following hierarchy of privileges:
- `DROP`. Level:
- `DROP DATABASE`. Level: `DATABASE`
@ -327,19 +327,19 @@ Allows to perform [DROP](misc.md#drop) and [DETACH](misc.md#detach) queries corr
### TRUNCATE {#grant-truncate}
Allows to perform [TRUNCATE](misc.md#truncate-statement) queries.
Allows executing [TRUNCATE](misc.md#truncate-statement) queries.
Privilege level: `TABLE`.
### OPTIMIZE {#grant-optimize}
Allows to perform the [OPTIMIZE TABLE](misc.md#misc_operations-optimize) queries.
Allows executing [OPTIMIZE TABLE](misc.md#misc_operations-optimize) queries.
Privilege level: `TABLE`.
### SHOW {#grant-show}
Allows to perform `SHOW`, `DESCRIBE`, `USE`, and `EXISTS` queries, corresponding to the following hierarchy of privileges:
Allows executing `SHOW`, `DESCRIBE`, `USE`, and `EXISTS` queries according to the following hierarchy of privileges:
- `SHOW`. Level: `GROUP`
- `SHOW DATABASES`. Level: `DATABASE`. Allows to execute `SHOW DATABASES`, `SHOW CREATE DATABASE`, `USE <database>` queries.
@ -349,12 +349,12 @@ Allows to perform `SHOW`, `DESCRIBE`, `USE`, and `EXISTS` queries, corresponding
**Notes**
A user has the `SHOW` privilege if it has any another privilege concerning the specified table, dictionary or database.
A user has the `SHOW` privilege if it has any other privilege concerning the specified table, dictionary or database.
### KILL QUERY {#grant-kill-query}
Allows to perform the [KILL](misc.md#kill-query-statement) queries corresponding to the following hierarchy of privileges:
Allows executing [KILL](misc.md#kill-query-statement) queries according to the following hierarchy of privileges:
Privilege level: `GLOBAL`.
@ -365,7 +365,7 @@ Privilege level: `GLOBAL`.
### ACCESS MANAGEMENT {#grant-access-management}
Allows a user to perform queries that manage users, roles and row policies.
Allows a user to execute queries that manage users, roles and row policies.
- `ACCESS MANAGEMENT`. Level: `GROUP`
- `CREATE USER`. Level: `GLOBAL`
@ -391,11 +391,11 @@ Allows a user to perform queries that manage users, roles and row policies.
- `SHOW_QUOTAS`. Level: `GLOBAL`. Aliases: `SHOW CREATE QUOTA`
- `SHOW_SETTINGS_PROFILES`. Level: `GLOBAL`. Aliases: `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`, `SHOW CREATE PROFILE`
The `ROLE ADMIN` privilege allows a user to grant and revoke any roles including those which are not granted to the user with the admin option.
The `ROLE ADMIN` privilege allows a user to assign and revoke any roles including those which are not assigned to the user with the admin option.
### SYSTEM {#grant-system}
Allows a user to perform the [SYSTEM](system.md) queries corresponding to the following hierarchy of privileges.
Allows a user to execute [SYSTEM](system.md) queries according to the following hierarchy of privileges.
- `SYSTEM`. Level: `GROUP`
- `SYSTEM SHUTDOWN`. Level: `GLOBAL`. Aliases: `SYSTEM KILL`, `SHUTDOWN`
@ -461,7 +461,7 @@ Examples:
Allows a user to execute [dictGet](../functions/ext-dict-functions.md#dictget), [dictHas](../functions/ext-dict-functions.md#dicthas), [dictGetHierarchy](../functions/ext-dict-functions.md#dictgethierarchy), [dictIsIn](../functions/ext-dict-functions.md#dictisin) functions.
Level of privilege: `DICTIONARY`.
Privilege level: `DICTIONARY`.
**Examples**
@ -480,6 +480,6 @@ Doesn't grant any privileges.
### ADMIN OPTION {#admin-option-privilege}
The `ADMIN OPTION` privilege allows a user granting their role to another user.
The `ADMIN OPTION` privilege allows a user to grant their role to another user.
[Original article](https://clickhouse.tech/docs/en/query_language/grant/) <!--hide-->

View File

@ -126,7 +126,7 @@ DROP USER [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
Deletes a role.
Deleted role is revoked from all the entities where it was granted.
Deleted role is revoked from all the entities where it was assigned.
### Syntax {#drop-role-syntax}
@ -162,9 +162,9 @@ DROP QUOTA [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
## DROP SETTINGS PROFILE {#drop-settings-profile-statement}
Deletes a quota.
Deletes a settings profile.
Deleted quota is revoked from all the entities where it was assigned.
Deleted settings profile is revoked from all the entities where it was assigned.
### Syntax {#drop-settings-profile-syntax}
@ -360,4 +360,4 @@ Lets you set the current database for the session.
The current database is used for searching for tables if the database is not explicitly defined in the query with a dot before the table name.
This query cant be made when using the HTTP protocol, since there is no concept of a session.
[Original article](https://clickhouse.tech/docs/en/query_language/misc/) <!--hide-->
[Original article](https://clickhouse.tech/docs/en/query_language/misc/)

View File

@ -23,7 +23,7 @@ REVOKE [ON CLUSTER cluster_name] [ADMIN OPTION FOR] role [,...] FROM {user | rol
## Description {#revoke-description}
To revoke some privilege you can use a privilege of wider scope then you plan to revoke. For example, if a user has the `SELECT (x,y)` privilege, administrator can perform `REVOKE SELECT(x,y) ...`, or `REVOKE SELECT * ...`, or even `REVOKE ALL PRIVILEGES ...` query to revoke this privilege.
To revoke some privilege you can use a privilege of a wider scope than you plan to revoke. For example, if a user has the `SELECT (x,y)` privilege, administrator can execute `REVOKE SELECT(x,y) ...`, or `REVOKE SELECT * ...`, or even `REVOKE ALL PRIVILEGES ...` query to revoke this privilege.
### Partial Revokes {#partial-revokes-dscr}
@ -32,14 +32,14 @@ You can revoke a part of a privilege. For example, if a user has the `SELECT *.*
## Examples {#revoke-example}
Grant the `john` user account with a privilege to select from all the databases excepting the `accounts` one:
Grant the `john` user account with a privilege to select from all the databases, excepting the `accounts` one:
``` sql
GRANT SELECT ON *.* TO john;
REVOKE SELECT ON accounts.* FROM john;
```
Grant the `mira` user account with a privilege to select from all the columns of the `accounts.staff` table excepting the `wage` one.
Grant the `mira` user account with a privilege to select from all the columns of the `accounts.staff` table, excepting the `wage` one.
``` sql
GRANT SELECT ON accounts.staff TO mira;

View File

@ -1,3 +1,7 @@
---
toc_title: ARRAY JOIN
---
# ARRAY JOIN Clause {#select-array-join-clause}
It is a common operation for tables that contain an array column to produce a new table that has a column with each individual array element of that initial column, while values of other columns are duplicated. This is the basic case of what `ARRAY JOIN` clause does.

View File

@ -1,3 +1,7 @@
---
toc_title: DISTINCT
---
# DISTINCT Clause {#select-distinct}
If `SELECT DISTINCT` is specified, only unique rows will remain in a query result. Thus only a single row will remain out of all the sets of fully matching rows in the result.

View File

@ -1,3 +1,7 @@
---
toc_title: FORMAT
---
# FORMAT Clause {#format-clause}
ClickHouse supports a wide range of [serialization formats](../../../interfaces/formats.md) that can be used on query results among other things. There are multiple ways to choose a format for `SELECT` output, one of them is to specify `FORMAT format` at the end of query to get resulting data in any specific format.

View File

@ -1,3 +1,7 @@
---
toc_title: FROM
---
# FROM Clause {#select-from}
The `FROM` clause specifies the source to read data from:

View File

@ -1,3 +1,7 @@
---
toc_title: GROUP BY
---
# GROUP BY Clause {#select-group-by-clause}
`GROUP BY` clause switches the `SELECT` query into an aggregation mode, which works as follows:

View File

@ -1,3 +1,7 @@
---
toc_title: HAVING
---
# HAVING Clause {#having-clause}
Allows filtering the aggregation results produced by [GROUP BY](group-by.md). It is similar to the [WHERE](where.md) clause, but the difference is that `WHERE` is performed before aggregation, while `HAVING` is performed after it.

View File

@ -1,6 +1,7 @@
---
toc_priority: 33
toc_title: SELECT
toc_folder_title: SELECT
toc_title: Queries Syntax
---
# SELECT Queries Syntax {#select-queries-syntax}

View File

@ -1,3 +1,7 @@
---
toc_title: INTO OUTFILE
---
# INTO OUTFILE Clause {#into-outfile-clause}
Add the `INTO OUTFILE filename` clause (where filename is a string literal) to `SELECT query` to redirect its output to the specified file on the client-side.

View File

@ -1,3 +1,7 @@
---
toc_title: JOIN
---
# JOIN Clause {#select-join}
Join produces a new table by combining columns from one or multiple tables by using values common to each. It is a common operation in databases with SQL support, which corresponds to [relational algebra](https://en.wikipedia.org/wiki/Relational_algebra#Joins_and_join-like_operators) join. The special case of one table join is often referred to as "self-join".

View File

@ -1,3 +1,7 @@
---
toc_title: LIMIT BY
---
# LIMIT BY Clause {#limit-by-clause}
A query with the `LIMIT n BY expressions` clause selects the first `n` rows for each distinct value of `expressions`. The key for `LIMIT BY` can contain any number of [expressions](../../syntax.md#syntax-expressions).

View File

@ -1,3 +1,7 @@
---
toc_title: LIMIT
---
# LIMIT Clause {#limit-clause}
`LIMIT m` allows to select the first `m` rows from the result.

View File

@ -1,3 +1,7 @@
---
toc_title: ORDER BY
---
# ORDER BY Clause {#select-order-by}
The `ORDER BY` clause contains a list of expressions, which can each be attributed with `DESC` (descending) or `ASC` (ascending) modifier which determine the sorting direction. If the direction is not specified, `ASC` is assumed, so it's usually omitted. The sorting direction applies to a single expression, not to the entire list. Example: `ORDER BY Visits DESC, SearchPhrase`

View File

@ -1,3 +1,7 @@
---
toc_title: PREWHERE
---
# PREWHERE Clause {#prewhere-clause}
Prewhere is an optimization to apply filtering more efficiently. It is enabled by default even if `PREWHERE` clause is not specified explicitly. It works by automatically moving part of [WHERE](where.md) condition to prewhere stage. The role of `PREWHERE` clause is only to control this optimization if you think that you know how to do it better than it happens by default.

View File

@ -1,3 +1,7 @@
---
toc_title: SAMPLE
---
# SAMPLE Clause {#select-sample-clause}
The `SAMPLE` clause allows for approximated `SELECT` query processing.

View File

@ -1,3 +1,7 @@
---
toc_title: UNION ALL
---
# UNION ALL Clause {#union-all-clause}
You can use `UNION ALL` to combine any number of `SELECT` queries by extending their results. Example:

View File

@ -1,3 +1,7 @@
---
toc_title: WHERE
---
# WHERE Clause {#select-where}
`WHERE` clause allows to filter the data that is coming from [FROM](from.md) clause of `SELECT`.

View File

@ -1,3 +1,7 @@
---
toc_title: WITH
---
# WITH Clause {#with-clause}
This section provides support for Common Table Expressions ([CTE](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL)), so the results of `WITH` clause can be used in the rest of `SELECT` query.

View File

@ -131,7 +131,7 @@ SHOW CREATE USER [name | CURRENT_USER]
## SHOW CREATE ROLE {#show-create-role-statement}
Shows parameters that were used at a [role creation](create.md#create-role-statement)
Shows parameters that were used at a [role creation](create.md#create-role-statement).
### Syntax {#show-create-role-syntax}
@ -143,7 +143,7 @@ SHOW CREATE ROLE name
## SHOW CREATE ROW POLICY {#show-create-row-policy-statement}
Shows parameters that were used at a [row policy creation](create.md#create-row-policy-statement)
Shows parameters that were used at a [row policy creation](create.md#create-row-policy-statement).
### Syntax {#show-create-row-policy-syntax}
@ -154,7 +154,7 @@ SHOW CREATE [ROW] POLICY name ON [database.]table
## SHOW CREATE QUOTA {#show-create-quota-statement}
Shows parameters that were used at a [quota creation](create.md#create-quota-statement)
Shows parameters that were used at a [quota creation](create.md#create-quota-statement).
### Syntax {#show-create-row-policy-syntax}
@ -165,7 +165,7 @@ SHOW CREATE QUOTA [name | CURRENT]
## SHOW CREATE SETTINGS PROFILE {#show-create-settings-profile-statement}
Shows parameters that were used at a [settings profile creation](create.md#create-settings-profile-statement)
Shows parameters that were used at a [settings profile creation](create.md#create-settings-profile-statement).
### Syntax {#show-create-row-policy-syntax}

View File

@ -209,7 +209,7 @@ Si `http_port` se especifica, la configuración de OpenSSL se ignora incluso si
**Ejemplo**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}

View File

@ -210,7 +210,7 @@ toc_title: "\u062A\u0646\u0638\u06CC\u0645\u0627\u062A \u06A9\u0627\u0631\u06AF\
**مثال**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## نقلقولهای جدید از این نویسنده {#server_configuration_parameters-http_server_default_response}

View File

@ -209,7 +209,7 @@ Si `http_port` est spécifié, la configuration OpenSSL est ignorée même si el
**Exemple**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}

View File

@ -209,7 +209,7 @@ HTTP経由でサーバーに接続するためのポート。
**例**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}

View File

@ -1,101 +1,143 @@
# Права доступа {#prava-dostupa}
# Управление доступом {#access-control}
Пользователи и права доступа настраиваются в конфиге пользователей. Обычно это `users.xml`.
ClickHouse поддерживает управление доступом на основе подхода [RBAC](https://ru.wikipedia.org/wiki/Управлениеоступом_на_основе_ролей).
Пользователи прописаны в секции `users`. Рассмотрим фрагмент файла `users.xml`:
Объекты системы доступа в ClickHouse:
``` xml
<!-- Пользователи и ACL. -->
<users>
<!-- Если имя пользователя не указано, используется пользователь default. -->
<default>
<!-- Password could be specified in plaintext or in SHA256 (in hex format).
- [Аккаунт пользователя](#user-account-management)
- [Роль](#role-management)
- [Политика доступа к строкам](#row-policy-management)
- [Профиль настроек](#settings-profiles-management)
- [Квота](#quotas-management)
If you want to specify password in plaintext (not recommended), place it in 'password' element.
Example: <password>qwerty</password>.
Password could be empty.
Вы можете настроить объекты системы доступа, используя:
If you want to specify SHA256, place it in 'password_sha256_hex' element.
Example: <password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>
- SQL-ориентированный воркфлоу.
How to generate decent password:
Execute: PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | sha256sum | tr -d '-'
In first line will be password and in second - corresponding SHA256.
-->
<password></password>
Функциональность необходимо [включить](#enabling-access-control).
<!-- Список сетей, из которых разрешён доступ.
Каждый элемент списка имеет одну из следующих форм:
<ip> IP-адрес или маска подсети. Например, 198.51.100.0/24 или 2001:DB8::/32.
<host> Имя хоста. Например: example01. Для проверки делается DNS-запрос, и все полученные адреса сравниваются с адресом клиента.
<host_regexp> Регулярное выражение для имён хостов. Например, ^example\d\d-\d\d-\d\.host\.ru$
Для проверки, для адреса клиента делается DNS PTR-запрос и к результату применяется регулярное выражение.
Потом для результата PTR-запроса делается снова DNS-запрос, и все полученные адреса сравниваются с адресом клиента.
Настоятельно рекомендуется, чтобы регулярное выражение заканчивалось на \.host\.ru$.
- [Конфигурационные файлы](configuration-files.md) сервера: `users.xml` и `config.xml`.
Если вы устанавливаете ClickHouse самостоятельно, укажите здесь:
<networks>
<ip>::/0</ip>
</networks>
-->
<networks incl="networks" />
Рекомендуется использовать SQL-воркфлоу. Оба метода конфигурации работают одновременно, поэтому, если для управления доступом вы используете конфигурационные файлы, вы можете плавно перейти на SQL-воркфлоу.
<!-- Профиль настроек, использующийся для пользователя. -->
<profile>default</profile>
!!! note "Внимание"
Нельзя одновременно использовать оба метода для управления одним и тем же объектом системы доступа.
<!-- Квота, использующаяся для пользователя. -->
<quota>default</quota>
</default>
<!-- Для запросов из пользовательского интерфейса Метрики через API для данных по отдельным счётчикам. -->
<web>
<password></password>
<networks incl="networks" />
<profile>web</profile>
<quota>default</quota>
<allow_databases>
<database>test</database>
</allow_databases>
</web>
```
## Использование {#access-control-usage}
Здесь видно объявление двух пользователей - `default` и `web`. Пользователя `web` мы добавили самостоятельно.
По умолчанию сервер ClickHouse предоставляет аккаунт пользователя `default`, для которого выключена функция SQL-ориентированного управления доступом, но у него есть все права и разрешения. Аккаунт `default` используется во всех случаях, когда имя пользователя не определено. Например, при входе с клиента или в распределенных запросах. При распределенной обработке запроса `default` используется, если в конфигурации сервера или кластера не указаны свойства [user и password](../engines/table-engines/special/distributed.md).
Пользователь `default` выбирается в случаях, когда имя пользователя не передаётся. Также пользователь `default` может использоваться при распределённой обработке запроса - если в конфигурации кластера для сервера не указаны `user` и `password`. (см. раздел о движке [Distributed](../engines/table-engines/special/distributed.md)).
Если вы начали пользоваться ClickHouse недавно, попробуйте следующий сценарий:
Пользователь, который используется для обмена информацией между серверами, объединенными в кластер, не должен иметь существенных ограничений или квот - иначе распределённые запросы сломаются.
1. [Включите](#enabling-access-control) SQL-ориентированное управление доступом для пользователя `default`.
2. Войдите под пользователем `default` и создайте всех необходимых пользователей. Не забудьте создать аккаунт администратора (`GRANT ALL ON *.* WITH GRANT OPTION TO admin_user_account`).
3. [Ограничьте разрешения](settings/permissions-for-queries.md#permissions_for_queries) для пользователя `default` и отключите для него SQL-ориентированное управление доступом.
Пароль указывается либо в открытом виде (не рекомендуется), либо в виде SHA-256. Хэш не содержит соль. В связи с этим, не следует рассматривать такие пароли, как защиту от потенциального злоумышленника. Скорее, они нужны для защиты от сотрудников.
### Особенности реализации {#access-control-properties}
Указывается список сетей, из которых разрешён доступ. В этом примере, список сетей для обеих пользователей, загружается из отдельного файла (`/etc/metrika.xml`), содержащего подстановку `networks`. Вот его фрагмент:
- Вы можете выдавать разрешения на базы данных или таблицы, даже если они не существуют.
- При удалении таблицы все связанные с ней привилегии не отзываются. Если вы затем создадите новую таблицу с таким же именем, все привилегии останутся действительными. Чтобы отозвать привилегии, связанные с удаленной таблицей, необходимо выполнить, например, запрос `REVOKE ALL PRIVILEGES ON db.table FROM ALL`.
- У привилегий нет настроек времени жизни.
``` xml
<yandex>
...
<networks>
<ip>::/64</ip>
<ip>203.0.113.0/24</ip>
<ip>2001:DB8::/32</ip>
...
</networks>
</yandex>
```
## Аккаунт пользователя {#user-account-management}
Можно было бы указать этот список сетей непосредственно в `users.xml`, или в файле в директории `users.d` (подробнее смотрите раздел «[Конфигурационные файлы](configuration-files.md#configuration_files)»).
Аккаунт пользователя — это объект системы доступа, позволяющий авторизовать кого-либо в ClickHouse. Аккаунт содержит:
В конфиге приведён комментарий, указывающий, как можно открыть доступ отовсюду.
- Идентификационную информацию.
- [Привилегии](../sql-reference/statements/grant.md#grant-privileges), определяющие область действия запросов, которые могут быть выполнены пользователем.
- Хосты, которые могут подключаться к серверу ClickHouse.
- Назначенные роли и роли по умолчанию.
- Настройки и их ограничения, которые применяются по умолчанию при входе пользователя.
- Присвоенные профили настроек.
Для продакшен использования, указывайте только элементы вида `ip` (IP-адреса и их маски), так как использование `host` и `host_regexp` может вызывать лишние задержки.
Привилегии присваиваются аккаунту пользователя с помощью запроса [GRANT](../sql-reference/statements/grant.md) или через назначение [ролей](#role-management). Отозвать привилегию можно с помощью запроса [REVOKE](../sql-reference/statements/revoke.md). Чтобы вывести список присвоенных привилегий, используется выражение [SHOW GRANTS](../sql-reference/statements/show.md#show-grants-statement).
Далее указывается используемый профиль настроек пользователя (смотрите раздел «[Профили настроек](settings/settings-profiles.md)»). Вы можете указать профиль по умолчанию - `default`. Профиль может называться как угодно; один и тот же профиль может быть указан для разных пользователей. Наиболее важная вещь, которую вы можете прописать в профиле настроек `readonly=1`, что обеспечивает доступ только на чтение.
Затем указывается используемая квота (смотрите раздел «[Квоты](quotas.md#quotas)»). Вы можете указать квоту по умолчанию — `default`. Она настроена в конфиге по умолчанию так, что только считает использование ресурсов, но никак их не ограничивает. Квота может называться как угодно. Одна и та же квота может быть указана для разных пользователей, в этом случае подсчёт использования ресурсов делается для каждого пользователя по отдельности.
Запросы управления:
Также, в необязательном разделе `<allow_databases>` можно указать перечень баз, к которым у пользователя будет доступ. По умолчанию пользователю доступны все базы. Можно указать базу данных `default`, в этом случае пользователь получит доступ к базе данных по умолчанию.
- [CREATE USER](../sql-reference/statements/create.md#create-user-statement)
- [ALTER USER](../sql-reference/statements/alter.md#alter-user-statement)
- [DROP USER](../sql-reference/statements/misc.md#drop-user-statement)
- [SHOW CREATE USER](../sql-reference/statements/show.md#show-create-user-statement)
Доступ к БД `system` всегда считается разрешённым (так как эта БД используется для выполнения запросов).
### Применение настроек {#access-control-settings-applying}
Пользователь может получить список всех БД и таблиц в них с помощью запросов `SHOW` или системных таблиц, даже если у него нет доступа к отдельным БД.
Настройки могут быть заданы разными способами: для аккаунта пользователя, для назначенных ему ролей или в профилях настроек. При входе пользователя, если настройка задана для разных объектов системы доступа, значение настройки и ее ограничения применяются в следующем порядке (от высшего приоритета к низшему):
1. Настройки аккаунта.
2. Настройки ролей по умолчанию для аккаунта. Если настройка задана для нескольких ролей, порядок применения не определен.
3. Настройки из профилей настроек, присвоенных пользователю или его ролям по умолчанию. Если настройка задана в нескольких профилях, порядок применения не определен.
4. Настройки, которые по умолчанию применяются ко всему серверу, или настройки из [профиля по умолчанию](server-configuration-parameters/settings.md#default-profile).
## Роль {#role-management}
Роль — это контейнер объектов системы доступа, которые можно присвоить аккаунту пользователя.
Роль содержит:
- [Привилегии](../sql-reference/statements/grant.md#grant-privileges)
- Настройки и ограничения
- Список назначенных ролей
Запросы управления:
- [CREATE ROLE](../sql-reference/statements/create.md#create-role-statement)
- [ALTER ROLE](../sql-reference/statements/alter.md#alter-role-statement)
- [DROP ROLE](../sql-reference/statements/misc.md#drop-role-statement)
- [SET ROLE](../sql-reference/statements/misc.md#set-role-statement)
- [SET DEFAULT ROLE](../sql-reference/statements/misc.md#set-default-role-statement)
- [SHOW CREATE ROLE](../sql-reference/statements/show.md#show-create-role-statement)
Привилегии можно присвоить роли с помощью запроса [GRANT](../sql-reference/statements/grant.md). Для отзыва привилегий у роли ClickHouse предоставляет запрос [REVOKE](../sql-reference/statements/revoke.md).
## Политика доступа к строкам {#row-policy-management}
Политика доступа к строкам — это фильтр, определяющий, какие строки доступны пользователю или роли. Политика содержит фильтры для конкретной таблицы, а также список ролей и/или пользователей, которые должны использовать данную политику.
Запросы управления:
- [CREATE ROW POLICY](../sql-reference/statements/create.md#create-row-policy-statement)
- [ALTER ROW POLICY](../sql-reference/statements/alter.md#alter-row-policy-statement)
- [DROP ROW POLICY](../sql-reference/statements/misc.md#drop-row-policy-statement)
- [SHOW CREATE ROW POLICY](../sql-reference/statements/show.md#show-create-row-policy-statement)
## Профиль настроек {#settings-profiles-management}
Профиль настроек — это набор [настроек](settings/index.md). Профиль настроек содержит настройки и ограничения, а также список ролей и/или пользователей, по отношению к которым применяется данный профиль.
Запросы управления:
- [CREATE SETTINGS PROFILE](../sql-reference/statements/create.md#create-settings-profile-statement)
- [ALTER SETTINGS PROFILE](../sql-reference/statements/alter.md#alter-settings-profile-statement)
- [DROP SETTINGS PROFILE](../sql-reference/statements/misc.md#drop-settings-profile-statement)
- [SHOW CREATE SETTINGS PROFILE](../sql-reference/statements/show.md#show-create-settings-profile-statement)
## Квота {#quotas-management}
Квота ограничивает использование ресурсов. См. [Квоты](quotas.md).
Квота содержит набор ограничений определенной длительности, а также список ролей и/или пользователей, на которых распространяется данная квота.
Запросы управления:
- [CREATE QUOTA](../sql-reference/statements/create.md#create-quota-statement)
- [ALTER QUOTA](../sql-reference/statements/alter.md#alter-quota-statement)
- [DROP QUOTA](../sql-reference/statements/misc.md#drop-quota-statement)
- [SHOW CREATE QUOTA](../sql-reference/statements/show.md#show-create-quota-statement)
## Включение SQL-ориентированного управления доступом {#enabling-access-control}
- Настройте каталог для хранения конфигураций.
ClickHouse хранит конфигурации объектов системы доступа в каталоге, установленном в конфигурационном параметре сервера [access_control_path](server-configuration-parameters/settings.md#access_control_path).
- Включите SQL-ориентированное управление доступом как минимум для одного аккаунта.
По умолчанию управление доступом на основе SQL выключено для всех пользователей. Вам необходимо настроить хотя бы одного пользователя в файле конфигурации `users.xml` и присвоить значение 1 параметру [access_management](settings/settings-users.md#access_management-user-setting).
Доступ к БД не связан с настройкой [readonly](settings/permissions-for-queries.md#settings_readonly). Невозможно дать полный доступ к одной БД и `readonly` к другой.
[Оригинальная статья](https://clickhouse.tech/docs/ru/operations/access_rights/) <!--hide-->

View File

@ -195,7 +195,7 @@ ClickHouse проверит условия `min_part_size` и `min_part_size_rat
**Пример**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}
@ -832,4 +832,14 @@ ClickHouse использует ZooKeeper для хранения метадан
**Значение по умолчанию**: 15.
## access_control_path {#access_control_path}
Путь к каталогу, где сервер ClickHouse хранит конфигурации пользователей и ролей, созданные командами SQL.
Значение по умолчанию: `/var/lib/clickhouse/access/`.
**Смотрите также**
- [Управление доступом](../access-rights.md#access-control)
[Оригинальная статья](https://clickhouse.tech/docs/ru/operations/server_configuration_parameters/settings/) <!--hide-->

View File

@ -1,6 +1,15 @@
# Профили настроек {#profili-nastroek}
# Профили настроек {#settings-profiles}
Профиль настроек — это набор настроек, сгруппированных под одним именем.
!!! note "Информация"
Для управления профилями настроек рекомендуется использовать [SQL-ориентированный воркфлоу](../access-rights.md#access-control), который также поддерживается в ClickHouse.
Название профиля может быть любым. Вы можете указать один и тот же профиль для разных пользователей. Самое важное, что можно прописать в профиле — `readonly=1`, это обеспечит доступ только на чтение.
Профили настроек поддерживают наследование. Это реализуется указанием одной или нескольких настроек `profile` перед остальными настройками, перечисленными в профиле. Если одна настройка указана в нескольких профилях, используется последнее из значений.
Профили настроек - это множество настроек, сгруппированных под одним именем. Для каждого пользователя ClickHouse указывается некоторый профиль.
Все настройки профиля можно применить, установив настройку `profile`.
Пример:
@ -57,8 +66,10 @@ SET profile = 'web'
</profiles>
```
В примере задано два профиля: `default` и `web`. Профиль `default` имеет специальное значение - он всегда обязан присутствовать и применяется при запуске сервера. То есть, профиль `default` содержит настройки по умолчанию. Профиль `web` - обычный профиль, который может быть установлен с помощью запроса `SET` или с помощью параметра URL при запросе по HTTP.
В примере задано два профиля: `default` и `web`.
Профили настроек могут наследоваться от друг-друга - это реализуется указанием одной или нескольких настроек `profile` перед остальными настройками, перечисленными в профиле. Если одна настройка указана в нескольких профилях, используется последнее из значений.
Профиль `default` имеет специальное значение — он обязателен и применяется при запуске сервера. Профиль `default` содержит настройки по умолчанию.
Профиль `web` — обычный профиль, который может быть установлен с помощью запроса `SET` или параметра URL при запросе по HTTP.
[Оригинальная статья](https://clickhouse.tech/docs/ru/operations/settings/settings_profiles/) <!--hide-->

View File

@ -2,6 +2,9 @@
Раздел `users` конфигурационного файла `user.xml` содержит настройки для пользователей.
!!! note "Информация"
Для управления пользователями рекомендуется использовать [SQL-ориентированный воркфлоу](../access-rights.md#access-control), который также поддерживается в ClickHouse.
Структура раздела `users`:
``` xml
@ -12,6 +15,8 @@
<!-- Or -->
<password_sha256_hex></password_sha256_hex>
<access_management>0|1</access_management>
<networks incl="networks" replace="replace">
</networks>
@ -67,6 +72,17 @@
Первая строка результата — пароль. Вторая строка — соответствующий ему двойной хэш SHA1.
### access_management {#access_management-user-setting}
Включает или выключает SQL-ориентированное [управление доступом](../access-rights.md#access-control) для пользователя.
Возможные значения:
- 0 — Выключено.
- 1 — Включено.
Значение по умолчанию: 0.
### user\_name/networks {#user-namenetworks}
Список сетей, из которых пользователь может подключиться к серверу ClickHouse.

View File

@ -498,8 +498,112 @@ ALTER TABLE [db.]table MATERIALIZE INDEX name IN PARTITION partition_name
Мутации линейно упорядочены между собой и накладываются на каждый кусок в порядке добавления. Мутации также упорядочены со вставками - гарантируется, что данные, вставленные в таблицу до начала выполнения запроса мутации, будут изменены, а данные, вставленные после окончания запроса мутации, изменены не будут. При этом мутации никак не блокируют вставки.
Запрос завершается немедленно после добавления информации о мутации (для реплицированных таблиц - в ZooKeeper, для нереплицированных - на файловую систему). Сама мутация выполняется асинхронно, используя настройки системного профиля. Следить за ходом её выполнения можно по таблице [`system.mutations`](../../operations/system-tables.md#system_tables-mutations). Добавленные мутации будут выполняться до конца даже в случае перезапуска серверов ClickHouse. Откатить мутацию после её добавления нельзя, но если мутация по какой-то причине не может выполниться до конца, её можно остановить с помощью запроса [`KILL MUTATION`](misc.md#kill-mutation).
Запрос завершается немедленно после добавления информации о мутации (для реплицированных таблиц - в ZooKeeper, для нереплицированных - на файловую систему). Сама мутация выполняется асинхронно, используя настройки системного профиля. Следить за ходом её выполнения можно по таблице [`system.mutations`](../../operations/system-tables.md#system_tables-mutations). Добавленные мутации будут выполняться до конца даже в случае перезапуска серверов ClickHouse. Откатить мутацию после её добавления нельзя, но если мутация по какой-то причине не может выполниться до конца, её можно остановить с помощью запроса [`KILL MUTATION`](misc.md#kill-mutation-statement).
Записи о последних выполненных мутациях удаляются не сразу (количество сохраняемых мутаций определяется параметром движка таблиц `finished_mutations_to_keep`). Более старые записи удаляются.
## ALTER USER {#alter-user-statement}
Изменяет аккаунт пользователя ClickHouse.
### Синтаксис {#alter-user-syntax}
``` sql
ALTER USER [IF EXISTS] name [ON CLUSTER cluster_name]
[RENAME TO new_name]
[IDENTIFIED [WITH {PLAINTEXT_PASSWORD|SHA256_PASSWORD|DOUBLE_SHA1_PASSWORD}] BY {'password'|'hash'}]
[[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
[DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
```
### Описание {#alter-user-dscr}
Для выполнения `ALTER USER` необходима привилегия [ALTER USER](grant.md#grant-access-management).
### Примеры {#alter-user-examples}
Установить ролями по умолчанию роли, назначенные пользователю:
``` sql
ALTER USER user DEFAULT ROLE role1, role2
```
Если роли не были назначены пользователю, ClickHouse выбрасывает исключение.
Установить ролями по умолчанию все роли, назначенные пользователю:
``` sql
ALTER USER user DEFAULT ROLE ALL
```
Если роль будет впоследствии назначена пользователю, она автоматически станет ролью по умолчанию.
Установить ролями по умолчанию все назначенные пользователю роли кроме `role1` и `role2`:
``` sql
ALTER USER user DEFAULT ROLE ALL EXCEPT role1, role2
```
## ALTER ROLE {#alter-role-statement}
Изменяет роль.
### Синтаксис {#alter-role-syntax}
``` sql
ALTER ROLE [IF EXISTS] name [ON CLUSTER cluster_name]
[RENAME TO new_name]
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
```
## ALTER ROW POLICY {#alter-row-policy-statement}
Изменяет политику доступа к строкам.
### Синтаксис {#alter-row-policy-syntax}
``` sql
ALTER [ROW] POLICY [IF EXISTS] name [ON CLUSTER cluster_name] ON [database.]table
[RENAME TO new_name]
[AS {PERMISSIVE | RESTRICTIVE}]
[FOR SELECT]
[USING {condition | NONE}][,...]
[TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
```
## ALTER QUOTA {#alter-quota-statement}
Изменяет квоту.
### Синтаксис {#alter-quota-syntax}
``` sql
ALTER QUOTA [IF EXISTS] name [ON CLUSTER cluster_name]
[RENAME TO new_name]
[KEYED BY {'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}]
[FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY}
{MAX { {QUERIES | ERRORS | RESULT ROWS | RESULT BYTES | READ ROWS | READ BYTES | EXECUTION TIME} = number } [,...] |
NO LIMITS | TRACKING ONLY} [,...]]
[TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
```
## ALTER SETTINGS PROFILE {#alter-settings-profile-statement}
Изменяет профили настроек.
### Синтаксис {#alter-settings-profile-syntax}
``` sql
ALTER SETTINGS PROFILE [IF EXISTS] name [ON CLUSTER cluster_name]
[RENAME TO new_name]
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
```
[Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/alter/) <!--hide-->

View File

@ -302,4 +302,206 @@ LIFETIME([MIN val1] MAX val2)
Смотрите [Внешние словари](../../sql-reference/statements/create.md).
## CREATE USER {#create-user-statement}
Создает [аккаунт пользователя](../../operations/access-rights.md#user-account-management).
### Синтаксис {#create-user-syntax}
```sql
CREATE USER [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]
[IDENTIFIED [WITH {NO_PASSWORD|PLAINTEXT_PASSWORD|SHA256_PASSWORD|SHA256_HASH|DOUBLE_SHA1_PASSWORD|DOUBLE_SHA1_HASH}] BY {'password'|'hash'}]
[HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
[DEFAULT ROLE role [,...]]
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
```
#### Идентификация
Существует несколько способов идентификации пользователя:
- `IDENTIFIED WITH no_password`
- `IDENTIFIED WITH plaintext_password BY 'qwerty'`
- `IDENTIFIED WITH sha256_password BY 'qwerty'` or `IDENTIFIED BY 'password'`
- `IDENTIFIED WITH sha256_hash BY 'hash'`
- `IDENTIFIED WITH double_sha1_password BY 'qwerty'`
- `IDENTIFIED WITH double_sha1_hash BY 'hash'`
#### Пользовательский хост
Пользовательский хост — это хост, с которого можно установить соединение с сервером ClickHouse. Хост задается в секции `HOST` следующими способами:
- `HOST IP 'ip_address_or_subnetwork'` — Пользователь может подключиться к серверу ClickHouse только с указанного IP-адреса или [подсети](https://ru.wikipedia.org/wiki/Подсеть). Примеры: `HOST IP '192.168.0.0/16'`, `HOST IP '2001:DB8::/32'`. При использовании в эксплуатации указывайте только элементы `HOST IP` (IP-адреса и маски подсети), так как использование `host` и `host_regexp` может привести к дополнительной задержке.
- `HOST ANY` — Пользователь может подключиться с любого хоста. Используется по умолчанию.
- `HOST LOCAL` — Пользователь может подключиться только локально.
- `HOST NAME 'fqdn'` — Хост задается через FQDN. Например, `HOST NAME 'mysite.com'`.
- `HOST NAME REGEXP 'regexp'` — Позволяет использовать регулярные выражения [pcre](http://www.pcre.org/), чтобы задать хосты. Например, `HOST NAME REGEXP '.*\.mysite\.com'`.
- `HOST LIKE 'template'` — Позволяет использовать оператор [LIKE](../functions/string-search-functions.md#function-like) для фильтрации хостов. Например, `HOST LIKE '%'` эквивалентен `HOST ANY`; `HOST LIKE '%.mysite.com'` разрешает подключение со всех хостов в домене `mysite.com`.
Также, чтобы задать хост, вы можете использовать `@` вместе с именем пользователя. Примеры:
- `CREATE USER mira@'127.0.0.1'` — Эквивалентно `HOST IP`.
- `CREATE USER mira@'localhost'` — Эквивалентно `HOST LOCAL`.
- `CREATE USER mira@'192.168.%.%'` — Эквивалентно `HOST LIKE`.
!!! info "Внимание"
ClickHouse трактует конструкцию `user_name@'address'` как имя пользователя целиком. То есть технически вы можете создать несколько пользователей с одинаковыми `user_name`, но разными частями конструкции после `@`, но лучше так не делать.
### Примеры {#create-user-examples}
Создать аккаунт `mira`, защищенный паролем `qwerty`:
```sql
CREATE USER mira HOST IP '127.0.0.1' IDENTIFIED WITH sha256_password BY 'qwerty'
```
Пользователь `mira` должен запустить клиентское приложение на хосте, где запущен ClickHouse.
Создать аккаунт `john`, назначить на него роли, сделать данные роли ролями по умолчанию:
``` sql
CREATE USER john DEFAULT ROLE role1, role2
```
Создать аккаунт `john` и установить ролями по умолчанию все его будущие роли:
``` sql
ALTER USER user DEFAULT ROLE ALL
```
Когда роль будет назначена аккаунту `john`, она автоматически станет ролью по умолчанию.
Создать аккаунт `john` и установить ролями по умолчанию все его будущие роли, кроме `role1` и `role2`:
``` sql
ALTER USER john DEFAULT ROLE ALL EXCEPT role1, role2
```
## CREATE ROLE {#create-role-statement}
Создает [роль](../../operations/access-rights.md#role-management).
### Синтаксис {#create-role-syntax}
```sql
CREATE ROLE [IF NOT EXISTS | OR REPLACE] name
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
```
### Описание {#create-role-description}
Роль — это набор [привилегий](grant.md#grant-privileges). Пользователь, которому назначена роль, получает все привилегии этой роли.
Одному пользователю можно назначить несколько ролей. Пользователи могут применять назначенные роли в произвольных комбинациях с помощью выражения [SET ROLE](misc.md#set-role-statement). Конечный объем привилегий — это комбинация всех привилегий всех примененных ролей. Если у пользователя имеются привилегии, присвоенные его аккаунту напрямую, они также прибавляются к привилегиям, присвоенным через роли.
Роли по умолчанию применяются при входе пользователя в систему. Установить роли по умолчанию можно с помощью выражений [SET DEFAULT ROLE](misc.md#set-default-role-statement) или [ALTER USER](alter.md#alter-user-statement).
Для отзыва роли используется выражение [REVOKE](revoke.md).
Для удаления роли используется выражение [DROP ROLE](misc.md#drop-role-statement). Удаленная роль автоматически отзывается у всех пользователей, которым была назначена.
### Примеры {#create-role-examples}
```sql
CREATE ROLE accountant;
GRANT SELECT ON db.* TO accountant;
```
Такая последовательность запросов создаст роль `accountant`, у которой есть привилегия на чтение из базы данных `accounting`.
Назначить роль `accountant` аккаунту `mira`:
```sql
GRANT accountant TO mira;
```
После назначения роли пользователь может ее применить и выполнять разрешенные ей запросы. Например:
```sql
SET ROLE accountant;
SELECT * FROM db.*;
```
## CREATE ROW POLICY {#create-row-policy-statement}
Создает [фильтр для строк](../../operations/access-rights.md#row-policy-management), которые пользователь может прочесть из таблицы.
### Синтаксис {#create-row-policy-syntax}
``` sql
CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE] policy_name [ON CLUSTER cluster_name] ON [db.]table
[AS {PERMISSIVE | RESTRICTIVE}]
[FOR SELECT]
[USING condition]
[TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
```
#### Секция AS {#create-row-policy-as}
С помощью данной секции можно создать политику разрешения или ограничения.
Политика разрешения предоставляет доступ к строкам. Разрешительные политики, которые применяются к одной таблице, объединяются с помощью логического оператора `OR`. Политики являются разрешительными по умолчанию.
Политика ограничения запрещает доступ к строкам. Ограничительные политики, которые применяются к одной таблице, объединяются логическим оператором `AND`.
Ограничительные политики применяются к строкам, прошедшим фильтр разрешительной политики. Если вы не зададите разрешительные политики, пользователь не сможет обращаться ни к каким строкам из таблицы.
#### Секция TO {#create-row-policy-to}
В секции `TO` вы можете перечислить как роли, так и пользователей. Например, `CREATE ROW POLICY ... TO accountant, john@localhost`.
Ключевым словом `ALL` обозначаются все пользователи, включая текущего. Ключевые слова `ALL EXCEPT` позволяют исключить пользователей из списка всех пользователей. Например, `CREATE ROW POLICY ... TO ALL EXCEPT accountant, john@localhost`
### Примеры
- `CREATE ROW POLICY filter ON mydb.mytable FOR SELECT USING a<1000 TO accountant, john@localhost`
- `CREATE ROW POLICY filter ON mydb.mytable FOR SELECT USING a<1000 TO ALL EXCEPT mira`
## CREATE QUOTA {#create-quota-statement}
Создает [квоту](../../operations/access-rights.md#quotas-management), которая может быть присвоена пользователю или роли.
### Синтаксис {#create-quota-syntax}
``` sql
CREATE QUOTA [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]
[KEYED BY {'none' | 'user name' | 'ip address' | 'client key' | 'client key or user name' | 'client key or ip address'}]
[FOR [RANDOMIZED] INTERVAL number {SECOND | MINUTE | HOUR | DAY}
{MAX { {QUERIES | ERRORS | RESULT ROWS | RESULT BYTES | READ ROWS | READ BYTES | EXECUTION TIME} = number } [,...] |
NO LIMITS | TRACKING ONLY} [,...]]
[TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
```
### Пример {#create-quota-example}
Ограничить максимальное количество запросов для текущего пользователя до 123 запросов каждые 15 месяцев:
``` sql
CREATE QUOTA qA FOR INTERVAL 15 MONTH MAX QUERIES 123 TO CURRENT_USER
```
## CREATE SETTINGS PROFILE {#create-settings-profile-statement}
Создает [профиль настроек](../../operations/access-rights.md#settings-profiles-management), который может быть присвоен пользователю или роли.
### Синтаксис {#create-settings-profile-syntax}
``` sql
CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] name [ON CLUSTER cluster_name]
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
```
### Пример {#create-settings-profile-syntax}
Создать профиль настроек `max_memory_usage_profile`, который содержит значение и ограничения для настройки `max_memory_usage`. Присвоить профиль пользователю `robin`:
``` sql
CREATE SETTINGS PROFILE max_memory_usage_profile SETTINGS max_memory_usage = 100000001 MIN 90000000 MAX 110000000 TO robin
```
[Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/create/) <!--hide-->

View File

@ -1 +0,0 @@
../../../en/sql-reference/statements/grant.md

View File

@ -0,0 +1,479 @@
# GRANT
- Присваивает [привилегии](#grant-privileges) пользователям или ролям ClickHouse.
- Назначает роли пользователям или другим ролям.
Отозвать привилегию можно с помощью выражения [REVOKE](revoke.md). Чтобы вывести список присвоенных привилегий, воспользуйтесь выражением [SHOW GRANTS](show.md#show-grants-statement).
## Синтаксис присвоения привилегий {#grant-privigele-syntax}
```sql
GRANT [ON CLUSTER cluster_name] privilege[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} TO {user | role | CURRENT_USER} [,...] [WITH GRANT OPTION]
```
- `privilege` — Тип привилегии
- `role` — Роль пользователя ClickHouse.
- `user` — Пользователь ClickHouse.
`WITH GRANT OPTION` разрешает пользователю или роли выполнять запрос `GRANT`. Пользователь может выдавать только те привилегии, которые есть у него, той же или меньшей области действий.
## Синтаксис назначения ролей {#assign-role-syntax}
```sql
GRANT [ON CLUSTER cluster_name] role [,...] TO {user | another_role | CURRENT_USER} [,...] [WITH ADMIN OPTION]
```
- `role` — Роль пользователя ClickHouse.
- `user` — Пользователь ClickHouse.
`WITH ADMIN OPTION` присваивает привилегию [ADMIN OPTION](#admin-option-privilege) пользователю или роли.
## Использование {#grant-usage}
Для использования `GRANT` пользователь должен иметь привилегию `GRANT OPTION`. Пользователь может выдавать привилегии только внутри области действий назначенных ему самому привилегий.
Например, администратор выдал привилегию пользователю `john`:
```sql
GRANT SELECT(x,y) ON db.table TO john WITH GRANT OPTION
```
Это означает, что пользователю `john` разрешено выполнять:
- `SELECT x,y FROM db.table`.
- `SELECT x FROM db.table`.
- `SELECT y FROM db.table`.
`john` не может выполнить `SELECT z FROM db.table` или `SELECT * FROM db.table`. После обработки данных запросов ClickHouse ничего не вернет — даже `x` или `y`. Единственное исключение — если таблица содержит только столбцы `x` и `y`. В таком случае ClickHouse вернет все данные.
Также у `john` есть привилегия `GRANT OPTION`. `john` может выдать другим пользователям привилегии той же или меньшей области действий из тех, которые есть у него.
При присвоении привилегий допускается использовать астериск (`*`) вместо имени таблицы или базы данных. Например, запрос `GRANT SELECT ON db.* TO john` позволит пользователю `john` выполнять `SELECT` над всеми таблицам в базе данных `db`. Также вы можете опускать имя базы данных. В таком случае привилегии позволят совершать операции над текущей базой данных. Например, запрос `GRANT SELECT ON * TO john` выдаст привилегию на выполнение `SELECT` над всеми таблицами в текущей базе данных; `GRANT SELECT ON mytable TO john` — только над таблицей `mytable` в текущей базе данных.
Доступ к базе данных `system` разрешен всегда (данная база данных используется при обработке запросов).
Вы можете присвоить несколько привилегий нескольким пользователям в одном запросе. Запрос `GRANT SELECT, INSERT ON *.* TO john, robin` позволит пользователям `john` и `robin` выполнять `INSERT` и `SELECT` над всеми таблицами всех баз данных на сервере.
## Привилегии {#grant-privileges}
Привилегия — это разрешение на выполнение определенного типа запросов.
Привилегии имеют иерархическую структуру. Набор разрешенных запросов зависит от области действия привилегии.
Иерархия привилегий:
- [SELECT](#grant-select)
- [INSERT](#grant-insert)
- [ALTER](#grant-alter)
- `ALTER TABLE`
- `ALTER UPDATE`
- `ALTER DELETE`
- `ALTER COLUMN`
- `ALTER ADD COLUMN`
- `ALTER DROP COLUMN`
- `ALTER MODIFY COLUMN`
- `ALTER COMMENT COLUMN`
- `ALTER CLEAR COLUMN`
- `ALTER RENAME COLUMN`
- `ALTER INDEX`
- `ALTER ORDER BY`
- `ALTER ADD INDEX`
- `ALTER DROP INDEX`
- `ALTER MATERIALIZE INDEX`
- `ALTER CLEAR INDEX`
- `ALTER CONSTRAINT`
- `ALTER ADD CONSTRAINT`
- `ALTER DROP CONSTRAINT`
- `ALTER TTL`
- `ALTER MATERIALIZE TTL`
- `ALTER SETTINGS`
- `ALTER MOVE PARTITION`
- `ALTER FETCH PARTITION`
- `ALTER FREEZE PARTITION`
- `ALTER VIEW`
- `ALTER VIEW REFRESH `
- `ALTER VIEW MODIFY QUERY`
- [CREATE](#grant-create)
- `CREATE DATABASE`
- `CREATE TABLE`
- `CREATE VIEW`
- `CREATE DICTIONARY`
- `CREATE TEMPORARY TABLE`
- [DROP](#grant-drop)
- `DROP DATABASE`
- `DROP TABLE`
- `DROP VIEW`
- `DROP DICTIONARY`
- [TRUNCATE](#grant-truncate)
- [OPTIMIZE](#grant-optimize)
- [SHOW](#grant-show)
- `SHOW DATABASES`
- `SHOW TABLES`
- `SHOW COLUMNS`
- `SHOW DICTIONARIES`
- [KILL QUERY](#grant-kill-query)
- [ACCESS MANAGEMENT](#grant-access-management)
- `CREATE USER`
- `ALTER USER`
- `DROP USER`
- `CREATE ROLE`
- `ALTER ROLE`
- `DROP ROLE`
- `CREATE ROW POLICY`
- `ALTER ROW POLICY`
- `DROP ROW POLICY`
- `CREATE QUOTA`
- `ALTER QUOTA`
- `DROP QUOTA`
- `CREATE SETTINGS PROFILE`
- `ALTER SETTINGS PROFILE`
- `DROP SETTINGS PROFILE`
- `SHOW ACCESS`
- `SHOW_USERS`
- `SHOW_ROLES`
- `SHOW_ROW_POLICIES`
- `SHOW_QUOTAS`
- `SHOW_SETTINGS_PROFILES`
- `ROLE ADMIN`
- [SYSTEM](#grant-system)
- `SYSTEM SHUTDOWN`
- `SYSTEM DROP CACHE`
- `SYSTEM DROP DNS CACHE`
- `SYSTEM DROP MARK CACHE`
- `SYSTEM DROP UNCOMPRESSED CACHE`
- `SYSTEM RELOAD`
- `SYSTEM RELOAD CONFIG`
- `SYSTEM RELOAD DICTIONARY`
- `SYSTEM RELOAD EMBEDDED DICTIONARIES`
- `SYSTEM MERGES`
- `SYSTEM TTL MERGES`
- `SYSTEM FETCHES`
- `SYSTEM MOVES`
- `SYSTEM SENDS`
- `SYSTEM DISTRIBUTED SENDS`
- `SYSTEM REPLICATED SENDS`
- `SYSTEM REPLICATION QUEUES`
- `SYSTEM SYNC REPLICA`
- `SYSTEM RESTART REPLICA`
- `SYSTEM FLUSH`
- `SYSTEM FLUSH DISTRIBUTED`
- `SYSTEM FLUSH LOGS`
- [INTROSPECTION](#grant-introspection)
- `addressToLine`
- `addressToSymbol`
- `demangle`
- [SOURCES](#grant-sources)
- `FILE`
- `URL`
- `REMOTE`
- `YSQL`
- `ODBC`
- `JDBC`
- `HDFS`
- `S3`
- [dictGet](#grant-dictget)
Примеры того, как трактуется данная иерархия:
- Привилегия `ALTER` включает все остальные `ALTER*` привилегии.
- `ALTER CONSTRAINT` включает `ALTER ADD CONSTRAINT` и `ALTER DROP CONSTRAINT`.
Привилегии применяются на разных уровнях. Уровень определяет синтаксис присваивания привилегии.
Уровни (от низшего к высшему):
- `COLUMN` — Привилегия присваивается для столбца, таблицы, базы данных или глобально.
- `TABLE` — Привилегия присваивается для таблицы, базы данных или глобально.
- `VIEW` — Привилегия присваивается для представления, базы данных или глобально.
- `DICTIONARY` — Привилегия присваивается для словаря, базы данных или глобально.
- `DATABASE` — Привилегия присваивается для базы данных или глобально.
- `GLOBAL` — Привилегия присваивается только глобально.
- `GROUP` — Группирует привилегии разных уровней. При присвоении привилегии уровня `GROUP` присваиваются только привилегии из группы в соответствии с используемым синтаксисом.
Примеры допустимого синтаксиса:
- `GRANT SELECT(x) ON db.table TO user`
- `GRANT SELECT ON db.* TO user`
Примеры недопустимого синтаксиса:
- `GRANT CREATE USER(x) ON db.table TO user`
- `GRANT CREATE USER ON db.* TO user`
Специальная привилегия [ALL](#grant-all) присваивает все привилегии пользователю или роли.
По умолчанию пользователь или роль не имеют привилегий.
Отсутствие привилегий у пользователя или роли отображается как привилегия [NONE](#grant-none).
Выполнение некоторых запросов требует определенного набора привилегий. Например, чтобы выполнить запрос [RENAME](misc.md#misc_operations-rename), нужны следующие привилегии: `SELECT`, `CREATE TABLE`, `INSERT` и `DROP TABLE`.
### SELECT {#grant-select}
Разрешает выполнять запросы [SELECT](select/index.md).
Уровень: `COLUMN`.
**Описание**
Пользователь с данной привилегией может выполнять запросы `SELECT` над определенными столбцами из определенной таблицы и базы данных. При включении в запрос других столбцов запрос ничего не вернет.
Рассмотрим следующую привилегию:
```sql
GRANT SELECT(x,y) ON db.table TO john
```
Данная привилегия позволяет пользователю `john` выполнять выборку данных из столбцов `x` и/или `y` в `db.table`, например, `SELECT x FROM db.table`. `john` не может выполнить `SELECT z FROM db.table` или `SELECT * FROM db.table`. После обработки данных запросов ClickHouse ничего не вернет — даже `x` или `y`. Единственное исключение — если таблица содержит только столбцы `x` и `y`. В таком случае ClickHouse вернет все данные.
### INSERT {#grant-insert}
Разрешает выполнять запросы [INSERT](insert-into.md).
Уровень: `COLUMN`.
**Описание**
Пользователь с данной привилегией может выполнять запросы `INSERT` над определенными столбцами из определенной таблицы и базы данных. При включении в запрос других столбцов запрос не добавит никаких данных.
**Пример**
```sql
GRANT INSERT(x,y) ON db.table TO john
```
Присвоенная привилегия позволит пользователю `john` вставить данные в столбцы `x` и/или `y` в `db.table`.
### ALTER {#grant-alter}
Разрешает выполнять запросы [ALTER](alter.md) в соответствии со следующей иерархией привилегий:
- `ALTER`. Уровень: `COLUMN`.
- `ALTER TABLE`. Уровень: `GROUP`
- `ALTER UPDATE`. Уровень: `COLUMN`. Алиасы: `UPDATE`
- `ALTER DELETE`. Уровень: `COLUMN`. Алиасы: `DELETE`
- `ALTER COLUMN`. Уровень: `GROUP`
- `ALTER ADD COLUMN`. Уровень: `COLUMN`. Алиасы: `ADD COLUMN`
- `ALTER DROP COLUMN`. Уровень: `COLUMN`. Алиасы: `DROP COLUMN`
- `ALTER MODIFY COLUMN`. Уровень: `COLUMN`. Алиасы: `MODIFY COLUMN`
- `ALTER COMMENT COLUMN`. Уровень: `COLUMN`. Алиасы: `COMMENT COLUMN`
- `ALTER CLEAR COLUMN`. Уровень: `COLUMN`. Алиасы: `CLEAR COLUMN`
- `ALTER RENAME COLUMN`. Уровень: `COLUMN`. Алиасы: `RENAME COLUMN`
- `ALTER INDEX`. Уровень: `GROUP`. Алиасы: `INDEX`
- `ALTER ORDER BY`. Уровень: `TABLE`. Алиасы: `ALTER MODIFY ORDER BY`, `MODIFY ORDER BY`
- `ALTER ADD INDEX`. Уровень: `TABLE`. Алиасы: `ADD INDEX`
- `ALTER DROP INDEX`. Уровень: `TABLE`. Алиасы: `DROP INDEX`
- `ALTER MATERIALIZE INDEX`. Уровень: `TABLE`. Алиасы: `MATERIALIZE INDEX`
- `ALTER CLEAR INDEX`. Уровень: `TABLE`. Алиасы: `CLEAR INDEX`
- `ALTER CONSTRAINT`. Уровень: `GROUP`. Алиасы: `CONSTRAINT`
- `ALTER ADD CONSTRAINT`. Уровень: `TABLE`. Алиасы: `ADD CONSTRAINT`
- `ALTER DROP CONSTRAINT`. Уровень: `TABLE`. Алиасы: `DROP CONSTRAINT`
- `ALTER TTL`. Уровень: `TABLE`. Алиасы: `ALTER MODIFY TTL`, `MODIFY TTL`
- `ALTER MATERIALIZE TTL`. Уровень: `TABLE`. Алиасы: `MATERIALIZE TTL`
- `ALTER SETTINGS`. Уровень: `TABLE`. Алиасы: `ALTER SETTING`, `ALTER MODIFY SETTING`, `MODIFY SETTING`
- `ALTER MOVE PARTITION`. Уровень: `TABLE`. Алиасы: `ALTER MOVE PART`, `MOVE PARTITION`, `MOVE PART`
- `ALTER FETCH PARTITION`. Уровень: `TABLE`. Алиасы: `FETCH PARTITION`
- `ALTER FREEZE PARTITION`. Уровень: `TABLE`. Алиасы: `FREEZE PARTITION`
- `ALTER VIEW` Уровень: `GROUP`
- `ALTER VIEW REFRESH `. Уровень: `VIEW`. Алиасы: `ALTER LIVE VIEW REFRESH`, `REFRESH VIEW`
- `ALTER VIEW MODIFY QUERY`. Уровень: `VIEW`. Алиасы: `ALTER TABLE MODIFY QUERY`
Примеры того, как трактуется данная иерархия:
- Привилегия `ALTER` включает все остальные `ALTER*` привилегии.
- `ALTER CONSTRAINT` включает `ALTER ADD CONSTRAINT` и `ALTER DROP CONSTRAINT`.
**Дополнительно**
- Привилегия `MODIFY SETTING` позволяет изменять настройки движков таблиц. Не влияет на настройки или конфигурационные параметры сервера.
- Операция `ATTACH` требует наличие привилегии [CREATE](#grant-create).
- Операция `DETACH` требует наличие привилегии [DROP](#grant-drop).
- Для остановки мутации с помощью [KILL MUTATION](misc.md#kill-mutation-statement), необходима привилегия на выполнение данной мутации. Например, чтобы остановить запрос `ALTER UPDATE`, необходима одна из привилегий: `ALTER UPDATE`, `ALTER TABLE` или `ALTER`.
### CREATE {#grant-create}
Разрешает выполнять DDL-запросы [CREATE](create.md) и [ATTACH](misc.md#attach) в соответствии со следующей иерархией привилегий:
- `CREATE`. Уровень: `GROUP`
- `CREATE DATABASE`. Уровень: `DATABASE`
- `CREATE TABLE`. Уровень: `TABLE`
- `CREATE VIEW`. Уровень: `VIEW`
- `CREATE DICTIONARY`. Уровень: `DICTIONARY`
- `CREATE TEMPORARY TABLE`. Уровень: `GLOBAL`
**Дополнительно**
- Для удаления созданной таблицы пользователю необходима привилегия [DROP](#grant-drop).
### DROP {#grant-drop}
Разрешает выполнять запросы [DROP](misc.md#drop) и [DETACH](misc.md#detach-statement) в соответствии со следующей иерархией привилегий:
- `DROP`. Уровень:
- `DROP DATABASE`. Уровень: `DATABASE`
- `DROP TABLE`. Уровень: `TABLE`
- `DROP VIEW`. Уровень: `VIEW`
- `DROP DICTIONARY`. Уровень: `DICTIONARY`
### TRUNCATE {#grant-truncate}
Разрешает выполнять запросы [TRUNCATE](misc.md#truncate-statement).
Уровень: `TABLE`.
### OPTIMIZE {#grant-optimize}
Разрешает выполнять запросы [OPTIMIZE TABLE](misc.md#misc_operations-optimize).
Уровень: `TABLE`.
### SHOW {#grant-show}
Разрешает выполнять запросы `SHOW`, `DESCRIBE`, `USE` и `EXISTS` в соответствии со следующей иерархией привилегий:
- `SHOW`. Уровень: `GROUP`
- `SHOW DATABASES`. Уровень: `DATABASE`. Разрешает выполнять запросы `SHOW DATABASES`, `SHOW CREATE DATABASE`, `USE <database>`.
- `SHOW TABLES`. Уровень: `TABLE`. Разрешает выполнять запросы `SHOW TABLES`, `EXISTS <table>`, `CHECK <table>`.
- `SHOW COLUMNS`. Уровень: `COLUMN`. Разрешает выполнять запросы `SHOW CREATE TABLE`, `DESCRIBE`.
- `SHOW DICTIONARIES`. Уровень: `DICTIONARY`. Разрешает выполнять запросы `SHOW DICTIONARIES`, `SHOW CREATE DICTIONARY`, `EXISTS <dictionary>`.
**Дополнительно**
У пользователя есть привилегия `SHOW`, если ему присвоена любая другая привилегия по отношению к определенной таблице, словарю или базе данных.
### KILL QUERY {#grant-kill-query}
Разрешает выполнять запросы [KILL](misc.md#kill-query-statement) в соответствии со следующей иерархией привилегий:
Уровень: `GLOBAL`.
**Дополнительно**
`KILL QUERY` позволяет пользователю останавливать запросы других пользователей.
### ACCESS MANAGEMENT {#grant-access-management}
Разрешает пользователю выполнять запросы на управление пользователями, ролями и политиками доступа к строкам.
- `ACCESS MANAGEMENT`. Уровень: `GROUP`
- `CREATE USER`. Уровень: `GLOBAL`
- `ALTER USER`. Уровень: `GLOBAL`
- `DROP USER`. Уровень: `GLOBAL`
- `CREATE ROLE`. Уровень: `GLOBAL`
- `ALTER ROLE`. Уровень: `GLOBAL`
- `DROP ROLE`. Уровень: `GLOBAL`
- `ROLE ADMIN`. Уровень: `GLOBAL`
- `CREATE ROW POLICY`. Уровень: `GLOBAL`. Алиасы: `CREATE POLICY`
- `ALTER ROW POLICY`. Уровень: `GLOBAL`. Алиасы: `ALTER POLICY`
- `DROP ROW POLICY`. Уровень: `GLOBAL`. Алиасы: `DROP POLICY`
- `CREATE QUOTA`. Уровень: `GLOBAL`
- `ALTER QUOTA`. Уровень: `GLOBAL`
- `DROP QUOTA`. Уровень: `GLOBAL`
- `CREATE SETTINGS PROFILE`. Уровень: `GLOBAL`. Алиасы: `CREATE PROFILE`
- `ALTER SETTINGS PROFILE`. Уровень: `GLOBAL`. Алиасы: `ALTER PROFILE`
- `DROP SETTINGS PROFILE`. Уровень: `GLOBAL`. Алиасы: `DROP PROFILE`
- `SHOW ACCESS`. Уровень: `GROUP`
- `SHOW_USERS`. Уровень: `GLOBAL`. Алиасы: `SHOW CREATE USER`
- `SHOW_ROLES`. Уровень: `GLOBAL`. Алиасы: `SHOW CREATE ROLE`
- `SHOW_ROW_POLICIES`. Уровень: `GLOBAL`. Алиасы: `SHOW POLICIES`, `SHOW CREATE ROW POLICY`, `SHOW CREATE POLICY`
- `SHOW_QUOTAS`. Уровень: `GLOBAL`. Алиасы: `SHOW CREATE QUOTA`
- `SHOW_SETTINGS_PROFILES`. Уровень: `GLOBAL`. Алиасы: `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`, `SHOW CREATE PROFILE`
Привилегия `ROLE ADMIN` разрешает пользователю назначать и отзывать любые роли, включая те, которые не назначены пользователю с опцией администратора.
### SYSTEM {#grant-system}
Разрешает выполнять запросы [SYSTEM](system.md) в соответствии со следующей иерархией привилегий:
- `SYSTEM`. Уровень: `GROUP`
- `SYSTEM SHUTDOWN`. Уровень: `GLOBAL`. Алиасы: `SYSTEM KILL`, `SHUTDOWN`
- `SYSTEM DROP CACHE`. Алиасы: `DROP CACHE`
- `SYSTEM DROP DNS CACHE`. Уровень: `GLOBAL`. Алиасы: `SYSTEM DROP DNS`, `DROP DNS CACHE`, `DROP DNS`
- `SYSTEM DROP MARK CACHE`. Уровень: `GLOBAL`. Алиасы: `SYSTEM DROP MARK`, `DROP MARK CACHE`, `DROP MARKS`
- `SYSTEM DROP UNCOMPRESSED CACHE`. Уровень: `GLOBAL`. Алиасы: `SYSTEM DROP UNCOMPRESSED`, `DROP UNCOMPRESSED CACHE`, `DROP UNCOMPRESSED`
- `SYSTEM RELOAD`. Уровень: `GROUP`
- `SYSTEM RELOAD CONFIG`. Уровень: `GLOBAL`. Алиасы: `RELOAD CONFIG`
- `SYSTEM RELOAD DICTIONARY`. Уровень: `GLOBAL`. Алиасы: `SYSTEM RELOAD DICTIONARIES`, `RELOAD DICTIONARY`, `RELOAD DICTIONARIES`
- `SYSTEM RELOAD EMBEDDED DICTIONARIES`. Уровень: `GLOBAL`. Алиасы: `RELOAD EMBEDDED DICTIONARIES`
- `SYSTEM MERGES`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP MERGES`, `SYSTEM START MERGES`, `STOP MERGES`, `START MERGES`
- `SYSTEM TTL MERGES`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP TTL MERGES`, `SYSTEM START TTL MERGES`, `STOP TTL MERGES`, `START TTL MERGES`
- `SYSTEM FETCHES`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP FETCHES`, `SYSTEM START FETCHES`, `STOP FETCHES`, `START FETCHES`
- `SYSTEM MOVES`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP MOVES`, `SYSTEM START MOVES`, `STOP MOVES`, `START MOVES`
- `SYSTEM SENDS`. Уровень: `GROUP`. Алиасы: `SYSTEM STOP SENDS`, `SYSTEM START SENDS`, `STOP SENDS`, `START SENDS`
- `SYSTEM DISTRIBUTED SENDS`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP DISTRIBUTED SENDS`, `SYSTEM START DISTRIBUTED SENDS`, `STOP DISTRIBUTED SENDS`, `START DISTRIBUTED SENDS`
- `SYSTEM REPLICATED SENDS`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP REPLICATED SENDS`, `SYSTEM START REPLICATED SENDS`, `STOP REPLICATED SENDS`, `START REPLICATED SENDS`
- `SYSTEM REPLICATION QUEUES`. Уровень: `TABLE`. Алиасы: `SYSTEM STOP REPLICATION QUEUES`, `SYSTEM START REPLICATION QUEUES`, `STOP REPLICATION QUEUES`, `START REPLICATION QUEUES`
- `SYSTEM SYNC REPLICA`. Уровень: `TABLE`. Алиасы: `SYNC REPLICA`
- `SYSTEM RESTART REPLICA`. Уровень: `TABLE`. Алиасы: `RESTART REPLICA`
- `SYSTEM FLUSH`. Уровень: `GROUP`
- `SYSTEM FLUSH DISTRIBUTED`. Уровень: `TABLE`. Алиасы: `FLUSH DISTRIBUTED`
- `SYSTEM FLUSH LOGS`. Уровень: `GLOBAL`. Алиасы: `FLUSH LOGS`
Привилегия `SYSTEM RELOAD EMBEDDED DICTIONARIES` имплицитно присваивается привилегией `SYSTEM RELOAD DICTIONARY ON *.*`.
### INTROSPECTION {#grant-introspection}
Разрешает использовать функции [интроспекции](../../operations/optimizing-performance/sampling-query-profiler.md).
- `INTROSPECTION`. Уровень: `GROUP`. Алиасы: `INTROSPECTION FUNCTIONS`
- `addressToLine`. Уровень: `GLOBAL`
- `addressToSymbol`. Уровень: `GLOBAL`
- `demangle`. Уровень: `GLOBAL`
### SOURCES {#grant-sources}
Разрешает использовать внешние источники данных. Применяется к [движкам таблиц](../../engines/table-engines/index.md) и [табличным функциям](../table-functions/index.md#table-functions).
- `SOURCES`. Уровень: `GROUP`
- `FILE`. Уровень: `GLOBAL`
- `URL`. Уровень: `GLOBAL`
- `REMOTE`. Уровень: `GLOBAL`
- `YSQL`. Уровень: `GLOBAL`
- `ODBC`. Уровень: `GLOBAL`
- `JDBC`. Уровень: `GLOBAL`
- `HDFS`. Уровень: `GLOBAL`
- `S3`. Уровень: `GLOBAL`
Привилегия `SOURCES` разрешает использование всех источников. Также вы можете присвоить привилегию для каждого источника отдельно. Для использования источников необходимы дополнительные привилегии.
Примеры:
- Чтобы создать таблицу с [движком MySQL](../../engines/table-engines/integrations/mysql.md), необходимы привилегии `CREATE TABLE (ON db.table_name)` и `MYSQL`.
- Чтобы использовать [табличную функцию mysql](../table-functions/mysql.md), необходимы привилегии `CREATE TEMPORARY TABLE` и `MYSQL`.
### dictGet {#grant-dictget}
- `dictGet`. Алиасы: `dictHas`, `dictGetHierarchy`, `dictIsIn`
Разрешает вызывать функции [dictGet](../functions/ext-dict-functions.md#dictget), [dictHas](../functions/ext-dict-functions.md#dicthas), [dictGetHierarchy](../functions/ext-dict-functions.md#dictgethierarchy), [dictIsIn](../functions/ext-dict-functions.md#dictisin).
Уровень: `DICTIONARY`.
**Примеры**
- `GRANT dictGet ON mydb.mydictionary TO john`
- `GRANT dictGet ON mydictionary TO john`
### ALL {#grant-all}
Присваивает пользователю или роли все привилегии на объект с регулируемым доступом.
### NONE {#grant-none}
Не присваивает никаких привилегий.
### ADMIN OPTION {#admin-option-privilege}
Привилегия `ADMIN OPTION` разрешает пользователю назначать свои роли другому пользователю.
[Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/grant/) <!--hide-->

View File

@ -70,7 +70,7 @@ DESC|DESCRIBE TABLE [db.]table [INTO OUTFILE filename] [FORMAT format]
Вложенные структуры данных выводятся в «развёрнутом» виде. То есть, каждый столбец - по отдельности, с именем через точку.
## DETACH {#detach}
## DETACH {#detach-statement}
Удаляет из сервера информацию о таблице name. Сервер перестаёт знать о существовании таблицы.
@ -91,9 +91,6 @@ DETACH TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster]
DROP DATABASE [IF EXISTS] db [ON CLUSTER cluster]
```
Удаляет все таблицы внутри базы данных db, а затем саму базу данных db.
Если указано `IF EXISTS` - не выдавать ошибку, если база данных не существует.
``` sql
DROP [TEMPORARY] TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster]
```
@ -101,6 +98,68 @@ DROP [TEMPORARY] TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster]
Удаляет таблицу.
Если указано `IF EXISTS` - не выдавать ошибку, если таблица не существует или база данных не существует.
## DROP USER {#drop-user-statement}
Удаляет пользователя.
### Синтаксис {#drop-user-syntax}
```sql
DROP USER [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
```
## DROP ROLE {#drop-role-statement}
Удаляет роль.
При удалении роль отзывается у всех объектов системы доступа, которым она присвоена.
### Синтаксис {#drop-role-syntax}
```sql
DROP ROLE [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
```
## DROP ROW POLICY {#drop-row-policy-statement}
Удаляет политику доступа к строкам.
При удалении политика отзывается у всех объектов системы доступа, которым она присвоена.
### Синтаксис {#drop-row-policy-syntax}
``` sql
DROP [ROW] POLICY [IF EXISTS] name [,...] ON [database.]table [,...] [ON CLUSTER cluster_name]
```
## DROP QUOTA {#drop-quota-statement}
Удаляет квоту.
При удалении квота отзывается у всех объектов системы доступа, которым она присвоена.
### Синтаксис {#drop-quota-syntax}
``` sql
DROP QUOTA [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
```
## DROP SETTINGS PROFILE {#drop-settings-profile-statement}
Удаляет профиль настроек.
При удалении профиль отзывается у всех объектов системы доступа, которым он присвоен.
### Синтаксис {#drop-settings-profile-syntax}
``` sql
DROP [SETTINGS] PROFILE [IF EXISTS] name [,...] [ON CLUSTER cluster_name]
```
## EXISTS {#exists}
``` sql
@ -109,7 +168,7 @@ EXISTS [TEMPORARY] TABLE [db.]name [INTO OUTFILE filename] [FORMAT format]
Возвращает один столбец типа `UInt8`, содержащий одно значение - `0`, если таблицы или БД не существует и `1`, если таблица в указанной БД существует.
## KILL QUERY {#kill-query}
## KILL QUERY {#kill-query-statement}
``` sql
KILL QUERY [ON CLUSTER cluster]
@ -144,7 +203,7 @@ Readonly-пользователи могут останавливать толь
Тестовый вариант запроса (`TEST`) только проверяет права пользователя и выводит список запросов для остановки.
## KILL MUTATION {#kill-mutation}
## KILL MUTATION {#kill-mutation-statement}
``` sql
KILL MUTATION [ON CLUSTER cluster]
@ -215,7 +274,57 @@ SET profile = 'profile-name-from-the-settings-file'
Подробности смотрите в разделе [Настройки](../../operations/settings/settings.md).
## TRUNCATE {#truncate}
## SET ROLE {#set-role-statement}
Активирует роли для текущего пользователя.
### Синтаксис {#set-role-syntax}
``` sql
SET ROLE {DEFAULT | NONE | role [,...] | ALL | ALL EXCEPT role [,...]}
```
## SET DEFAULT ROLE {#set-default-role-statement}
Устанавливает роли по умолчанию для пользователя.
Роли по умолчанию активируются автоматически при входе пользователя. Ролями по умолчанию могут быть установлены только ранее назначенные роли. Если роль не назначена пользователю, ClickHouse выбрасывает исключение.
### Синтаксис {#set-default-role-syntax}
``` sql
SET DEFAULT ROLE {NONE | role [,...] | ALL | ALL EXCEPT role [,...]} TO {user|CURRENT_USER} [,...]
```
### Примеры {#set-default-role-examples}
Установить несколько ролей по умолчанию для пользователя:
``` sql
SET DEFAULT ROLE role1, role2, ... TO user
```
Установить ролями по умолчанию все назначенные пользователю роли:
``` sql
SET DEFAULT ROLE ALL TO user
```
Удалить роли по умолчанию для пользователя:
``` sql
SET DEFAULT ROLE NONE TO user
```
Установить ролями по умолчанию все назначенные пользователю роли за исключением указанных:
```sql
SET DEFAULT ROLE ALL EXCEPT role1, role2 TO user
```
## TRUNCATE {#truncate-statement}
``` sql
TRUNCATE TABLE [IF EXISTS] [db.]name [ON CLUSTER cluster]

View File

@ -1 +0,0 @@
../../../en/sql-reference/statements/revoke.md

View File

@ -0,0 +1,43 @@
# REVOKE
Отзывает привилегии у пользователей или ролей.
## Синтаксис {#revoke-syntax}
**Отзыв привилегий у пользователей**
``` sql
REVOKE [ON CLUSTER cluster_name] privilege[(column_name [,...])] [,...] ON {db.table|db.*|*.*|table|*} FROM {user | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user | CURRENT_USER} [,...]
```
**Отзыв ролей у пользователей**
``` sql
REVOKE [ON CLUSTER cluster_name] [ADMIN OPTION FOR] role [,...] FROM {user | role | CURRENT_USER} [,...] | ALL | ALL EXCEPT {user_name | role_name | CURRENT_USER} [,...]
```
## Описание {#revoke-description}
Для отзыва привилегий можно использовать привилегию более широкой области действия. Например, если у пользователя есть привилегия `SELECT (x,y)`, администратор может отозвать ее с помощью одного из запросов: `REVOKE SELECT(x,y) ...`, `REVOKE SELECT * ...` или даже `REVOKE ALL PRIVILEGES ...`.
### Частичный отзыв {#partial-revokes-dscr}
Вы можете отозвать часть привилегии. Например, если у пользователя есть привилегия `SELECT *.*`, вы можете отозвать привилегию на чтение данных из какой-то таблицы или базы данных.
## Примеры {#revoke-example}
Присвоить пользователю `john` привилегию на `SELECT` из всех баз данных кроме `accounts`:
``` sql
GRANT SELECT ON *.* TO john;
REVOKE SELECT ON accounts.* FROM john;
```
Присвоить пользователю `mira` привилегию на `SELECT` из всех столбцов таблицы `accounts.staff` кроме столбца `wage`:
``` sql
GRANT SELECT ON accounts.staff TO mira;
REVOKE SELECT(wage) ON accounts.staff FROM mira;
```
[Оригинальная статья](https://clickhouse.tech/docs/en/operations/settings/settings/) <!-- hide -->

View File

@ -1,4 +1,4 @@
# Секции PREWHERE {#prewhere-clause}
# Секция PREWHERE {#prewhere-clause}
Prewhere — это оптимизация для более эффективного применения фильтрации. Она включена по умолчанию, даже если секция `PREWHERE` явно не указана. В этом случае работает автоматическое перемещение части выражения из [WHERE](where.md) до стадии prewhere. Роль секции `PREWHERE` только для управления этой оптимизацией, если вы думаете, что знаете, как сделать перемещение условия лучше, чем это происходит по умолчанию.

View File

@ -1,6 +1,6 @@
# Предложение WHERE {#select-where}
# Секция WHERE {#select-where}
Позволяет задать выражение, которое ClickHouse использует для фильтрации данных перед всеми другими действиями в запросе кроме выражений, содержащихся в секции [PREWHERE](prewhere.md#select-prewhere). Обычно, это выражение с логическими операторами.
Позволяет задать выражение, которое ClickHouse использует для фильтрации данных перед всеми другими действиями в запросе кроме выражений, содержащихся в секции [PREWHERE](prewhere.md#prewhere-clause). Обычно, это выражение с логическими операторами.
Результат выражения должен иметь тип `UInt8`.

View File

@ -95,4 +95,78 @@ SHOW DICTIONARIES FROM db LIKE '%reg%' LIMIT 2
└──────────────┘
```
## SHOW GRANTS {#show-grants-statement}
Выводит привилегии пользователя.
### Синтаксис {#show-grants-syntax}
``` sql
SHOW GRANTS [FOR user]
```
Если пользователь не задан, запрос возвращает привилегии текущего пользователя.
## SHOW CREATE USER {#show-create-user-statement}
Выводит параметры, использованные при [создании пользователя](create.md#create-user-statement).
`SHOW CREATE USER` не возвращает пароль пользователя.
### Синтаксис {#show-create-user-syntax}
``` sql
SHOW CREATE USER [name | CURRENT_USER]
```
## SHOW CREATE ROLE {#show-create-role-statement}
Выводит параметры, использованные при [создании роли](create.md#create-role-statement).
### Синтаксис {#show-create-role-syntax}
``` sql
SHOW CREATE ROLE name
```
## SHOW CREATE ROW POLICY {#show-create-row-policy-statement}
Выводит параметры, использованные при [создании политики доступа к строкам](create.md#create-row-policy-statement).
### Синтаксис {#show-create-row-policy-syntax}
```sql
SHOW CREATE [ROW] POLICY name ON [database.]table
```
## SHOW CREATE QUOTA {#show-create-quota-statement}
Выводит параметры, использованные при [создании квоты](create.md#create-quota-statement).
### Синтаксис {#show-create-row-policy-syntax}
```sql
SHOW CREATE QUOTA [name | CURRENT]
```
## SHOW CREATE SETTINGS PROFILE {#show-create-settings-profile-statement}
Выводит параметры, использованные при [создании профиля настроек](create.md#create-settings-profile-statement).
### Синтаксис {#show-create-row-policy-syntax}
```sql
SHOW CREATE [SETTINGS] PROFILE name
```
[Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/show/) <!--hide-->

View File

@ -5,7 +5,7 @@ toc_priority: 34
toc_title: "\u0412\u0432\u0435\u0434\u0435\u043D\u0438\u0435"
---
# Табличные функции {#tablichnye-funktsii}
# Табличные функции {#table-functions}
Табличные функции — это метод создания таблиц.

View File

@ -209,7 +209,7 @@ Eğer `http_port` belirtilmişse, OpenSSL yapılandırması ayarlanmış olsa bi
**Örnek**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}

View File

@ -207,7 +207,7 @@ ClickHouse每x秒重新加载内置字典。 这使得编辑字典 “on the fly
**示例**
``` xml
<https>0000</https>
<https_port>9999</https_port>
```
## http\_server\_default\_response {#server_configuration_parameters-http_server_default_response}

View File

@ -858,7 +858,7 @@ public:
ColumnPtr new_nested_column = nested_model->generate(nested_column);
return ColumnArray::create((*std::move(new_nested_column)).mutate(), (*std::move(column_array.getOffsetsPtr())).mutate());
return ColumnArray::create(IColumn::mutate(std::move(new_nested_column)), IColumn::mutate(std::move(column_array.getOffsetsPtr())));
}
void updateSeed() override
@ -896,7 +896,7 @@ public:
ColumnPtr new_nested_column = nested_model->generate(nested_column);
return ColumnNullable::create((*std::move(new_nested_column)).mutate(), (*std::move(column_nullable.getNullMapColumnPtr())).mutate());
return ColumnNullable::create(IColumn::mutate(std::move(new_nested_column)), IColumn::mutate(std::move(column_nullable.getNullMapColumnPtr())));
}
void updateSeed() override

View File

@ -5,6 +5,7 @@ set (CLICKHOUSE_ODBC_BRIDGE_SOURCES
IdentifierQuoteHandler.cpp
MainHandler.cpp
ODBCBlockInputStream.cpp
ODBCBlockOutputStream.cpp
ODBCBridge.cpp
PingHandler.cpp
validateODBCConnectionString.cpp

View File

@ -16,6 +16,7 @@
# include <Poco/Net/HTTPServerResponse.h>
# include <Poco/NumberParser.h>
# include <common/logger_useful.h>
# include <Common/quoteString.h>
# include <ext/scope_guard.h>
# include "getIdentifierQuote.h"
# include "validateODBCConnectionString.h"
@ -58,11 +59,6 @@ namespace
}
}
namespace ErrorCodes
{
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
}
void ODBCColumnsInfoHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response)
{
Poco::Net::HTMLForm params(request, request.stream());
@ -116,7 +112,7 @@ void ODBCColumnsInfoHandler::handleRequest(Poco::Net::HTTPServerRequest & reques
const auto & context_settings = context.getSettingsRef();
/// TODO Why not do SQLColumns instead?
std::string name = schema_name.empty() ? table_name : schema_name + "." + table_name;
std::string name = schema_name.empty() ? backQuoteIfNeed(table_name) : backQuoteIfNeed(schema_name) + "." + backQuoteIfNeed(table_name);
std::stringstream ss;
std::string input = "SELECT * FROM " + name + " WHERE 1 = 0";
ParserQueryWithOutput parser;
@ -124,17 +120,7 @@ void ODBCColumnsInfoHandler::handleRequest(Poco::Net::HTTPServerRequest & reques
IAST::FormatSettings settings(ss, true);
settings.always_quote_identifiers = true;
auto identifier_quote = getIdentifierQuote(hdbc);
if (identifier_quote.length() == 0)
settings.identifier_quoting_style = IdentifierQuotingStyle::None;
else if (identifier_quote[0] == '`')
settings.identifier_quoting_style = IdentifierQuotingStyle::Backticks;
else if (identifier_quote[0] == '"')
settings.identifier_quoting_style = IdentifierQuotingStyle::DoubleQuotes;
else
throw Exception("Can not map quote identifier '" + identifier_quote + "' to IdentifierQuotingStyle value", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
settings.identifier_quoting_style = getQuotingStyle(hdbc);
select->format(settings);
std::string query = ss.str();

View File

@ -30,8 +30,10 @@ Poco::Net::HTTPRequestHandler * HandlerFactory::createRequestHandler(const Poco:
#else
return nullptr;
#endif
else if (uri.getPath() == "/write")
return new ODBCHandler(pool_map, keep_alive_timeout, context, "write");
else
return new ODBCHandler(pool_map, keep_alive_timeout, context);
return new ODBCHandler(pool_map, keep_alive_timeout, context, "read");
}
return nullptr;
}

View File

@ -5,17 +5,25 @@
#include <DataStreams/copyData.h>
#include <DataTypes/DataTypeFactory.h>
#include "ODBCBlockInputStream.h"
#include "ODBCBlockOutputStream.h"
#include <Formats/FormatFactory.h>
#include <IO/WriteBufferFromHTTPServerResponse.h>
#include <IO/WriteHelpers.h>
#include <IO/ReadHelpers.h>
#include <Interpreters/Context.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/HTMLForm.h>
#include <common/logger_useful.h>
#include <mutex>
#include <Poco/ThreadPool.h>
#include <IO/ReadBufferFromIStream.h>
#include <Columns/ColumnsNumber.h>
#include "getIdentifierQuote.h"
#if USE_ODBC
#include <Poco/Data/ODBC/SessionImpl.h>
#define POCO_SQL_ODBC_CLASS Poco::Data::ODBC
#endif
namespace DB
{
@ -63,34 +71,36 @@ ODBCHandler::PoolPtr ODBCHandler::getPool(const std::string & connection_str)
return pool_map->at(connection_str);
}
void ODBCHandler::processError(Poco::Net::HTTPServerResponse & response, const std::string & message)
{
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
if (!response.sent())
response.send() << message << std::endl;
LOG_WARNING(log, message);
}
void ODBCHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response)
{
Poco::Net::HTMLForm params(request, request.stream());
Poco::Net::HTMLForm params(request);
if (mode == "read")
params.read(request.stream());
LOG_TRACE(log, "Request URI: " + request.getURI());
auto process_error = [&response, this](const std::string & message)
if (mode == "read" && !params.has("query"))
{
response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
if (!response.sent())
response.send() << message << std::endl;
LOG_WARNING(log, message);
};
if (!params.has("query"))
{
process_error("No 'query' in request body");
processError(response, "No 'query' in request body");
return;
}
if (!params.has("columns"))
{
process_error("No 'columns' in request URL");
processError(response, "No 'columns' in request URL");
return;
}
if (!params.has("connection_string"))
{
process_error("No 'connection_string' in request URL");
processError(response, "No 'connection_string' in request URL");
return;
}
@ -100,7 +110,7 @@ void ODBCHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Ne
std::string max_block_size_str = params.get("max_block_size", "");
if (max_block_size_str.empty())
{
process_error("Empty max_block_size specified");
processError(response, "Empty max_block_size specified");
return;
}
max_block_size = parse<size_t>(max_block_size_str);
@ -114,33 +124,70 @@ void ODBCHandler::handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Ne
}
catch (const Exception & ex)
{
process_error("Invalid 'columns' parameter in request body '" + ex.message() + "'");
processError(response, "Invalid 'columns' parameter in request body '" + ex.message() + "'");
LOG_WARNING(log, ex.getStackTraceString());
return;
}
std::string format = params.get("format", "RowBinary");
std::string query = params.get("query");
LOG_TRACE(log, "Query: " << query);
std::string connection_string = params.get("connection_string");
LOG_TRACE(log, "Connection string: '" << connection_string << "'");
WriteBufferFromHTTPServerResponse out(request, response, keep_alive_timeout);
try
{
BlockOutputStreamPtr writer = FormatFactory::instance().getOutput(format, out, *sample_block, context);
auto pool = getPool(connection_string);
ODBCBlockInputStream inp(pool->get(), query, *sample_block, max_block_size);
copyData(inp, *writer);
if (mode == "write")
{
if (!params.has("db_name"))
{
processError(response, "No 'db_name' in request URL");
return;
}
if (!params.has("table_name"))
{
processError(response, "No 'table_name' in request URL");
return;
}
std::string db_name = params.get("db_name");
std::string table_name = params.get("table_name");
LOG_TRACE(log, "DB name: '" << db_name << "', table name: '" << table_name << "'");
auto quoting_style = IdentifierQuotingStyle::None;
#if USE_ODBC
POCO_SQL_ODBC_CLASS::SessionImpl session(validateODBCConnectionString(connection_string), DBMS_DEFAULT_CONNECT_TIMEOUT_SEC);
quoting_style = getQuotingStyle(session.dbc().handle());
#endif
auto pool = getPool(connection_string);
ReadBufferFromIStream read_buf(request.stream());
BlockInputStreamPtr input_stream = FormatFactory::instance().getInput(format, read_buf, *sample_block,
context, max_block_size);
ODBCBlockOutputStream output_stream(pool->get(), db_name, table_name, *sample_block, quoting_style);
copyData(*input_stream, output_stream);
writeStringBinary("Ok.", out);
}
else
{
std::string query = params.get("query");
LOG_TRACE(log, "Query: " << query);
BlockOutputStreamPtr writer = FormatFactory::instance().getOutput(format, out, *sample_block, context);
auto pool = getPool(connection_string);
ODBCBlockInputStream inp(pool->get(), query, *sample_block, max_block_size);
copyData(inp, *writer);
}
}
catch (...)
{
auto message = getCurrentExceptionMessage(true);
response.setStatusAndReason(
Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); // can't call process_error, because of too soon response sending
Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); // can't call process_error, because of too soon response sending
writeStringBinary(message, out);
tryLogCurrentException(log);
}
}
}

View File

@ -24,11 +24,13 @@ public:
ODBCHandler(std::shared_ptr<PoolMap> pool_map_,
size_t keep_alive_timeout_,
Context & context_)
Context & context_,
const String & mode_)
: log(&Poco::Logger::get("ODBCHandler"))
, pool_map(pool_map_)
, keep_alive_timeout(keep_alive_timeout_)
, context(context_)
, mode(mode_)
{
}
@ -40,10 +42,12 @@ private:
std::shared_ptr<PoolMap> pool_map;
size_t keep_alive_timeout;
Context & context;
String mode;
static inline std::mutex mutex;
PoolPtr getPool(const std::string & connection_str);
void processError(Poco::Net::HTTPServerResponse & response, const std::string & message);
};
}

View File

@ -0,0 +1,133 @@
#include "ODBCBlockOutputStream.h"
#include <common/logger_useful.h>
#include <Core/Field.h>
#include <common/LocalDate.h>
#include <common/LocalDateTime.h>
#include <Parsers/ASTInsertQuery.h>
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ASTIdentifier.h>
#include "getIdentifierQuote.h"
namespace DB
{
namespace
{
using ValueType = ExternalResultDescription::ValueType;
std::string getInsertQuery(const std::string & db_name, const std::string & table_name, const ColumnsWithTypeAndName & columns, IdentifierQuotingStyle quoting)
{
ASTInsertQuery query;
query.table_id.database_name = db_name;
query.table_id.table_name = table_name;
query.columns = std::make_shared<ASTExpressionList>(',');
query.children.push_back(query.columns);
for (size_t i = 0; i < columns.size(); ++i)
query.columns->children.emplace_back(std::make_shared<ASTIdentifier>(columns[i].name));
std::stringstream ss;
IAST::FormatSettings settings(ss, true);
settings.always_quote_identifiers = true;
settings.identifier_quoting_style = quoting;
query.IAST::format(settings);
return ss.str();
}
std::string getQuestionMarks(size_t n)
{
std::string result = "(";
for (size_t i = 0; i < n; ++i)
{
if (i > 0)
result += ",";
result += "?";
}
return result + ")";
}
Poco::Dynamic::Var getVarFromField(const Field & field, const ValueType type)
{
switch (type)
{
case ValueType::vtUInt8:
return Poco::Dynamic::Var(static_cast<UInt64>(field.get<UInt64>())).convert<UInt64>();
case ValueType::vtUInt16:
return Poco::Dynamic::Var(static_cast<UInt64>(field.get<UInt64>())).convert<UInt64>();
case ValueType::vtUInt32:
return Poco::Dynamic::Var(static_cast<UInt64>(field.get<UInt64>())).convert<UInt64>();
case ValueType::vtUInt64:
return Poco::Dynamic::Var(field.get<UInt64>()).convert<UInt64>();
case ValueType::vtInt8:
return Poco::Dynamic::Var(static_cast<Int64>(field.get<Int64>())).convert<Int64>();
case ValueType::vtInt16:
return Poco::Dynamic::Var(static_cast<Int64>(field.get<Int64>())).convert<Int64>();
case ValueType::vtInt32:
return Poco::Dynamic::Var(static_cast<Int64>(field.get<Int64>())).convert<Int64>();
case ValueType::vtInt64:
return Poco::Dynamic::Var(field.get<Int64>()).convert<Int64>();
case ValueType::vtFloat32:
return Poco::Dynamic::Var(field.get<Float64>()).convert<Float64>();
case ValueType::vtFloat64:
return Poco::Dynamic::Var(field.get<Float64>()).convert<Float64>();
case ValueType::vtString:
return Poco::Dynamic::Var(field.get<String>()).convert<String>();
case ValueType::vtDate:
return Poco::Dynamic::Var(LocalDate(DayNum(field.get<UInt64>())).toString()).convert<String>();
case ValueType::vtDateTime:
return Poco::Dynamic::Var(std::to_string(LocalDateTime(time_t(field.get<UInt64>())))).convert<String>();
case ValueType::vtUUID:
return Poco::Dynamic::Var(UUID(field.get<UInt128>()).toUnderType().toHexString()).convert<std::string>();
}
__builtin_unreachable();
}
}
ODBCBlockOutputStream::ODBCBlockOutputStream(Poco::Data::Session && session_,
const std::string & remote_database_name_,
const std::string & remote_table_name_,
const Block & sample_block_,
IdentifierQuotingStyle quoting_)
: session(session_)
, db_name(remote_database_name_)
, table_name(remote_table_name_)
, sample_block(sample_block_)
, quoting(quoting_)
, log(&Logger::get("ODBCBlockOutputStream"))
{
description.init(sample_block);
}
Block ODBCBlockOutputStream::getHeader() const
{
return sample_block;
}
void ODBCBlockOutputStream::write(const Block & block)
{
ColumnsWithTypeAndName columns;
for (size_t i = 0; i < block.columns(); ++i)
columns.push_back({block.getColumns()[i], sample_block.getDataTypes()[i], sample_block.getNames()[i]});
std::vector<Poco::Dynamic::Var> row_to_insert(block.columns());
Poco::Data::Statement statement(session << getInsertQuery(db_name, table_name, columns, quoting) + getQuestionMarks(block.columns()));
for (size_t i = 0; i < block.columns(); ++i)
statement.addBind(Poco::Data::Keywords::use(row_to_insert[i]));
for (size_t i = 0; i < block.rows(); ++i)
{
for (size_t col_idx = 0; col_idx < block.columns(); ++col_idx)
{
Field val;
columns[col_idx].column->get(i, val);
if (val.isNull())
row_to_insert[col_idx] = Poco::Dynamic::Var();
else
row_to_insert[col_idx] = getVarFromField(val, description.types[col_idx].first);
}
statement.execute();
}
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <Core/Block.h>
#include <DataStreams/IBlockOutputStream.h>
#include <Poco/Data/Session.h>
#include <Core/ExternalResultDescription.h>
#include <Parsers/IdentifierQuotingStyle.h>
namespace DB
{
class ODBCBlockOutputStream : public IBlockOutputStream
{
public:
ODBCBlockOutputStream(Poco::Data::Session && session_, const std::string & remote_database_name_,
const std::string & remote_table_name_, const Block & sample_block_, IdentifierQuotingStyle quoting);
Block getHeader() const override;
void write(const Block & block) override;
private:
Poco::Data::Session session;
std::string db_name;
std::string table_name;
Block sample_block;
IdentifierQuotingStyle quoting;
ExternalResultDescription description;
Poco::Logger * log;
};
}

View File

@ -12,6 +12,11 @@
namespace DB
{
namespace ErrorCodes
{
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
}
std::string getIdentifierQuote(SQLHDBC hdbc)
{
std::string identifier;
@ -36,6 +41,19 @@ std::string getIdentifierQuote(SQLHDBC hdbc)
return identifier;
}
IdentifierQuotingStyle getQuotingStyle(SQLHDBC hdbc)
{
auto identifier_quote = getIdentifierQuote(hdbc);
if (identifier_quote.length() == 0)
return IdentifierQuotingStyle::None;
else if (identifier_quote[0] == '`')
return IdentifierQuotingStyle::Backticks;
else if (identifier_quote[0] == '"')
return IdentifierQuotingStyle::DoubleQuotes;
else
throw Exception("Can not map quote identifier '" + identifier_quote + "' to IdentifierQuotingStyle value", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
}
}
#endif

View File

@ -8,11 +8,15 @@
# include <Poco/Data/ODBC/Utility.h>
#include <Parsers/IdentifierQuotingStyle.h>
namespace DB
{
std::string getIdentifierQuote(SQLHDBC hdbc);
IdentifierQuotingStyle getQuotingStyle(SQLHDBC hdbc);
}
#endif

View File

@ -28,7 +28,7 @@
#include <Compression/CompressionFactory.h>
#include <common/logger_useful.h>
#include <Processors/Formats/LazyOutputFormat.h>
#include <Processors/Executors/PullingPipelineExecutor.h>
#include "TCPHandler.h"
@ -553,68 +553,23 @@ void TCPHandler::processOrdinaryQueryWithProcessors()
/// Send header-block, to allow client to prepare output format for data to send.
{
auto & header = pipeline.getHeader();
const auto & header = pipeline.getHeader();
if (header)
sendData(header);
}
auto lazy_format = std::make_shared<LazyOutputFormat>(pipeline.getHeader());
pipeline.setOutput(lazy_format);
{
auto thread_group = CurrentThread::getGroup();
ThreadPool pool(1);
auto executor = pipeline.execute();
std::atomic_bool exception = false;
PullingPipelineExecutor executor(pipeline);
CurrentMetrics::Increment query_thread_metric_increment{CurrentMetrics::QueryThread};
pool.scheduleOrThrowOnError([&]()
{
/// ThreadStatus thread_status;
if (thread_group)
CurrentThread::attachTo(thread_group);
SCOPE_EXIT(
if (thread_group)
CurrentThread::detachQueryIfNotDetached();
);
CurrentMetrics::Increment query_thread_metric_increment{CurrentMetrics::QueryThread};
setThreadName("QueryPipelineEx");
try
{
executor->execute(pipeline.getNumThreads());
}
catch (...)
{
exception = true;
throw;
}
});
/// Wait in case of exception happened outside of pool.
SCOPE_EXIT(
lazy_format->finish();
try
{
pool.wait();
}
catch (...)
{
/// If exception was thrown during pipeline execution, skip it while processing other exception.
tryLogCurrentException(log);
}
);
while (!lazy_format->isFinished() && !exception)
Block block;
while (executor.pull(block, query_context->getSettingsRef().interactive_delay / 1000))
{
if (isQueryCancelled())
{
/// A packet was received requesting to stop execution of the request.
executor->cancel();
executor.cancel();
break;
}
@ -627,17 +582,13 @@ void TCPHandler::processOrdinaryQueryWithProcessors()
sendLogs();
if (auto block = lazy_format->getBlock(query_context->getSettingsRef().interactive_delay / 1000))
if (block)
{
if (!state.io.null_format)
sendData(block);
}
}
/// Finish lazy_format before waiting. Otherwise some thread may write into it, and waiting will lock.
lazy_format->finish();
pool.wait();
/** If data has run out, we will send the profiling data and total values to
* the last zero block to be able to use
* this information in the suffix output of stream.
@ -647,9 +598,9 @@ void TCPHandler::processOrdinaryQueryWithProcessors()
*/
if (!isQueryCancelled())
{
sendTotals(lazy_format->getTotals());
sendExtremes(lazy_format->getExtremes());
sendProfileInfo(lazy_format->getProfileInfo());
sendTotals(executor.getTotalsBlock());
sendExtremes(executor.getExtremesBlock());
sendProfileInfo(executor.getProfileInfo());
sendProgress();
sendLogs();
}

View File

@ -7,7 +7,7 @@
#include <Access/RoleCache.h>
#include <Access/RowPolicyCache.h>
#include <Access/QuotaCache.h>
#include <Access/QuotaUsageInfo.h>
#include <Access/QuotaUsage.h>
#include <Access/SettingsProfilesCache.h>
#include <Core/Settings.h>
#include <Poco/ExpireCache.h>
@ -42,7 +42,7 @@ public:
std::shared_ptr<const ContextAccess> getContextAccess(
const UUID & user_id,
const std::vector<UUID> & current_roles,
const boost::container::flat_set<UUID> & current_roles,
bool use_default_roles,
const Settings & settings,
const String & current_database,
@ -113,7 +113,7 @@ void AccessControlManager::setDefaultProfileName(const String & default_profile_
std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(
const UUID & user_id,
const std::vector<UUID> & current_roles,
const boost::container::flat_set<UUID> & current_roles,
bool use_default_roles,
const Settings & settings,
const String & current_database,
@ -124,36 +124,36 @@ std::shared_ptr<const ContextAccess> AccessControlManager::getContextAccess(
std::shared_ptr<const EnabledRoles> AccessControlManager::getEnabledRoles(
const std::vector<UUID> & current_roles,
const std::vector<UUID> & current_roles_with_admin_option) const
const boost::container::flat_set<UUID> & current_roles,
const boost::container::flat_set<UUID> & current_roles_with_admin_option) const
{
return role_cache->getEnabledRoles(current_roles, current_roles_with_admin_option);
}
std::shared_ptr<const EnabledRowPolicies> AccessControlManager::getEnabledRowPolicies(const UUID & user_id, const std::vector<UUID> & enabled_roles) const
std::shared_ptr<const EnabledRowPolicies> AccessControlManager::getEnabledRowPolicies(const UUID & user_id, const boost::container::flat_set<UUID> & enabled_roles) const
{
return row_policy_cache->getEnabledRowPolicies(user_id, enabled_roles);
}
std::shared_ptr<const EnabledQuota> AccessControlManager::getEnabledQuota(
const UUID & user_id, const String & user_name, const std::vector<UUID> & enabled_roles, const Poco::Net::IPAddress & address, const String & custom_quota_key) const
const UUID & user_id, const String & user_name, const boost::container::flat_set<UUID> & enabled_roles, const Poco::Net::IPAddress & address, const String & custom_quota_key) const
{
return quota_cache->getEnabledQuota(user_id, user_name, enabled_roles, address, custom_quota_key);
}
std::vector<QuotaUsageInfo> AccessControlManager::getQuotaUsageInfo() const
std::vector<QuotaUsage> AccessControlManager::getAllQuotasUsage() const
{
return quota_cache->getUsageInfo();
return quota_cache->getAllQuotasUsage();
}
std::shared_ptr<const EnabledSettings> AccessControlManager::getEnabledSettings(
const UUID & user_id,
const SettingsProfileElements & settings_from_user,
const std::vector<UUID> & enabled_roles,
const boost::container::flat_set<UUID> & enabled_roles,
const SettingsProfileElements & settings_from_enabled_roles) const
{
return settings_profiles_cache->getEnabledSettings(user_id, settings_from_user, enabled_roles, settings_from_enabled_roles);

View File

@ -2,6 +2,7 @@
#include <Access/MultipleAccessStorage.h>
#include <Poco/AutoPtr.h>
#include <boost/container/flat_set.hpp>
#include <memory>
@ -28,7 +29,7 @@ class EnabledRowPolicies;
class RowPolicyCache;
class EnabledQuota;
class QuotaCache;
struct QuotaUsageInfo;
struct QuotaUsage;
struct SettingsProfile;
using SettingsProfilePtr = std::shared_ptr<const SettingsProfile>;
class EnabledSettings;
@ -51,32 +52,32 @@ public:
std::shared_ptr<const ContextAccess> getContextAccess(
const UUID & user_id,
const std::vector<UUID> & current_roles,
const boost::container::flat_set<UUID> & current_roles,
bool use_default_roles,
const Settings & settings,
const String & current_database,
const ClientInfo & client_info) const;
std::shared_ptr<const EnabledRoles> getEnabledRoles(
const std::vector<UUID> & current_roles,
const std::vector<UUID> & current_roles_with_admin_option) const;
const boost::container::flat_set<UUID> & current_roles,
const boost::container::flat_set<UUID> & current_roles_with_admin_option) const;
std::shared_ptr<const EnabledRowPolicies> getEnabledRowPolicies(
const UUID & user_id,
const std::vector<UUID> & enabled_roles) const;
const boost::container::flat_set<UUID> & enabled_roles) const;
std::shared_ptr<const EnabledQuota> getEnabledQuota(
const UUID & user_id,
const String & user_name,
const std::vector<UUID> & enabled_roles,
const boost::container::flat_set<UUID> & enabled_roles,
const Poco::Net::IPAddress & address,
const String & custom_quota_key) const;
std::vector<QuotaUsageInfo> getQuotaUsageInfo() const;
std::vector<QuotaUsage> getAllQuotasUsage() const;
std::shared_ptr<const EnabledSettings> getEnabledSettings(const UUID & user_id,
const SettingsProfileElements & settings_from_user,
const std::vector<UUID> & enabled_roles,
const boost::container::flat_set<UUID> & enabled_roles,
const SettingsProfileElements & settings_from_enabled_roles) const;
std::shared_ptr<const SettingsChanges> getProfileSettings(const String & profile_name) const;

View File

@ -61,12 +61,19 @@ public:
friend bool operator ==(const AccessFlags & left, const AccessFlags & right) { return left.flags == right.flags; }
friend bool operator !=(const AccessFlags & left, const AccessFlags & right) { return !(left == right); }
friend bool operator <(const AccessFlags & left, const AccessFlags & right) { return memcmp(&left.flags, &right.flags, sizeof(Flags)) < 0; }
friend bool operator >(const AccessFlags & left, const AccessFlags & right) { return right < left; }
friend bool operator <=(const AccessFlags & left, const AccessFlags & right) { return !(right < left); }
friend bool operator >=(const AccessFlags & left, const AccessFlags & right) { return !(left < right); }
void clear() { flags.reset(); }
/// Returns a comma-separated list of keywords, like "SELECT, CREATE USER, UPDATE".
String toString() const;
/// Returns a list of access types.
std::vector<AccessType> toAccessTypes() const;
/// Returns a list of keywords.
std::vector<std::string_view> toKeywords() const;
@ -151,21 +158,27 @@ public:
return res;
}
std::vector<AccessType> flagsToAccessTypes(const Flags & flags_) const
{
std::vector<AccessType> access_types;
flagsToAccessTypesRec(flags_, access_types, *all_node);
return access_types;
}
std::vector<std::string_view> flagsToKeywords(const Flags & flags_) const
{
std::vector<std::string_view> keywords;
flagsToKeywordsRec(flags_, keywords, *flags_to_keyword_tree);
if (keywords.empty())
keywords.push_back("USAGE");
flagsToKeywordsRec(flags_, keywords, *all_node);
return keywords;
}
String flagsToString(const Flags & flags_) const
{
auto keywords = flagsToKeywords(flags_);
if (keywords.empty())
return "USAGE";
String str;
for (const auto & keyword : flagsToKeywords(flags_))
for (const auto & keyword : keywords)
{
if (!str.empty())
str += ", ";
@ -201,7 +214,7 @@ private:
{
const String keyword;
NodeType node_type;
AccessType type = AccessType::NONE;
AccessType access_type = AccessType::NONE;
Strings aliases;
Flags flags;
std::vector<NodePtr> children;
@ -233,8 +246,8 @@ private:
return aliases;
}
static void makeFlagsToKeywordTreeNode(
AccessType type,
static void makeNode(
AccessType access_type,
const std::string_view & name,
const std::string_view & aliases,
NodeType node_type,
@ -259,13 +272,13 @@ private:
nodes[node->keyword] = node.get();
}
node->type = type;
node->access_type = access_type;
node->node_type = node_type;
node->aliases = splitAliases(aliases);
if (node_type != GROUP)
node->setFlag(next_flag++);
bool has_parent_group = (parent_group_name != "NONE");
bool has_parent_group = (parent_group_name != std::string_view{"NONE"});
if (!has_parent_group)
{
std::string_view keyword_as_string_view = node->keyword;
@ -286,25 +299,25 @@ private:
it_parent->second->addChild(std::move(node));
}
void makeFlagsToKeywordTree()
void makeNodes()
{
std::unordered_map<std::string_view, NodePtr> owned_nodes;
std::unordered_map<std::string_view, Node *> nodes;
size_t next_flag = 0;
#define MAKE_ACCESS_FLAGS_TO_KEYWORD_TREE_NODE(name, aliases, node_type, parent_group_name) \
makeFlagsToKeywordTreeNode(AccessType::name, #name, aliases, node_type, #parent_group_name, nodes, owned_nodes, next_flag);
#define MAKE_ACCESS_FLAGS_NODE(name, aliases, node_type, parent_group_name) \
makeNode(AccessType::name, #name, aliases, node_type, #parent_group_name, nodes, owned_nodes, next_flag);
APPLY_FOR_ACCESS_TYPES(MAKE_ACCESS_FLAGS_TO_KEYWORD_TREE_NODE)
APPLY_FOR_ACCESS_TYPES(MAKE_ACCESS_FLAGS_NODE)
#undef MAKE_ACCESS_FLAGS_TO_KEYWORD_TREE_NODE
#undef MAKE_ACCESS_FLAGS_NODE
if (!owned_nodes.count("NONE"))
throw Exception("'NONE' not declared", ErrorCodes::LOGICAL_ERROR);
if (!owned_nodes.count("ALL"))
throw Exception("'ALL' not declared", ErrorCodes::LOGICAL_ERROR);
flags_to_keyword_tree = std::move(owned_nodes["ALL"]);
all_node = std::move(owned_nodes["ALL"]);
none_node = std::move(owned_nodes["NONE"]);
owned_nodes.erase("ALL");
owned_nodes.erase("NONE");
@ -324,7 +337,7 @@ private:
if (!start_node)
{
makeKeywordToFlagsMap(none_node.get());
start_node = flags_to_keyword_tree.get();
start_node = all_node.get();
}
start_node->aliases.emplace_back(start_node->keyword);
@ -343,10 +356,10 @@ private:
if (!start_node)
{
makeAccessTypeToFlagsMapping(none_node.get());
start_node = flags_to_keyword_tree.get();
start_node = all_node.get();
}
size_t index = static_cast<size_t>(start_node->type);
size_t index = static_cast<size_t>(start_node->access_type);
access_type_to_flags_mapping.resize(std::max(index + 1, access_type_to_flags_mapping.size()));
access_type_to_flags_mapping[index] = start_node->flags;
@ -358,7 +371,7 @@ private:
{
if (!start_node)
{
start_node = flags_to_keyword_tree.get();
start_node = all_node.get();
all_flags = start_node->flags;
}
if (start_node->node_type != GROUP)
@ -372,12 +385,29 @@ private:
Impl()
{
makeFlagsToKeywordTree();
makeNodes();
makeKeywordToFlagsMap();
makeAccessTypeToFlagsMapping();
collectAllFlags();
}
static void flagsToAccessTypesRec(const Flags & flags_, std::vector<AccessType> & access_types, const Node & start_node)
{
Flags matching_flags = (flags_ & start_node.flags);
if (matching_flags.any())
{
if (matching_flags == start_node.flags)
{
access_types.push_back(start_node.access_type);
}
else
{
for (const auto & child : start_node.children)
flagsToAccessTypesRec(flags_, access_types, *child);
}
}
}
static void flagsToKeywordsRec(const Flags & flags_, std::vector<std::string_view> & keywords, const Node & start_node)
{
Flags matching_flags = (flags_ & start_node.flags);
@ -395,7 +425,7 @@ private:
}
}
NodePtr flags_to_keyword_tree;
NodePtr all_node;
NodePtr none_node;
std::unordered_map<std::string_view, Flags> keyword_to_flags_map;
std::vector<Flags> access_type_to_flags_mapping;
@ -409,6 +439,7 @@ inline AccessFlags::AccessFlags(const std::string_view & keyword) : flags(Impl<>
inline AccessFlags::AccessFlags(const std::vector<std::string_view> & keywords) : flags(Impl<>::instance().keywordsToFlags(keywords)) {}
inline AccessFlags::AccessFlags(const Strings & keywords) : flags(Impl<>::instance().keywordsToFlags(keywords)) {}
inline String AccessFlags::toString() const { return Impl<>::instance().flagsToString(flags); }
inline std::vector<AccessType> AccessFlags::toAccessTypes() const { return Impl<>::instance().flagsToAccessTypes(flags); }
inline std::vector<std::string_view> AccessFlags::toKeywords() const { return Impl<>::instance().flagsToKeywords(flags); }
inline AccessFlags AccessFlags::allFlags() { return Impl<>::instance().getAllFlags(); }
inline AccessFlags AccessFlags::allGlobalFlags() { return Impl<>::instance().getGlobalFlags(); }

View File

@ -606,69 +606,102 @@ void AccessRights::revoke(const AccessRightsElements & elements, std::string_vie
}
AccessRights::Elements AccessRights::getElements() const
AccessRightsElements AccessRights::getGrants() const
{
AccessRightsElements grants;
getGrantsAndPartialRevokesImpl(&grants, nullptr);
return grants;
}
AccessRightsElements AccessRights::getPartialRevokes() const
{
AccessRightsElements partial_revokes;
getGrantsAndPartialRevokesImpl(nullptr, &partial_revokes);
return partial_revokes;
}
AccessRights::GrantsAndPartialRevokes AccessRights::getGrantsAndPartialRevokes() const
{
GrantsAndPartialRevokes res;
getGrantsAndPartialRevokesImpl(&res.grants, &res.revokes);
return res;
}
void AccessRights::getGrantsAndPartialRevokesImpl(AccessRightsElements * out_grants, AccessRightsElements * out_partial_revokes) const
{
if (!root)
return {};
Elements res;
return;
auto global_access = root->access;
if (global_access)
res.grants.push_back({global_access});
if (out_grants && global_access)
out_grants->push_back({global_access});
if (root->children)
{
for (const auto & [db_name, db_node] : *root->children)
{
auto db_grants = db_node.access - global_access;
auto db_partial_revokes = global_access - db_node.access;
if (db_partial_revokes)
res.partial_revokes.push_back({db_partial_revokes, db_name});
if (db_grants)
res.grants.push_back({db_grants, db_name});
if (out_grants)
{
if (auto db_grants = db_node.access - global_access)
out_grants->push_back({db_grants, db_name});
}
if (out_partial_revokes)
{
if (auto db_partial_revokes = global_access - db_node.access)
out_partial_revokes->push_back({db_partial_revokes, db_name});
}
if (db_node.children)
{
for (const auto & [table_name, table_node] : *db_node.children)
{
auto table_grants = table_node.access - db_node.access;
auto table_partial_revokes = db_node.access - table_node.access;
if (table_partial_revokes)
res.partial_revokes.push_back({table_partial_revokes, db_name, table_name});
if (table_grants)
res.grants.push_back({table_grants, db_name, table_name});
if (out_grants)
{
if (auto table_grants = table_node.access - db_node.access)
out_grants->push_back({table_grants, db_name, table_name});
}
if (out_partial_revokes)
{
if (auto table_partial_revokes = db_node.access - table_node.access)
out_partial_revokes->push_back({table_partial_revokes, db_name, table_name});
}
if (table_node.children)
{
for (const auto & [column_name, column_node] : *table_node.children)
{
auto column_grants = column_node.access - table_node.access;
auto column_partial_revokes = table_node.access - column_node.access;
if (column_partial_revokes)
res.partial_revokes.push_back({column_partial_revokes, db_name, table_name, column_name});
if (column_grants)
res.grants.push_back({column_grants, db_name, table_name, column_name});
if (out_grants)
{
if (auto column_grants = column_node.access - table_node.access)
out_grants->push_back({column_grants, db_name, table_name, column_name});
}
if (out_partial_revokes)
{
if (auto column_partial_revokes = table_node.access - column_node.access)
out_partial_revokes->push_back({column_partial_revokes, db_name, table_name, column_name});
}
}
}
}
}
}
}
return res;
}
String AccessRights::toString() const
{
auto elements = getElements();
String res;
if (!elements.grants.empty())
auto gr = getGrantsAndPartialRevokes();
if (!gr.grants.empty())
{
res += "GRANT ";
res += elements.grants.toString();
res += gr.grants.toString();
}
if (!elements.partial_revokes.empty())
if (!gr.revokes.empty())
{
if (!res.empty())
res += ", ";
res += "REVOKE ";
res += elements.partial_revokes.toString();
res += gr.revokes.toString();
}
if (res.empty())
res = "GRANT USAGE ON *.*";

View File

@ -49,12 +49,14 @@ public:
void revoke(const AccessRightsElements & elements, std::string_view current_database = {});
/// Returns the information about all the access granted.
struct Elements
struct GrantsAndPartialRevokes
{
AccessRightsElements grants;
AccessRightsElements partial_revokes;
AccessRightsElements revokes;
};
Elements getElements() const;
AccessRightsElements getGrants() const;
AccessRightsElements getPartialRevokes() const;
GrantsAndPartialRevokes getGrantsAndPartialRevokes() const;
/// Returns the information about all the access granted as a string.
String toString() const;
@ -92,6 +94,8 @@ private:
template <typename... Args>
AccessFlags getAccessImpl(const Args &... args) const;
void getGrantsAndPartialRevokesImpl(AccessRightsElements * grants, AccessRightsElements * partial_revokes) const;
void logTree() const;
struct Node;

View File

@ -1,10 +1,124 @@
#include <Access/AccessRightsElement.h>
#include <Dictionaries/IDictionary.h>
#include <Common/quoteString.h>
#include <boost/range/algorithm/sort.hpp>
#include <boost/range/algorithm/unique.hpp>
#include <boost/range/algorithm_ext/is_sorted.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
namespace DB
{
namespace
{
size_t groupElements(AccessRightsElements & elements, size_t start)
{
auto & start_element = elements[start];
auto it = std::find_if(elements.begin() + start + 1, elements.end(),
[&](const AccessRightsElement & element)
{
return (element.database != start_element.database) ||
(element.any_database != start_element.any_database) ||
(element.table != start_element.table) ||
(element.any_table != start_element.any_table) ||
(element.any_column != start_element.any_column);
});
size_t end = it - elements.begin();
/// All the elements at indices from start to end here specify
/// the same database and table.
if (start_element.any_column)
{
/// Easy case: the elements don't specify columns.
/// All we need is to combine the access flags.
for (size_t i = start + 1; i != end; ++i)
{
start_element.access_flags |= elements[i].access_flags;
elements[i].access_flags = {};
}
return end;
}
/// Difficult case: the elements specify columns.
/// We have to find groups of columns with common access flags.
for (size_t i = start; i != end; ++i)
{
if (!elements[i].access_flags)
continue;
AccessFlags common_flags = elements[i].access_flags;
size_t num_elements_with_common_flags = 1;
for (size_t j = i + 1; j != end; ++j)
{
auto new_common_flags = common_flags & elements[j].access_flags;
if (new_common_flags)
{
common_flags = new_common_flags;
++num_elements_with_common_flags;
}
}
if (num_elements_with_common_flags == 1)
continue;
if (elements[i].access_flags != common_flags)
{
elements.insert(elements.begin() + i + 1, elements[i]);
elements[i].access_flags = common_flags;
elements[i].columns.clear();
++end;
}
for (size_t j = i + 1; j != end; ++j)
{
if ((elements[j].access_flags & common_flags) == common_flags)
{
boost::range::push_back(elements[i].columns, elements[j].columns);
elements[j].access_flags -= common_flags;
}
}
}
return end;
}
/// Tries to combine elements to decrease their number.
void groupElements(AccessRightsElements & elements)
{
if (!boost::range::is_sorted(elements))
boost::range::sort(elements); /// Algorithm in groupElement() requires elements to be sorted.
for (size_t start = 0; start != elements.size();)
start = groupElements(elements, start);
}
/// Removes unnecessary elements, sorts elements and makes them unique.
void sortElementsAndMakeUnique(AccessRightsElements & elements)
{
/// Remove empty elements.
boost::range::remove_erase_if(elements, [](const AccessRightsElement & element)
{
return !element.access_flags || (!element.any_column && element.columns.empty());
});
/// Sort columns and make them unique.
for (auto & element : elements)
{
if (element.any_column)
continue;
if (!boost::range::is_sorted(element.columns))
boost::range::sort(element.columns);
element.columns.erase(std::unique(element.columns.begin(), element.columns.end()), element.columns.end());
}
/// Sort elements themselves.
boost::range::sort(elements);
elements.erase(std::unique(elements.begin(), elements.end()), elements.end());
}
}
void AccessRightsElement::setDatabase(const String & new_database)
{
database = new_database;
@ -26,10 +140,29 @@ bool AccessRightsElement::isEmptyDatabase() const
String AccessRightsElement::toString() const
{
String msg = toStringWithoutON();
msg += " ON ";
if (any_database)
msg += "*.";
else if (!database.empty())
msg += backQuoteIfNeed(database) + ".";
if (any_table)
msg += "*";
else
msg += backQuoteIfNeed(table);
return msg;
}
String AccessRightsElement::toStringWithoutON() const
{
String columns_in_parentheses;
if (!any_column)
{
if (columns.empty())
return "USAGE";
for (const auto & column : columns)
{
columns_in_parentheses += columns_in_parentheses.empty() ? "(" : ", ";
@ -38,25 +171,17 @@ String AccessRightsElement::toString() const
columns_in_parentheses += ")";
}
auto keywords = access_flags.toKeywords();
if (keywords.empty())
return "USAGE";
String msg;
for (const std::string_view & keyword : access_flags.toKeywords())
for (const std::string_view & keyword : keywords)
{
if (!msg.empty())
msg += ", ";
msg += String{keyword} + columns_in_parentheses;
}
msg += " ON ";
if (any_database)
msg += "*.";
else if (!database.empty() && (database != IDictionary::NO_DATABASE_TAG))
msg += backQuoteIfNeed(database) + ".";
if (any_table)
msg += "*";
else
msg += backQuoteIfNeed(table);
return msg;
}
@ -68,19 +193,41 @@ void AccessRightsElements::replaceEmptyDatabase(const String & new_database)
}
String AccessRightsElements::toString() const
String AccessRightsElements::toString()
{
String res;
bool need_comma = false;
for (const auto & element : *this)
{
if (std::exchange(need_comma, true))
res += ", ";
res += element.toString();
}
normalize();
if (res.empty())
res = "USAGE ON *.*";
return res;
if (empty())
return "USAGE ON *.*";
String msg;
bool need_comma = false;
for (size_t i = 0; i != size(); ++i)
{
const auto & element = (*this)[i];
if (std::exchange(need_comma, true))
msg += ", ";
bool next_element_on_same_db_and_table = false;
if (i != size() - 1)
{
const auto & next_element = (*this)[i + 1];
if ((element.database == next_element.database) && (element.any_database == next_element.any_database)
&& (element.table == next_element.table) && (element.any_table == next_element.any_table))
next_element_on_same_db_and_table = true;
}
if (next_element_on_same_db_and_table)
msg += element.toStringWithoutON();
else
msg += element.toString();
}
return msg;
}
void AccessRightsElements::normalize()
{
groupElements(*this);
sortElementsAndMakeUnique(*this);
}
}

View File

@ -71,6 +71,14 @@ struct AccessRightsElement
{
}
auto toTuple() const { return std::tie(access_flags, database, any_database, table, any_table, columns, any_column); }
friend bool operator==(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() == right.toTuple(); }
friend bool operator!=(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() != right.toTuple(); }
friend bool operator<(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() < right.toTuple(); }
friend bool operator>(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() > right.toTuple(); }
friend bool operator<=(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() <= right.toTuple(); }
friend bool operator>=(const AccessRightsElement & left, const AccessRightsElement & right) { return left.toTuple() >= right.toTuple(); }
/// Sets the database.
void setDatabase(const String & new_database);
@ -82,6 +90,7 @@ struct AccessRightsElement
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
/// The returned string isn't prefixed with the "GRANT" keyword.
String toString() const;
String toStringWithoutON() const;
};
@ -94,7 +103,11 @@ public:
/// Returns a human-readable representation like "SELECT, UPDATE(x, y) ON db.table".
/// The returned string isn't prefixed with the "GRANT" keyword.
String toString() const;
String toString() const { return AccessRightsElements(*this).toString(); }
String toString();
/// Reorder and group elements to show them in more readable form.
void normalize();
};
}

View File

@ -167,50 +167,48 @@ enum class AccessType
#undef DECLARE_ACCESS_TYPE_ENUM_CONST
};
std::string_view toString(AccessType type);
namespace impl
{
template <typename = void>
class AccessTypeToKeywordConverter
class AccessTypeToStringConverter
{
public:
static const AccessTypeToKeywordConverter & instance()
static const AccessTypeToStringConverter & instance()
{
static const AccessTypeToKeywordConverter res;
static const AccessTypeToStringConverter res;
return res;
}
std::string_view convert(AccessType type) const
{
return access_type_to_keyword_mapping[static_cast<size_t>(type)];
return access_type_to_string_mapping[static_cast<size_t>(type)];
}
private:
AccessTypeToKeywordConverter()
AccessTypeToStringConverter()
{
#define INSERT_ACCESS_TYPE_KEYWORD_PAIR_TO_MAPPING(name, aliases, node_type, parent_group_name) \
insertToMapping(AccessType::name, #name);
#define ACCESS_TYPE_TO_STRING_CONVERTER_ADD_TO_MAPPING(name, aliases, node_type, parent_group_name) \
addToMapping(AccessType::name, #name);
APPLY_FOR_ACCESS_TYPES(INSERT_ACCESS_TYPE_KEYWORD_PAIR_TO_MAPPING)
APPLY_FOR_ACCESS_TYPES(ACCESS_TYPE_TO_STRING_CONVERTER_ADD_TO_MAPPING)
#undef INSERT_ACCESS_TYPE_KEYWORD_PAIR_TO_MAPPING
#undef ACCESS_TYPE_TO_STRING_CONVERTER_ADD_TO_MAPPING
}
void insertToMapping(AccessType type, const std::string_view & str)
void addToMapping(AccessType type, const std::string_view & str)
{
String str2{str};
boost::replace_all(str2, "_", " ");
size_t index = static_cast<size_t>(type);
access_type_to_keyword_mapping.resize(std::max(index + 1, access_type_to_keyword_mapping.size()));
access_type_to_keyword_mapping[index] = str2;
access_type_to_string_mapping.resize(std::max(index + 1, access_type_to_string_mapping.size()));
access_type_to_string_mapping[index] = str2;
}
Strings access_type_to_keyword_mapping;
Strings access_type_to_string_mapping;
};
}
inline std::string_view toKeyword(AccessType type) { return impl::AccessTypeToKeywordConverter<>::instance().convert(type); }
inline std::string_view toString(AccessType type) { return impl::AccessTypeToStringConverter<>::instance().convert(type); }
}

View File

@ -3,6 +3,7 @@
#include <Access/EnabledRoles.h>
#include <Access/EnabledRowPolicies.h>
#include <Access/EnabledQuota.h>
#include <Access/QuotaUsage.h>
#include <Access/User.h>
#include <Access/EnabledRolesInfo.h>
#include <Access/EnabledSettings.h>
@ -121,7 +122,6 @@ void ContextAccess::setUser(const UserPtr & user_) const
subscription_for_roles_changes = {};
enabled_roles = nullptr;
roles_info = nullptr;
roles_with_admin_option = nullptr;
enabled_row_policies = nullptr;
enabled_quota = nullptr;
enabled_settings = nullptr;
@ -131,29 +131,28 @@ void ContextAccess::setUser(const UserPtr & user_) const
user_name = user->getName();
trace_log = &Poco::Logger::get("ContextAccess (" + user_name + ")");
std::vector<UUID> current_roles, current_roles_with_admin_option;
boost::container::flat_set<UUID> current_roles, current_roles_with_admin_option;
if (params.use_default_roles)
{
for (const UUID & id : user->granted_roles)
for (const UUID & id : user->granted_roles.roles)
{
if (user->default_roles.match(id))
current_roles.push_back(id);
current_roles.emplace(id);
}
boost::range::set_intersection(current_roles, user->granted_roles_with_admin_option,
std::back_inserter(current_roles_with_admin_option));
}
else
{
current_roles.reserve(params.current_roles.size());
for (const auto & id : params.current_roles)
{
if (user->granted_roles.count(id))
current_roles.push_back(id);
if (user->granted_roles_with_admin_option.count(id))
current_roles_with_admin_option.push_back(id);
}
boost::range::set_intersection(
params.current_roles,
user->granted_roles.roles,
std::inserter(current_roles, current_roles.end()));
}
boost::range::set_intersection(
current_roles,
user->granted_roles.roles_with_admin_option,
std::inserter(current_roles_with_admin_option, current_roles_with_admin_option.end()));
subscription_for_roles_changes = {};
enabled_roles = manager->getEnabledRoles(current_roles, current_roles_with_admin_option);
subscription_for_roles_changes = enabled_roles->subscribeForChanges([this](const std::shared_ptr<const EnabledRolesInfo> & roles_info_)
@ -170,7 +169,6 @@ void ContextAccess::setRolesInfo(const std::shared_ptr<const EnabledRolesInfo> &
{
assert(roles_info_);
roles_info = roles_info_;
roles_with_admin_option.store(boost::make_shared<boost::container::flat_set<UUID>>(roles_info->enabled_roles_with_admin_option.begin(), roles_info->enabled_roles_with_admin_option.end()));
boost::range::fill(result_access, nullptr /* need recalculate */);
enabled_row_policies = manager->getEnabledRowPolicies(*params.user_id, roles_info->enabled_roles);
enabled_quota = manager->getEnabledQuota(*params.user_id, user_name, roles_info->enabled_roles, params.address, params.quota_key);
@ -357,10 +355,13 @@ void ContextAccess::checkAdminOption(const UUID & role_id) const
if (isGranted(AccessType::ROLE_ADMIN))
return;
auto roles_with_admin_option_loaded = roles_with_admin_option.load();
if (roles_with_admin_option_loaded && roles_with_admin_option_loaded->count(role_id))
auto info = getRolesInfo();
if (info && info->enabled_roles_with_admin_option.count(role_id))
return;
if (!user)
throw Exception(user_name + ": User has been dropped", ErrorCodes::UNKNOWN_USER);
std::optional<String> role_name = manager->readName(role_id);
if (!role_name)
role_name = "ID {" + toString(role_id) + "}";
@ -397,13 +398,13 @@ boost::shared_ptr<const AccessRights> ContextAccess::calculateResultAccess(bool
if (grant_option)
{
*merged_access = user->access_with_grant_option;
*merged_access = user->access.access_with_grant_option;
if (roles_info)
merged_access->merge(roles_info->access_with_grant_option);
}
else
{
*merged_access = user->access;
*merged_access = user->access.access;
if (roles_info)
merged_access->merge(roles_info->access);
}
@ -485,31 +486,7 @@ std::shared_ptr<const EnabledRolesInfo> ContextAccess::getRolesInfo() const
return roles_info;
}
std::vector<UUID> ContextAccess::getCurrentRoles() const
{
std::lock_guard lock{mutex};
return roles_info ? roles_info->current_roles : std::vector<UUID>{};
}
Strings ContextAccess::getCurrentRolesNames() const
{
std::lock_guard lock{mutex};
return roles_info ? roles_info->getCurrentRolesNames() : Strings{};
}
std::vector<UUID> ContextAccess::getEnabledRoles() const
{
std::lock_guard lock{mutex};
return roles_info ? roles_info->enabled_roles : std::vector<UUID>{};
}
Strings ContextAccess::getEnabledRolesNames() const
{
std::lock_guard lock{mutex};
return roles_info ? roles_info->getEnabledRolesNames() : Strings{};
}
std::shared_ptr<const EnabledRowPolicies> ContextAccess::getRowPolicies() const
std::shared_ptr<const EnabledRowPolicies> ContextAccess::getEnabledRowPolicies() const
{
std::lock_guard lock{mutex};
return enabled_row_policies;
@ -528,6 +505,13 @@ std::shared_ptr<const EnabledQuota> ContextAccess::getQuota() const
}
std::optional<QuotaUsage> ContextAccess::getQuotaUsage() const
{
std::lock_guard lock{mutex};
return enabled_quota ? enabled_quota->getUsage() : std::optional<QuotaUsage>{};
}
std::shared_ptr<const ContextAccess> ContextAccess::getFullAccess()
{
static const std::shared_ptr<const ContextAccess> res = []

View File

@ -22,6 +22,7 @@ class EnabledRoles;
class EnabledRowPolicies;
class EnabledQuota;
class EnabledSettings;
struct QuotaUsage;
struct Settings;
class SettingsConstraints;
class AccessControlManager;
@ -35,7 +36,7 @@ public:
struct Params
{
std::optional<UUID> user_id;
std::vector<UUID> current_roles;
boost::container::flat_set<UUID> current_roles;
bool use_default_roles = false;
UInt64 readonly = 0;
bool allow_ddl = false;
@ -56,22 +57,37 @@ public:
};
const Params & getParams() const { return params; }
/// Returns the current user. The function can return nullptr.
UserPtr getUser() const;
String getUserName() const;
bool isCorrectPassword(const String & password) const;
bool isClientHostAllowed() const;
/// Returns information about current and enabled roles.
/// The function can return nullptr.
std::shared_ptr<const EnabledRolesInfo> getRolesInfo() const;
std::vector<UUID> getCurrentRoles() const;
Strings getCurrentRolesNames() const;
std::vector<UUID> getEnabledRoles() const;
Strings getEnabledRolesNames() const;
std::shared_ptr<const EnabledRowPolicies> getRowPolicies() const;
/// Returns information about enabled row policies.
/// The function can return nullptr.
std::shared_ptr<const EnabledRowPolicies> getEnabledRowPolicies() const;
/// Returns the row policy filter for a specified table.
/// The function returns nullptr if there is no filter to apply.
ASTPtr getRowPolicyCondition(const String & database, const String & table_name, RowPolicy::ConditionType index, const ASTPtr & extra_condition = nullptr) const;
/// Returns the quota to track resource consumption.
/// The function returns nullptr if no tracking or limitation is needed.
std::shared_ptr<const EnabledQuota> getQuota() const;
std::optional<QuotaUsage> getQuotaUsage() const;
/// Returns the default settings, i.e. the settings to apply on user's login.
/// The function returns nullptr if it's no need to apply settings.
std::shared_ptr<const Settings> getDefaultSettings() const;
/// Returns the settings' constraints.
/// The function returns nullptr if there are no constraints.
std::shared_ptr<const SettingsConstraints> getSettingsConstraints() const;
/// Checks if a specified access is granted, and throws an exception if not.
@ -118,7 +134,8 @@ public:
/// Checks if a specified role is granted with admin option, and throws an exception if not.
void checkAdminOption(const UUID & role_id) const;
/// Returns an instance of ContextAccess which has full access to everything.
/// Makes an instance of ContextAccess which provides full access to everything
/// without any limitations. This is used for the global context.
static std::shared_ptr<const ContextAccess> getFullAccess();
private:
@ -157,7 +174,6 @@ private:
mutable std::shared_ptr<const EnabledRoles> enabled_roles;
mutable ext::scope_guard subscription_for_roles_changes;
mutable std::shared_ptr<const EnabledRolesInfo> roles_info;
mutable boost::atomic_shared_ptr<const boost::container::flat_set<UUID>> roles_with_admin_option;
mutable boost::atomic_shared_ptr<const AccessRights> result_access[7];
mutable std::shared_ptr<const EnabledRowPolicies> enabled_row_policies;
mutable std::shared_ptr<const EnabledQuota> enabled_quota;

View File

@ -53,6 +53,9 @@ namespace ErrorCodes
namespace
{
using EntityType = IAccessStorage::EntityType;
using EntityTypeInfo = IAccessStorage::EntityTypeInfo;
/// Special parser for the 'ATTACH access entity' queries.
class ParserAttachAccessEntity : public IParserBase
{
@ -79,7 +82,7 @@ namespace
/// Reads a file containing ATTACH queries and then parses it to build an access entity.
AccessEntityPtr readAccessEntityFile(const std::filesystem::path & file_path)
AccessEntityPtr readEntityFile(const std::filesystem::path & file_path)
{
/// Read the file.
ReadBufferFromFile in{file_path};
@ -164,11 +167,11 @@ namespace
}
AccessEntityPtr tryReadAccessEntityFile(const std::filesystem::path & file_path, Poco::Logger & log)
AccessEntityPtr tryReadEntityFile(const std::filesystem::path & file_path, Poco::Logger & log)
{
try
{
return readAccessEntityFile(file_path);
return readEntityFile(file_path);
}
catch (...)
{
@ -179,12 +182,12 @@ namespace
/// Writes ATTACH queries for building a specified access entity to a file.
void writeAccessEntityFile(const std::filesystem::path & file_path, const IAccessEntity & entity)
void writeEntityFile(const std::filesystem::path & file_path, const IAccessEntity & entity)
{
/// Build list of ATTACH queries.
ASTs queries;
queries.push_back(InterpreterShowCreateAccessEntityQuery::getAttachQuery(entity));
if (entity.getType() == typeid(User) || entity.getType() == typeid(Role))
if ((entity.getType() == EntityType::USER) || (entity.getType() == EntityType::ROLE))
boost::range::push_back(queries, InterpreterShowGrantsQuery::getAttachGrantQueries(entity));
/// Serialize the list of ATTACH queries to a string.
@ -213,21 +216,21 @@ namespace
/// Calculates the path to a file named <id>.sql for saving an access entity.
std::filesystem::path getAccessEntityFilePath(const String & directory_path, const UUID & id)
std::filesystem::path getEntityFilePath(const String & directory_path, const UUID & id)
{
return std::filesystem::path(directory_path).append(toString(id)).replace_extension(".sql");
}
/// Reads a map of name of access entity to UUID for access entities of some type from a file.
std::unordered_map<String, UUID> readListFile(const std::filesystem::path & file_path)
std::vector<std::pair<UUID, String>> readListFile(const std::filesystem::path & file_path)
{
ReadBufferFromFile in(file_path);
size_t num;
readVarUInt(num, in);
std::unordered_map<String, UUID> res;
res.reserve(num);
std::vector<std::pair<UUID, String>> id_name_pairs;
id_name_pairs.reserve(num);
for (size_t i = 0; i != num; ++i)
{
@ -235,19 +238,19 @@ namespace
readStringBinary(name, in);
UUID id;
readUUIDText(id, in);
res[name] = id;
id_name_pairs.emplace_back(id, std::move(name));
}
return res;
return id_name_pairs;
}
/// Writes a map of name of access entity to UUID for access entities of some type to a file.
void writeListFile(const std::filesystem::path & file_path, const std::unordered_map<String, UUID> & map)
void writeListFile(const std::filesystem::path & file_path, const std::vector<std::pair<UUID, std::string_view>> & id_name_pairs)
{
WriteBufferFromFile out(file_path);
writeVarUInt(map.size(), out);
for (const auto & [name, id] : map)
writeVarUInt(id_name_pairs.size(), out);
for (const auto & [id, name] : id_name_pairs)
{
writeStringBinary(name, out);
writeUUIDText(id, out);
@ -256,24 +259,10 @@ namespace
/// Calculates the path for storing a map of name of access entity to UUID for access entities of some type.
std::filesystem::path getListFilePath(const String & directory_path, std::type_index type)
std::filesystem::path getListFilePath(const String & directory_path, EntityType type)
{
std::string_view file_name;
if (type == typeid(User))
file_name = "users";
else if (type == typeid(Role))
file_name = "roles";
else if (type == typeid(Quota))
file_name = "quotas";
else if (type == typeid(RowPolicy))
file_name = "row_policies";
else if (type == typeid(SettingsProfile))
file_name = "settings_profiles";
else
throw Exception("Unexpected type of access entity: " + IAccessEntity::getTypeName(type),
ErrorCodes::LOGICAL_ERROR);
return std::filesystem::path(directory_path).append(file_name).replace_extension(".list");
std::string_view file_name = EntityTypeInfo::get(type).list_filename;
return std::filesystem::path(directory_path).append(file_name);
}
@ -297,21 +286,12 @@ namespace
return false;
}
}
const std::vector<std::type_index> & getAllAccessEntityTypes()
{
static const std::vector<std::type_index> res = {typeid(User), typeid(Role), typeid(RowPolicy), typeid(Quota), typeid(SettingsProfile)};
return res;
}
}
DiskAccessStorage::DiskAccessStorage()
: IAccessStorage("disk")
{
for (auto type : getAllAccessEntityTypes())
name_to_id_maps[type];
}
@ -363,18 +343,27 @@ void DiskAccessStorage::initialize(const String & directory_path_, Notifications
writeLists();
}
for (const auto & [id, entry] : id_to_entry_map)
for (const auto & [id, entry] : entries_by_id)
prepareNotifications(id, entry, false, notifications);
}
void DiskAccessStorage::clear()
{
entries_by_id.clear();
for (auto type : ext::range(EntityType::MAX))
entries_by_name_and_type[static_cast<size_t>(type)].clear();
}
bool DiskAccessStorage::readLists()
{
assert(id_to_entry_map.empty());
clear();
bool ok = true;
for (auto type : getAllAccessEntityTypes())
for (auto type : ext::range(EntityType::MAX))
{
auto & name_to_id_map = name_to_id_maps.at(type);
auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
auto file_path = getListFilePath(directory_path, type);
if (!std::filesystem::exists(file_path))
{
@ -385,7 +374,14 @@ bool DiskAccessStorage::readLists()
try
{
name_to_id_map = readListFile(file_path);
for (const auto & [id, name] : readListFile(file_path))
{
auto & entry = entries_by_id[id];
entry.id = id;
entry.type = type;
entry.name = name;
entries_by_name[entry.name] = &entry;
}
}
catch (...)
{
@ -393,17 +389,10 @@ bool DiskAccessStorage::readLists()
ok = false;
break;
}
for (const auto & [name, id] : name_to_id_map)
id_to_entry_map.emplace(id, Entry{name, type});
}
if (!ok)
{
id_to_entry_map.clear();
for (auto & name_to_id_map : name_to_id_maps | boost::adaptors::map_values)
name_to_id_map.clear();
}
clear();
return ok;
}
@ -419,11 +408,15 @@ bool DiskAccessStorage::writeLists()
for (const auto & type : types_of_lists_to_write)
{
const auto & name_to_id_map = name_to_id_maps.at(type);
auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
auto file_path = getListFilePath(directory_path, type);
try
{
writeListFile(file_path, name_to_id_map);
std::vector<std::pair<UUID, std::string_view>> id_name_pairs;
id_name_pairs.reserve(entries_by_name.size());
for (const auto * entry : entries_by_name | boost::adaptors::map_values)
id_name_pairs.emplace_back(entry->id, entry->name);
writeListFile(file_path, id_name_pairs);
}
catch (...)
{
@ -441,7 +434,7 @@ bool DiskAccessStorage::writeLists()
}
void DiskAccessStorage::scheduleWriteLists(std::type_index type)
void DiskAccessStorage::scheduleWriteLists(EntityType type)
{
if (failed_to_write_lists)
return;
@ -504,7 +497,7 @@ void DiskAccessStorage::listsWritingThreadFunc()
bool DiskAccessStorage::rebuildLists()
{
LOG_WARNING(getLogger(), "Recovering lists in directory " + directory_path);
assert(id_to_entry_map.empty());
clear();
for (const auto & directory_entry : std::filesystem::directory_iterator(directory_path))
{
@ -518,58 +511,64 @@ bool DiskAccessStorage::rebuildLists()
if (!tryParseUUID(path.stem(), id))
continue;
const auto access_entity_file_path = getAccessEntityFilePath(directory_path, id);
auto entity = tryReadAccessEntityFile(access_entity_file_path, *getLogger());
const auto access_entity_file_path = getEntityFilePath(directory_path, id);
auto entity = tryReadEntityFile(access_entity_file_path, *getLogger());
if (!entity)
continue;
const String & name = entity->getName();
auto type = entity->getType();
auto & name_to_id_map = name_to_id_maps.at(type);
auto it_by_name = name_to_id_map.emplace(entity->getFullName(), id).first;
id_to_entry_map.emplace(id, Entry{it_by_name->first, type});
auto & entry = entries_by_id[id];
entry.id = id;
entry.type = type;
entry.name = name;
entry.entity = entity;
auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
entries_by_name[entry.name] = &entry;
}
for (auto type : getAllAccessEntityTypes())
for (auto type : ext::range(EntityType::MAX))
types_of_lists_to_write.insert(type);
return true;
}
std::optional<UUID> DiskAccessStorage::findImpl(std::type_index type, const String & name) const
std::optional<UUID> DiskAccessStorage::findImpl(EntityType type, const String & name) const
{
std::lock_guard lock{mutex};
const auto & name_to_id_map = name_to_id_maps.at(type);
auto it = name_to_id_map.find(name);
if (it == name_to_id_map.end())
const auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
auto it = entries_by_name.find(name);
if (it == entries_by_name.end())
return {};
return it->second;
return it->second->id;
}
std::vector<UUID> DiskAccessStorage::findAllImpl(std::type_index type) const
std::vector<UUID> DiskAccessStorage::findAllImpl(EntityType type) const
{
std::lock_guard lock{mutex};
const auto & name_to_id_map = name_to_id_maps.at(type);
const auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
std::vector<UUID> res;
res.reserve(name_to_id_map.size());
boost::range::copy(name_to_id_map | boost::adaptors::map_values, std::back_inserter(res));
res.reserve(entries_by_name.size());
for (const auto * entry : entries_by_name | boost::adaptors::map_values)
res.emplace_back(entry->id);
return res;
}
bool DiskAccessStorage::existsImpl(const UUID & id) const
{
std::lock_guard lock{mutex};
return id_to_entry_map.count(id);
return entries_by_id.count(id);
}
AccessEntityPtr DiskAccessStorage::readImpl(const UUID & id) const
{
std::lock_guard lock{mutex};
auto it = id_to_entry_map.find(id);
if (it == id_to_entry_map.end())
auto it = entries_by_id.find(id);
if (it == entries_by_id.end())
throwNotFound(id);
const auto & entry = it->second;
@ -582,8 +581,8 @@ AccessEntityPtr DiskAccessStorage::readImpl(const UUID & id) const
String DiskAccessStorage::readNameImpl(const UUID & id) const
{
std::lock_guard lock{mutex};
auto it = id_to_entry_map.find(id);
if (it == id_to_entry_map.end())
auto it = entries_by_id.find(id);
if (it == entries_by_id.end())
throwNotFound(id);
return String{it->second.name};
}
@ -609,25 +608,25 @@ UUID DiskAccessStorage::insertImpl(const AccessEntityPtr & new_entity, bool repl
void DiskAccessStorage::insertNoLock(const UUID & id, const AccessEntityPtr & new_entity, bool replace_if_exists, Notifications & notifications)
{
const String & name = new_entity->getFullName();
std::type_index type = new_entity->getType();
const String & name = new_entity->getName();
EntityType type = new_entity->getType();
if (!initialized)
throw Exception(
"Cannot insert " + new_entity->getTypeName() + " " + backQuote(name) + " to " + getStorageName()
+ " because the output directory is not set",
"Cannot insert " + new_entity->outputTypeAndName() + " to storage [" + getStorageName()
+ "] because the output directory is not set",
ErrorCodes::LOGICAL_ERROR);
/// Check that we can insert.
auto it_by_id = id_to_entry_map.find(id);
if (it_by_id != id_to_entry_map.end())
auto it_by_id = entries_by_id.find(id);
if (it_by_id != entries_by_id.end())
{
const auto & existing_entry = it_by_id->second;
throwIDCollisionCannotInsert(id, type, name, existing_entry.entity->getType(), existing_entry.entity->getFullName());
throwIDCollisionCannotInsert(id, type, name, existing_entry.entity->getType(), existing_entry.entity->getName());
}
auto & name_to_id_map = name_to_id_maps.at(type);
auto it_by_name = name_to_id_map.find(name);
bool name_collision = (it_by_name != name_to_id_map.end());
auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
auto it_by_name = entries_by_name.find(name);
bool name_collision = (it_by_name != entries_by_name.end());
if (name_collision && !replace_if_exists)
throwNameCollisionCannotInsert(type, name);
@ -636,13 +635,15 @@ void DiskAccessStorage::insertNoLock(const UUID & id, const AccessEntityPtr & ne
writeAccessEntityToDisk(id, *new_entity);
if (name_collision && replace_if_exists)
removeNoLock(it_by_name->second, notifications);
removeNoLock(it_by_name->second->id, notifications);
/// Do insertion.
it_by_name = name_to_id_map.emplace(name, id).first;
it_by_id = id_to_entry_map.emplace(id, Entry{it_by_name->first, type}).first;
auto & entry = it_by_id->second;
auto & entry = entries_by_id[id];
entry.id = id;
entry.type = type;
entry.name = name;
entry.entity = new_entity;
entries_by_name[entry.name] = &entry;
prepareNotifications(id, entry, false, notifications);
}
@ -659,22 +660,21 @@ void DiskAccessStorage::removeImpl(const UUID & id)
void DiskAccessStorage::removeNoLock(const UUID & id, Notifications & notifications)
{
auto it = id_to_entry_map.find(id);
if (it == id_to_entry_map.end())
auto it = entries_by_id.find(id);
if (it == entries_by_id.end())
throwNotFound(id);
Entry & entry = it->second;
String name{it->second.name};
std::type_index type = it->second.type;
EntityType type = entry.type;
scheduleWriteLists(type);
deleteAccessEntityOnDisk(id);
/// Do removing.
prepareNotifications(id, entry, true, notifications);
id_to_entry_map.erase(it);
auto & name_to_id_map = name_to_id_maps.at(type);
name_to_id_map.erase(name);
auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
entries_by_name.erase(entry.name);
entries_by_id.erase(it);
}
@ -690,8 +690,8 @@ void DiskAccessStorage::updateImpl(const UUID & id, const UpdateFunc & update_fu
void DiskAccessStorage::updateNoLock(const UUID & id, const UpdateFunc & update_func, Notifications & notifications)
{
auto it = id_to_entry_map.find(id);
if (it == id_to_entry_map.end())
auto it = entries_by_id.find(id);
if (it == entries_by_id.end())
throwNotFound(id);
Entry & entry = it->second;
@ -700,18 +700,22 @@ void DiskAccessStorage::updateNoLock(const UUID & id, const UpdateFunc & update_
auto old_entity = entry.entity;
auto new_entity = update_func(old_entity);
if (!new_entity->isTypeOf(old_entity->getType()))
throwBadCast(id, new_entity->getType(), new_entity->getName(), old_entity->getType());
if (*new_entity == *old_entity)
return;
String new_name = new_entity->getFullName();
auto old_name = entry.name;
const std::type_index type = entry.type;
const String & new_name = new_entity->getName();
const String & old_name = old_entity->getName();
const EntityType type = entry.type;
auto & entries_by_name = entries_by_name_and_type[static_cast<size_t>(type)];
bool name_changed = (new_name != old_name);
if (name_changed)
{
const auto & name_to_id_map = name_to_id_maps.at(type);
if (name_to_id_map.count(new_name))
throwNameCollisionCannotRename(type, String{old_name}, new_name);
if (entries_by_name.count(new_name))
throwNameCollisionCannotRename(type, old_name, new_name);
scheduleWriteLists(type);
}
@ -720,10 +724,9 @@ void DiskAccessStorage::updateNoLock(const UUID & id, const UpdateFunc & update_
if (name_changed)
{
auto & name_to_id_map = name_to_id_maps.at(type);
name_to_id_map.erase(String{old_name});
auto it_by_name = name_to_id_map.emplace(new_name, id).first;
entry.name = it_by_name->first;
entries_by_name.erase(entry.name);
entry.name = new_name;
entries_by_name[entry.name] = &entry;
}
prepareNotifications(id, entry, false, notifications);
@ -732,19 +735,19 @@ void DiskAccessStorage::updateNoLock(const UUID & id, const UpdateFunc & update_
AccessEntityPtr DiskAccessStorage::readAccessEntityFromDisk(const UUID & id) const
{
return readAccessEntityFile(getAccessEntityFilePath(directory_path, id));
return readEntityFile(getEntityFilePath(directory_path, id));
}
void DiskAccessStorage::writeAccessEntityToDisk(const UUID & id, const IAccessEntity & entity) const
{
writeAccessEntityFile(getAccessEntityFilePath(directory_path, id), entity);
writeEntityFile(getEntityFilePath(directory_path, id), entity);
}
void DiskAccessStorage::deleteAccessEntityOnDisk(const UUID & id) const
{
auto file_path = getAccessEntityFilePath(directory_path, id);
auto file_path = getEntityFilePath(directory_path, id);
if (!std::filesystem::remove(file_path))
throw Exception("Couldn't delete " + file_path.string(), ErrorCodes::FILE_DOESNT_EXIST);
}
@ -759,17 +762,16 @@ void DiskAccessStorage::prepareNotifications(const UUID & id, const Entry & entr
for (const auto & handler : entry.handlers_by_id)
notifications.push_back({handler, id, entity});
auto range = handlers_by_type.equal_range(entry.type);
for (auto it = range.first; it != range.second; ++it)
notifications.push_back({it->second, id, entity});
for (const auto & handler : handlers_by_type[static_cast<size_t>(entry.type)])
notifications.push_back({handler, id, entity});
}
ext::scope_guard DiskAccessStorage::subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const
{
std::lock_guard lock{mutex};
auto it = id_to_entry_map.find(id);
if (it == id_to_entry_map.end())
auto it = entries_by_id.find(id);
if (it == entries_by_id.end())
return {};
const Entry & entry = it->second;
auto handler_it = entry.handlers_by_id.insert(entry.handlers_by_id.end(), handler);
@ -777,8 +779,8 @@ ext::scope_guard DiskAccessStorage::subscribeForChangesImpl(const UUID & id, con
return [this, id, handler_it]
{
std::lock_guard lock2{mutex};
auto it2 = id_to_entry_map.find(id);
if (it2 != id_to_entry_map.end())
auto it2 = entries_by_id.find(id);
if (it2 != entries_by_id.end())
{
const Entry & entry2 = it2->second;
entry2.handlers_by_id.erase(handler_it);
@ -786,23 +788,26 @@ ext::scope_guard DiskAccessStorage::subscribeForChangesImpl(const UUID & id, con
};
}
ext::scope_guard DiskAccessStorage::subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const
ext::scope_guard DiskAccessStorage::subscribeForChangesImpl(EntityType type, const OnChangedHandler & handler) const
{
std::lock_guard lock{mutex};
auto handler_it = handlers_by_type.emplace(type, handler);
auto & handlers = handlers_by_type[static_cast<size_t>(type)];
handlers.push_back(handler);
auto handler_it = std::prev(handlers.end());
return [this, handler_it]
return [this, type, handler_it]
{
std::lock_guard lock2{mutex};
handlers_by_type.erase(handler_it);
auto & handlers2 = handlers_by_type[static_cast<size_t>(type)];
handlers2.erase(handler_it);
};
}
bool DiskAccessStorage::hasSubscriptionImpl(const UUID & id) const
{
std::lock_guard lock{mutex};
auto it = id_to_entry_map.find(id);
if (it != id_to_entry_map.end())
auto it = entries_by_id.find(id);
if (it != entries_by_id.end())
{
const Entry & entry = it->second;
return !entry.handlers_by_id.empty();
@ -810,11 +815,11 @@ bool DiskAccessStorage::hasSubscriptionImpl(const UUID & id) const
return false;
}
bool DiskAccessStorage::hasSubscriptionImpl(std::type_index type) const
bool DiskAccessStorage::hasSubscriptionImpl(EntityType type) const
{
std::lock_guard lock{mutex};
auto range = handlers_by_type.equal_range(type);
return range.first != range.second;
const auto & handlers = handlers_by_type[static_cast<size_t>(type)];
return !handlers.empty();
}
}

View File

@ -17,8 +17,8 @@ public:
void setDirectory(const String & directory_path_);
private:
std::optional<UUID> findImpl(std::type_index type, const String & name) const override;
std::vector<UUID> findAllImpl(std::type_index type) const override;
std::optional<UUID> findImpl(EntityType type, const String & name) const override;
std::vector<UUID> findAllImpl(EntityType type) const override;
bool existsImpl(const UUID & id) const override;
AccessEntityPtr readImpl(const UUID & id) const override;
String readNameImpl(const UUID & id) const override;
@ -27,14 +27,15 @@ private:
void removeImpl(const UUID & id) override;
void updateImpl(const UUID & id, const UpdateFunc & update_func) override;
ext::scope_guard subscribeForChangesImpl(const UUID & id, const OnChangedHandler & handler) const override;
ext::scope_guard subscribeForChangesImpl(std::type_index type, const OnChangedHandler & handler) const override;
ext::scope_guard subscribeForChangesImpl(EntityType type, const OnChangedHandler & handler) const override;
bool hasSubscriptionImpl(const UUID & id) const override;
bool hasSubscriptionImpl(std::type_index type) const override;
bool hasSubscriptionImpl(EntityType type) const override;
void initialize(const String & directory_path_, Notifications & notifications);
void clear();
bool readLists();
bool writeLists();
void scheduleWriteLists(std::type_index type);
void scheduleWriteLists(EntityType type);
bool rebuildLists();
void startListsWritingThread();
@ -52,9 +53,9 @@ private:
using NameToIDMap = std::unordered_map<String, UUID>;
struct Entry
{
Entry(const std::string_view & name_, std::type_index type_) : name(name_), type(type_) {}
std::string_view name; /// view points to a string in `name_to_id_maps`.
std::type_index type;
UUID id;
String name;
EntityType type;
mutable AccessEntityPtr entity; /// may be nullptr, if the entity hasn't been loaded yet.
mutable std::list<OnChangedHandler> handlers_by_id;
};
@ -63,14 +64,14 @@ private:
String directory_path;
bool initialized = false;
std::unordered_map<std::type_index, NameToIDMap> name_to_id_maps;
std::unordered_map<UUID, Entry> id_to_entry_map;
boost::container::flat_set<std::type_index> types_of_lists_to_write;
std::unordered_map<UUID, Entry> entries_by_id;
std::unordered_map<std::string_view, Entry *> entries_by_name_and_type[static_cast<size_t>(EntityType::MAX)];
boost::container::flat_set<EntityType> types_of_lists_to_write;
bool failed_to_write_lists = false; /// Whether writing of the list files has been failed since the recent restart of the server.
ThreadFromGlobalPool lists_writing_thread; /// List files are written in a separate thread.
std::condition_variable lists_writing_thread_should_exit; /// Signals `lists_writing_thread` to exit.
std::atomic<bool> lists_writing_thread_exited = false;
mutable std::unordered_multimap<std::type_index, OnChangedHandler> handlers_by_type;
mutable std::list<OnChangedHandler> handlers_by_type[static_cast<size_t>(EntityType::MAX)];
mutable std::mutex mutex;
};
}

View File

@ -1,5 +1,5 @@
#include <Access/EnabledQuota.h>
#include <Access/QuotaUsageInfo.h>
#include <Access/QuotaUsage.h>
#include <Common/Exception.h>
#include <Common/quoteString.h>
#include <ext/chrono_io.h>
@ -30,9 +30,10 @@ struct EnabledQuota::Impl
if (resource_type == Quota::EXECUTION_TIME)
amount_to_string = [&](UInt64 amount) { return ext::to_string(std::chrono::nanoseconds(amount)); };
const auto & type_info = Quota::ResourceTypeInfo::get(resource_type);
throw Exception(
"Quota for user " + backQuote(user_name) + " for " + ext::to_string(duration) + " has been exceeded: "
+ Quota::getNameOfResourceType(resource_type) + " = " + amount_to_string(used) + "/" + amount_to_string(max) + ". "
+ type_info.outputWithAmount(used) + "/" + type_info.amountToString(max) + ". "
+ "Interval will end at " + ext::to_string(end_of_interval) + ". " + "Name of quota template: " + backQuote(quota_name),
ErrorCodes::QUOTA_EXPIRED);
}
@ -83,7 +84,7 @@ struct EnabledQuota::Impl
{
ResourceAmount used = (interval.used[resource_type] += amount);
ResourceAmount max = interval.max[resource_type];
if (max == Quota::UNLIMITED)
if (!max)
continue;
if (used > max)
{
@ -111,7 +112,7 @@ struct EnabledQuota::Impl
{
ResourceAmount used = interval.used[resource_type];
ResourceAmount max = interval.max[resource_type];
if (max == Quota::UNLIMITED)
if (!max)
continue;
if (used > max)
{
@ -128,12 +129,22 @@ struct EnabledQuota::Impl
const Intervals & intervals,
std::chrono::system_clock::time_point current_time)
{
for (auto resource_type : ext::range_with_static_cast<Quota::ResourceType>(Quota::MAX_RESOURCE_TYPE))
for (auto resource_type : ext::range(Quota::MAX_RESOURCE_TYPE))
checkExceeded(user_name, intervals, resource_type, current_time);
}
};
EnabledQuota::Interval::Interval()
{
for (auto resource_type : ext::range(MAX_RESOURCE_TYPE))
{
used[resource_type].store(0);
max[resource_type] = 0;
}
}
EnabledQuota::Interval & EnabledQuota::Interval::operator =(const Interval & src)
{
if (this == &src)
@ -151,27 +162,30 @@ EnabledQuota::Interval & EnabledQuota::Interval::operator =(const Interval & src
}
QuotaUsageInfo EnabledQuota::Intervals::getUsageInfo(std::chrono::system_clock::time_point current_time) const
std::optional<QuotaUsage> EnabledQuota::Intervals::getUsage(std::chrono::system_clock::time_point current_time) const
{
QuotaUsageInfo info;
info.quota_id = quota_id;
info.quota_name = quota_name;
info.quota_key = quota_key;
info.intervals.reserve(intervals.size());
if (!quota_id)
return {};
QuotaUsage usage;
usage.quota_id = *quota_id;
usage.quota_name = quota_name;
usage.quota_key = quota_key;
usage.intervals.reserve(intervals.size());
for (const auto & in : intervals)
{
info.intervals.push_back({});
auto & out = info.intervals.back();
usage.intervals.push_back({});
auto & out = usage.intervals.back();
out.duration = in.duration;
out.randomize_interval = in.randomize_interval;
out.end_of_interval = Impl::getEndOfInterval(in, current_time);
for (auto resource_type : ext::range(MAX_RESOURCE_TYPE))
{
out.max[resource_type] = in.max[resource_type];
if (in.max[resource_type])
out.max[resource_type] = in.max[resource_type];
out.used[resource_type] = in.used[resource_type];
}
}
return info;
return usage;
}
@ -238,10 +252,10 @@ void EnabledQuota::checkExceeded(ResourceType resource_type) const
}
QuotaUsageInfo EnabledQuota::getUsageInfo() const
std::optional<QuotaUsage> EnabledQuota::getUsage() const
{
auto loaded = intervals.load();
return loaded->getUsageInfo(std::chrono::system_clock::now());
return loaded->getUsage(std::chrono::system_clock::now());
}

View File

@ -12,7 +12,7 @@
namespace DB
{
struct QuotaUsageInfo;
struct QuotaUsage;
/// Instances of `EnabledQuota` are used to track resource consumption.
@ -23,7 +23,7 @@ public:
{
UUID user_id;
String user_name;
std::vector<UUID> enabled_roles;
boost::container::flat_set<UUID> enabled_roles;
Poco::Net::IPAddress client_address;
String client_key;
@ -53,7 +53,7 @@ public:
void checkExceeded(ResourceType resource_type) const;
/// Returns the information about quota consumption.
QuotaUsageInfo getUsageInfo() const;
std::optional<QuotaUsage> getUsage() const;
/// Returns an instance of EnabledQuota which is never exceeded.
static std::shared_ptr<const EnabledQuota> getUnlimitedQuota();
@ -65,17 +65,17 @@ private:
const String & getUserName() const { return params.user_name; }
static constexpr size_t MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
static constexpr auto MAX_RESOURCE_TYPE = Quota::MAX_RESOURCE_TYPE;
struct Interval
{
mutable std::atomic<ResourceAmount> used[MAX_RESOURCE_TYPE];
ResourceAmount max[MAX_RESOURCE_TYPE];
std::chrono::seconds duration;
bool randomize_interval;
std::chrono::seconds duration = std::chrono::seconds::zero();
bool randomize_interval = false;
mutable std::atomic<std::chrono::system_clock::duration> end_of_interval;
Interval() {}
Interval();
Interval(const Interval & src) { *this = src; }
Interval & operator =(const Interval & src);
};
@ -83,11 +83,11 @@ private:
struct Intervals
{
std::vector<Interval> intervals;
UUID quota_id;
std::optional<UUID> quota_id;
String quota_name;
String quota_key;
QuotaUsageInfo getUsageInfo(std::chrono::system_clock::time_point current_time) const;
std::optional<QuotaUsage> getUsage(std::chrono::system_clock::time_point current_time) const;
};
struct Impl;

Some files were not shown because too many files have changed in this diff Show More