Merge branch 'master' of github.com:ClickHouse/ClickHouse

This commit is contained in:
alesapin 2019-12-10 12:54:42 +03:00
commit 3cc3bd2fea
27 changed files with 778 additions and 107 deletions

View File

@ -221,7 +221,8 @@ void MySQLHandler::authenticate(const String & user_name, const String & auth_pl
{ {
// For compatibility with JavaScript MySQL client, Native41 authentication plugin is used when possible (if password is specified using double SHA1). Otherwise SHA256 plugin is used. // For compatibility with JavaScript MySQL client, Native41 authentication plugin is used when possible (if password is specified using double SHA1). Otherwise SHA256 plugin is used.
auto user = connection_context.getUser(user_name); auto user = connection_context.getUser(user_name);
if (user->authentication.getType() != DB::Authentication::DOUBLE_SHA1_PASSWORD) const DB::Authentication::Type user_auth_type = user->authentication.getType();
if (user_auth_type != DB::Authentication::DOUBLE_SHA1_PASSWORD && user_auth_type != DB::Authentication::PLAINTEXT_PASSWORD && user_auth_type != DB::Authentication::NO_PASSWORD)
{ {
authPluginSSL(); authPluginSSL();
} }

View File

@ -160,6 +160,35 @@ void Authentication::setPasswordHashBinary(const Digest & hash)
} }
Digest Authentication::getPasswordDoubleSHA1() const
{
switch (type)
{
case NO_PASSWORD:
{
Poco::SHA1Engine engine;
return engine.digest();
}
case PLAINTEXT_PASSWORD:
{
Poco::SHA1Engine engine;
engine.update(getPassword());
const Digest & first_sha1 = engine.digest();
engine.update(first_sha1.data(), first_sha1.size());
return engine.digest();
}
case SHA256_PASSWORD:
throw Exception("Cannot get password double SHA1 for user with 'SHA256_PASSWORD' authentication.", ErrorCodes::BAD_ARGUMENTS);
case DOUBLE_SHA1_PASSWORD:
return password_hash;
}
throw Exception("Unknown authentication type: " + std::to_string(static_cast<int>(type)), ErrorCodes::LOGICAL_ERROR);
}
bool Authentication::isCorrectPassword(const String & password_) const bool Authentication::isCorrectPassword(const String & password_) const
{ {
switch (type) switch (type)
@ -168,7 +197,14 @@ bool Authentication::isCorrectPassword(const String & password_) const
return true; return true;
case PLAINTEXT_PASSWORD: case PLAINTEXT_PASSWORD:
return password_ == StringRef{reinterpret_cast<const char *>(password_hash.data()), password_hash.size()}; {
if (password_ == StringRef{reinterpret_cast<const char *>(password_hash.data()), password_hash.size()})
return true;
// For compatibility with MySQL clients which support only native authentication plugin, SHA1 can be passed instead of password.
auto password_sha1 = encodeSHA1(password_hash);
return password_ == StringRef{reinterpret_cast<const char *>(password_sha1.data()), password_sha1.size()};
}
case SHA256_PASSWORD: case SHA256_PASSWORD:
return encodeSHA256(password_) == password_hash; return encodeSHA256(password_) == password_hash;

View File

@ -49,6 +49,10 @@ public:
void setPasswordHashBinary(const Digest & hash); void setPasswordHashBinary(const Digest & hash);
const Digest & getPasswordHashBinary() const { return password_hash; } const Digest & getPasswordHashBinary() const { return password_hash; }
/// Returns SHA1(SHA1(password)) used by MySQL compatibility server for authentication.
/// Allowed to use for Type::NO_PASSWORD, Type::PLAINTEXT_PASSWORD, Type::DOUBLE_SHA1_PASSWORD.
Digest getPasswordDoubleSHA1() const;
/// Checks if the provided password is correct. Returns false if not. /// Checks if the provided password is correct. Returns false if not.
bool isCorrectPassword(const String & password) const; bool isCorrectPassword(const String & password) const;

View File

@ -953,10 +953,7 @@ public:
auto user = context.getUser(user_name); auto user = context.getUser(user_name);
if (user->authentication.getType() != DB::Authentication::DOUBLE_SHA1_PASSWORD) Poco::SHA1Engine::Digest double_sha1_value = user->authentication.getPasswordDoubleSHA1();
throw Exception("Cannot use " + getName() + " auth plugin for user " + user_name + " since its password isn't specified using double SHA1.", ErrorCodes::UNKNOWN_EXCEPTION);
Poco::SHA1Engine::Digest double_sha1_value = user->authentication.getPasswordHashBinary();
assert(double_sha1_value.size() == Poco::SHA1Engine::DIGEST_SIZE); assert(double_sha1_value.size() == Poco::SHA1Engine::DIGEST_SIZE);
Poco::SHA1Engine engine; Poco::SHA1Engine engine;

View File

@ -8,18 +8,18 @@
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <ext/range.h> #include <ext/range.h>
#include <cmath> #include <cmath>
#include <array>
namespace DB namespace DB
{ {
/** Calculates the distance between two geographical locations. /** Calculates the distance between two geographical locations.
* There are two variants: * There are three variants:
* greatCircleDistance: calculates the distance on a sphere: https://en.wikipedia.org/wiki/Great-circle_distance * greatCircleAngle: calculates the distance on a sphere in degrees: https://en.wikipedia.org/wiki/Great-circle_distance
* geoDistance: calculates the distance on WGS-84 ellipsoid. * greatCircleDistance: calculates the distance on a sphere in meters.
* geoDistance: calculates the distance on WGS-84 ellipsoid in meters.
* *
* The function calculates distance in meters between two points on Earth specified by longitude and latitude in degrees. * The function calculates distance between two points on Earth specified by longitude and latitude in degrees.
* *
* Latitude must be in [-90, 90], longitude must be [-180, 180]. * Latitude must be in [-90, 90], longitude must be [-180, 180].
* *
@ -42,36 +42,19 @@ constexpr size_t COS_LUT_SIZE = 1024; // maxerr 0.00063%
constexpr size_t ASIN_SQRT_LUT_SIZE = 512; constexpr size_t ASIN_SQRT_LUT_SIZE = 512;
constexpr size_t METRIC_LUT_SIZE = 1024; constexpr size_t METRIC_LUT_SIZE = 1024;
/** We use "WGS-84 ellipsoidal quadratic mean radius of Earth" as the approximation to calculate distances on sphere. /** Earth radius in meters using WGS84 authalic radius.
* The motivation for it is explained here: https://math.wikia.org/wiki/Ellipsoidal_quadratic_mean_radius * We use this value to be consistent with H3 library.
*
* Brief explanation:
* - the radius of sphere is choosen to minimize the difference between distance on that sphere and distance on WGS-84 ellipsoid between two points,
* averaged uniformly (?) by all angles (?) between points.
* This sounds not clear enough for me: what set we are averaging and by what measure?
*
* The value should be calculated this way:
* WITH 6378137.0 AS a, 6356752.314245 AS b SELECT sqrt(3 * a * a + b * b) / 2
*
* But for unknown reason, slightly different value is used.
* This constant may be changed in future with a note about backward incompatible change in the changelog.
*
* See also:
* https://github.com/Project-OSRM/osrm-backend/blob/bb1f4a025a3cefd3598a38b9d3e55485d1080ec5/third_party/libosmium/include/osmium/geom/haversine.hpp#L58-L59
* https://github.com/Project-OSRM/osrm-backend/issues/5051
* https://github.com/mapbox/turf-swift/issues/26
* https://github.com/Project-OSRM/osrm-backend/pull/5041
* https://en.wikipedia.org/wiki/Talk:Great-circle_distance/Archive_1
*/ */
constexpr float EARTH_RADIUS = 6372797.560856; constexpr float EARTH_RADIUS = 6371007.180918475;
constexpr float EARTH_DIAMETER = 2 * EARTH_RADIUS; constexpr float EARTH_DIAMETER = 2 * EARTH_RADIUS;
float cos_lut[COS_LUT_SIZE + 1]; /// cos(x) table float cos_lut[COS_LUT_SIZE + 1]; /// cos(x) table
float asin_sqrt_lut[ASIN_SQRT_LUT_SIZE + 1]; /// asin(sqrt(x)) * earth_diameter table float asin_sqrt_lut[ASIN_SQRT_LUT_SIZE + 1]; /// asin(sqrt(x)) * earth_diameter table
float sphere_metric_lut[METRIC_LUT_SIZE + 1]; /// sphere metric: the distance for one degree across longitude depending on latitude float sphere_metric_lut[METRIC_LUT_SIZE + 1]; /// sphere metric, unitless: the distance in degrees for one degree across longitude depending on latitude
float wgs84_metric_lut[2 * (METRIC_LUT_SIZE + 1)]; /// ellipsoid metric: the distance across one degree latitude/longitude depending on latitude float sphere_metric_meters_lut[METRIC_LUT_SIZE + 1]; /// sphere metric: the distance in meters for one degree across longitude depending on latitude
float wgs84_metric_meters_lut[2 * (METRIC_LUT_SIZE + 1)]; /// ellipsoid metric: the distance in meters across one degree latitude/longitude depending on latitude
inline double sqr(double v) inline double sqr(double v)
@ -90,7 +73,7 @@ void geodistInit()
cos_lut[i] = static_cast<float>(cos(2 * PI * i / COS_LUT_SIZE)); // [0, 2 * pi] -> [0, COS_LUT_SIZE] cos_lut[i] = static_cast<float>(cos(2 * PI * i / COS_LUT_SIZE)); // [0, 2 * pi] -> [0, COS_LUT_SIZE]
for (size_t i = 0; i <= ASIN_SQRT_LUT_SIZE; ++i) for (size_t i = 0; i <= ASIN_SQRT_LUT_SIZE; ++i)
asin_sqrt_lut[i] = static_cast<float>(EARTH_DIAMETER * asin( asin_sqrt_lut[i] = static_cast<float>(asin(
sqrt(static_cast<double>(i) / ASIN_SQRT_LUT_SIZE))); // [0, 1] -> [0, ASIN_SQRT_LUT_SIZE] sqrt(static_cast<double>(i) / ASIN_SQRT_LUT_SIZE))); // [0, 1] -> [0, ASIN_SQRT_LUT_SIZE]
for (size_t i = 0; i <= METRIC_LUT_SIZE; ++i) for (size_t i = 0; i <= METRIC_LUT_SIZE; ++i)
@ -100,10 +83,13 @@ void geodistInit()
/// Squared metric coefficients (for the distance in meters) on a tangent plane, for latitude and longitude (in degrees), /// Squared metric coefficients (for the distance in meters) on a tangent plane, for latitude and longitude (in degrees),
/// depending on the latitude (in radians). /// depending on the latitude (in radians).
wgs84_metric_lut[i * 2] = static_cast<float>(sqr(111132.09 - 566.05 * cos(2 * latitude) + 1.20 * cos(4 * latitude))); /// https://github.com/mapbox/cheap-ruler/blob/master/index.js#L67
wgs84_metric_lut[i * 2 + 1] = static_cast<float>(sqr(111415.13 * cos(latitude) - 94.55 * cos(3 * latitude) + 0.12 * cos(5 * latitude))); wgs84_metric_meters_lut[i * 2] = static_cast<float>(sqr(111132.09 - 566.05 * cos(2 * latitude) + 1.20 * cos(4 * latitude)));
wgs84_metric_meters_lut[i * 2 + 1] = static_cast<float>(sqr(111415.13 * cos(latitude) - 94.55 * cos(3 * latitude) + 0.12 * cos(5 * latitude)));
sphere_metric_lut[i] = static_cast<float>(sqr((EARTH_DIAMETER * PI / 360) * cos(latitude))); sphere_metric_meters_lut[i] = static_cast<float>(sqr((EARTH_DIAMETER * PI / 360) * cos(latitude)));
sphere_metric_lut[i] = cosf(latitude);
} }
} }
@ -143,7 +129,7 @@ inline float geodistFastAsinSqrt(float x)
{ {
// distance under 4546 km, Taylor error under 0.00072% // distance under 4546 km, Taylor error under 0.00072%
float y = sqrtf(x); float y = sqrtf(x);
return EARTH_DIAMETER * (y + x * y * 0.166666666666666f + x * x * y * 0.075f + x * x * x * y * 0.044642857142857f); return y + x * y * 0.166666666666666f + x * x * y * 0.075f + x * x * x * y * 0.044642857142857f;
} }
if (x < 0.948f) if (x < 0.948f)
{ {
@ -158,8 +144,9 @@ inline float geodistFastAsinSqrt(float x)
enum class Method enum class Method
{ {
SPHERE, SPHERE_DEGREES,
WGS84 SPHERE_METERS,
WGS84_METERS,
}; };
@ -187,20 +174,27 @@ float distance(float lon1deg, float lat1deg, float lon2deg, float lat2deg)
float k_lat; float k_lat;
float k_lon; float k_lon;
if constexpr (method == Method::SPHERE) if constexpr (method == Method::SPHERE_DEGREES)
{ {
k_lat = sqr(EARTH_DIAMETER * PI / 360); k_lat = 1;
k_lon = sphere_metric_lut[latitude_midpoint_index] k_lon = sphere_metric_lut[latitude_midpoint_index]
+ (sphere_metric_lut[latitude_midpoint_index + 1] - sphere_metric_lut[latitude_midpoint_index]) * (latitude_midpoint - latitude_midpoint_index); + (sphere_metric_lut[latitude_midpoint_index + 1] - sphere_metric_lut[latitude_midpoint_index]) * (latitude_midpoint - latitude_midpoint_index);
} }
else if constexpr (method == Method::WGS84) else if constexpr (method == Method::SPHERE_METERS)
{ {
k_lat = wgs84_metric_lut[latitude_midpoint_index * 2] k_lat = sqr(EARTH_DIAMETER * PI / 360);
+ (wgs84_metric_lut[(latitude_midpoint_index + 1) * 2] - wgs84_metric_lut[latitude_midpoint_index * 2]) * (latitude_midpoint - latitude_midpoint_index);
k_lon = wgs84_metric_lut[latitude_midpoint_index * 2 + 1] k_lon = sphere_metric_meters_lut[latitude_midpoint_index]
+ (wgs84_metric_lut[(latitude_midpoint_index + 1) * 2 + 1] - wgs84_metric_lut[latitude_midpoint_index * 2 + 1]) * (latitude_midpoint - latitude_midpoint_index); + (sphere_metric_meters_lut[latitude_midpoint_index + 1] - sphere_metric_meters_lut[latitude_midpoint_index]) * (latitude_midpoint - latitude_midpoint_index);
}
else if constexpr (method == Method::WGS84_METERS)
{
k_lat = wgs84_metric_meters_lut[latitude_midpoint_index * 2]
+ (wgs84_metric_meters_lut[(latitude_midpoint_index + 1) * 2] - wgs84_metric_meters_lut[latitude_midpoint_index * 2]) * (latitude_midpoint - latitude_midpoint_index);
k_lon = wgs84_metric_meters_lut[latitude_midpoint_index * 2 + 1]
+ (wgs84_metric_meters_lut[(latitude_midpoint_index + 1) * 2 + 1] - wgs84_metric_meters_lut[latitude_midpoint_index * 2 + 1]) * (latitude_midpoint - latitude_midpoint_index);
} }
/// Metric on a tangent plane: it differs from Euclidean metric only by scale of coordinates. /// Metric on a tangent plane: it differs from Euclidean metric only by scale of coordinates.
@ -213,7 +207,10 @@ float distance(float lon1deg, float lat1deg, float lon2deg, float lat2deg)
float a = sqrf(geodistFastSin(lat_diff * RAD_IN_DEG_HALF)) float a = sqrf(geodistFastSin(lat_diff * RAD_IN_DEG_HALF))
+ geodistFastCos(lat1deg * RAD_IN_DEG) * geodistFastCos(lat2deg * RAD_IN_DEG) * sqrf(geodistFastSin(lon_diff * RAD_IN_DEG_HALF)); + geodistFastCos(lat1deg * RAD_IN_DEG) * geodistFastCos(lat2deg * RAD_IN_DEG) * sqrf(geodistFastSin(lon_diff * RAD_IN_DEG_HALF));
return geodistFastAsinSqrt(a); if constexpr (method == Method::SPHERE_DEGREES)
return (360.0f / PI) * geodistFastAsinSqrt(a);
else
return EARTH_DIAMETER * geodistFastAsinSqrt(a);
} }
} }
@ -224,7 +221,11 @@ template <Method method>
class FunctionGeoDistance : public IFunction class FunctionGeoDistance : public IFunction
{ {
public: public:
static constexpr auto name = (method == Method::SPHERE) ? "greatCircleDistance" : "geoDistance"; static constexpr auto name =
(method == Method::SPHERE_DEGREES) ? "greatCircleAngle"
: ((method == Method::SPHERE_METERS) ? "greatCircleDistance"
: "geoDistance");
static FunctionPtr create(const Context &) { return std::make_shared<FunctionGeoDistance<method>>(); } static FunctionPtr create(const Context &) { return std::make_shared<FunctionGeoDistance<method>>(); }
private: private:
@ -271,8 +272,9 @@ private:
void registerFunctionGeoDistance(FunctionFactory & factory) void registerFunctionGeoDistance(FunctionFactory & factory)
{ {
geodistInit(); geodistInit();
factory.registerFunction<FunctionGeoDistance<Method::SPHERE>>(); factory.registerFunction<FunctionGeoDistance<Method::SPHERE_DEGREES>>();
factory.registerFunction<FunctionGeoDistance<Method::WGS84>>(); factory.registerFunction<FunctionGeoDistance<Method::SPHERE_METERS>>();
factory.registerFunction<FunctionGeoDistance<Method::WGS84_METERS>>();
} }
} }

View File

@ -96,6 +96,12 @@ namespace ErrorCodes
} }
namespace
{
const char * DELETE_ON_DESTROY_MARKER_PATH = "delete-on-destroy.txt";
}
MergeTreeData::MergeTreeData( MergeTreeData::MergeTreeData(
const String & database_, const String & database_,
const String & table_, const String & table_,
@ -801,6 +807,17 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
part->relative_path = part_name; part->relative_path = part_name;
bool broken = false; bool broken = false;
Poco::Path part_path(getFullPathOnDisk(part_disk_ptr), part_name);
Poco::Path marker_path(part_path, DELETE_ON_DESTROY_MARKER_PATH);
if (Poco::File(marker_path).exists())
{
LOG_WARNING(log, "Detaching stale part " << getFullPathOnDisk(part_disk_ptr) << part_name << ", which should have been deleted after a move. That can only happen after unclean restart of ClickHouse after move of a part having an operation blocking that stale copy of part.");
std::lock_guard loading_lock(mutex);
broken_parts_to_detach.push_back(part);
++suspicious_broken_parts;
return;
}
try try
{ {
part->loadColumnsChecksumsIndexes(require_part_metadata, true); part->loadColumnsChecksumsIndexes(require_part_metadata, true);
@ -2515,7 +2532,7 @@ MergeTreeData::DataPartPtr MergeTreeData::getActiveContainingPart(
void MergeTreeData::swapActivePart(MergeTreeData::DataPartPtr part_copy) void MergeTreeData::swapActivePart(MergeTreeData::DataPartPtr part_copy)
{ {
auto lock = lockParts(); auto lock = lockParts();
for (const auto & original_active_part : getDataPartsStateRange(DataPartState::Committed)) for (auto original_active_part : getDataPartsStateRange(DataPartState::Committed))
{ {
if (part_copy->name == original_active_part->name) if (part_copy->name == original_active_part->name)
{ {
@ -2528,6 +2545,16 @@ void MergeTreeData::swapActivePart(MergeTreeData::DataPartPtr part_copy)
auto part_it = data_parts_indexes.insert(part_copy).first; auto part_it = data_parts_indexes.insert(part_copy).first;
modifyPartState(part_it, DataPartState::Committed); modifyPartState(part_it, DataPartState::Committed);
Poco::Path marker_path(Poco::Path(original_active_part->getFullPath()), DELETE_ON_DESTROY_MARKER_PATH);
try
{
Poco::File(marker_path).createFile();
}
catch (Poco::Exception & e)
{
LOG_ERROR(log, e.what() << " (while creating DeleteOnDestroy marker: " + backQuote(marker_path.toString()) + ")");
}
return; return;
} }
} }

View File

@ -346,6 +346,11 @@ MergeTreeDataPart::~MergeTreeDataPart()
} }
dir.remove(true); dir.remove(true);
if (state == State::DeleteOnDestroy)
{
LOG_TRACE(storage.log, "Removed part from old location " << path);
}
} }
catch (...) catch (...)
{ {

View File

@ -628,11 +628,11 @@ class ClickHouseInstance:
def http_query(self, sql, data=None): def http_query(self, sql, data=None):
return urllib.urlopen("http://" + self.ip_address + ":8123/?query=" + urllib.quote(sql, safe=''), data).read() return urllib.urlopen("http://" + self.ip_address + ":8123/?query=" + urllib.quote(sql, safe=''), data).read()
def restart_clickhouse(self, stop_start_wait_sec=5): def restart_clickhouse(self, stop_start_wait_sec=5, kill=False):
if not self.stay_alive: if not self.stay_alive:
raise Exception("clickhouse can be restarted only with stay_alive=True instance") raise Exception("clickhouse can be restarted only with stay_alive=True instance")
self.exec_in_container(["bash", "-c", "pkill clickhouse"], user='root') self.exec_in_container(["bash", "-c", "pkill {} clickhouse".format("-9" if kill else "")], user='root')
time.sleep(stop_start_wait_sec) time.sleep(stop_start_wait_sec)
self.exec_in_container(["bash", "-c", "{} --daemon".format(CLICKHOUSE_START_COMMAND)], user=str(os.getuid())) self.exec_in_container(["bash", "-c", "{} --daemon".format(CLICKHOUSE_START_COMMAND)], user=str(os.getuid()))

View File

@ -3,6 +3,7 @@ import pytest
import random import random
import re import re
import string import string
import threading
import time import time
from multiprocessing.dummy import Pool from multiprocessing.dummy import Pool
from helpers.client import QueryRuntimeException from helpers.client import QueryRuntimeException
@ -15,6 +16,7 @@ node1 = cluster.add_instance('node1',
config_dir='configs', config_dir='configs',
main_configs=['configs/logs_config.xml'], main_configs=['configs/logs_config.xml'],
with_zookeeper=True, with_zookeeper=True,
stay_alive=True,
tmpfs=['/jbod1:size=40M', '/jbod2:size=40M', '/external:size=200M'], tmpfs=['/jbod1:size=40M', '/jbod2:size=40M', '/external:size=200M'],
macros={"shard": 0, "replica": 1} ) macros={"shard": 0, "replica": 1} )
@ -22,6 +24,7 @@ node2 = cluster.add_instance('node2',
config_dir='configs', config_dir='configs',
main_configs=['configs/logs_config.xml'], main_configs=['configs/logs_config.xml'],
with_zookeeper=True, with_zookeeper=True,
stay_alive=True,
tmpfs=['/jbod1:size=40M', '/jbod2:size=40M', '/external:size=200M'], tmpfs=['/jbod1:size=40M', '/jbod2:size=40M', '/external:size=200M'],
macros={"shard": 0, "replica": 2} ) macros={"shard": 0, "replica": 2} )
@ -1028,6 +1031,7 @@ def test_rename(start_cluster):
node1.query("DROP TABLE IF EXISTS default.renaming_table1") node1.query("DROP TABLE IF EXISTS default.renaming_table1")
node1.query("DROP TABLE IF EXISTS test.renaming_table2") node1.query("DROP TABLE IF EXISTS test.renaming_table2")
def test_freeze(start_cluster): def test_freeze(start_cluster):
try: try:
node1.query(""" node1.query("""
@ -1057,6 +1061,50 @@ def test_freeze(start_cluster):
node1.exec_in_container(["bash", "-c", "find /jbod1/shadow -name '*.mrk2' | grep '.*'"]) node1.exec_in_container(["bash", "-c", "find /jbod1/shadow -name '*.mrk2' | grep '.*'"])
node1.exec_in_container(["bash", "-c", "find /external/shadow -name '*.mrk2' | grep '.*'"]) node1.exec_in_container(["bash", "-c", "find /external/shadow -name '*.mrk2' | grep '.*'"])
finally: finally:
node1.query("DROP TABLE IF EXISTS default.freezing_table") node1.query("DROP TABLE IF EXISTS default.freezing_table")
def test_kill_while_insert(start_cluster):
try:
name = "test_kill_while_insert"
node1.query("DROP TABLE IF EXISTS {name}".format(name=name))
node1.query("""
CREATE TABLE {name} (
s String
) ENGINE = MergeTree
ORDER BY tuple()
SETTINGS storage_policy='small_jbod_with_external'
""".format(name=name))
data = []
dates = []
for i in range(10):
data.append(get_random_string(1024 * 1024)) # 1MB value
node1.query("INSERT INTO {name} VALUES {}".format(','.join(["('" + s + "')" for s in data]), name=name))
disks = get_used_disks_for_table(node1, name)
assert set(disks) == {"jbod1"}
start_time = time.time()
long_select = threading.Thread(target=node1.query, args=("SELECT sleep(3) FROM {name}".format(name=name),))
long_select.start()
time.sleep(0.5)
node1.query("ALTER TABLE {name} MOVE PARTITION tuple() TO DISK 'external'".format(name=name))
assert time.time() - start_time < 2
node1.restart_clickhouse(kill=True)
try:
long_select.join()
except:
""""""
time.sleep(0.5)
assert node1.query("SELECT count() FROM {name}".format(name=name)).splitlines() == ["10"]
finally:
"""Don't drop table afterwards to not shadow assertion."""

View File

@ -6,3 +6,8 @@ services:
environment: environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 1 MYSQL_ALLOW_EMPTY_PASSWORD: 1
command: --federated --socket /var/run/mysqld/mysqld.sock command: --federated --socket /var/run/mysqld/mysqld.sock
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
interval: 1s
timeout: 2s
retries: 100

View File

@ -15,6 +15,16 @@
<quota>default</quota> <quota>default</quota>
</default> </default>
<user_with_sha256>
<!-- echo -n abacaba | openssl dgst -sha256 !-->
<password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>
<networks incl="networks" replace="replace">
<ip>::/0</ip>
</networks>
<profile>default</profile>
<quota>default</quota>
</user_with_sha256>
<user_with_double_sha1> <user_with_double_sha1>
<!-- echo -n abacaba | openssl dgst -sha1 -binary | openssl dgst -sha1 !--> <!-- echo -n abacaba | openssl dgst -sha1 -binary | openssl dgst -sha1 !-->
<password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex> <password_double_sha1_hex>e395796d6546b1b65db9d665cd43f0e858dd4303</password_double_sha1_hex>

View File

@ -1,14 +1,14 @@
# coding: utf-8 # coding: utf-8
import docker
import datetime import datetime
import math import math
import os import os
import pytest import pytest
import subprocess import subprocess
import time import time
import docker
import pymysql.connections import pymysql.connections
from docker.models.containers import Container from docker.models.containers import Container
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster
@ -39,6 +39,25 @@ def mysql_client():
yield docker.from_env().containers.get(cluster.project_name + '_mysql1_1') yield docker.from_env().containers.get(cluster.project_name + '_mysql1_1')
@pytest.fixture(scope='module')
def mysql_server(mysql_client):
"""Return MySQL container when it is healthy.
:type mysql_client: Container
:rtype: Container
"""
retries = 30
for i in range(retries):
info = mysql_client.client.api.inspect_container(mysql_client.name)
if info['State']['Health']['Status'] == 'healthy':
break
time.sleep(1)
else:
raise Exception('Mysql server has not started in %d seconds.' % retries)
return mysql_client
@pytest.fixture(scope='module') @pytest.fixture(scope='module')
def golang_container(): def golang_container():
docker_compose = os.path.join(SCRIPT_DIR, 'clients', 'golang', 'docker_compose.yml') docker_compose = os.path.join(SCRIPT_DIR, 'clients', 'golang', 'docker_compose.yml')
@ -111,14 +130,14 @@ def test_mysql_client(mysql_client, server_address):
assert stdout == '\n'.join(['column', '0', '0', '1', '1', '5', '5', 'tmp_column', '0', '1', '']) assert stdout == '\n'.join(['column', '0', '0', '1', '1', '5', '5', 'tmp_column', '0', '1', ''])
def test_mysql_federated(mysql_client, server_address):
def test_mysql_federated(mysql_server, server_address):
node.query('''DROP DATABASE IF EXISTS mysql_federated''', settings={"password": "123"}) node.query('''DROP DATABASE IF EXISTS mysql_federated''', settings={"password": "123"})
node.query('''CREATE DATABASE mysql_federated''', settings={"password": "123"}) node.query('''CREATE DATABASE mysql_federated''', settings={"password": "123"})
node.query('''CREATE TABLE mysql_federated.test (col UInt32) ENGINE = Log''', settings={"password": "123"}) node.query('''CREATE TABLE mysql_federated.test (col UInt32) ENGINE = Log''', settings={"password": "123"})
node.query('''INSERT INTO mysql_federated.test VALUES (0), (1), (5)''', settings={"password": "123"}) node.query('''INSERT INTO mysql_federated.test VALUES (0), (1), (5)''', settings={"password": "123"})
code, (_, stderr) = mysql_server.exec_run('''
code, (_, stderr) = mysql_client.exec_run('''
mysql mysql
-e "DROP SERVER IF EXISTS clickhouse;" -e "DROP SERVER IF EXISTS clickhouse;"
-e "CREATE SERVER clickhouse FOREIGN DATA WRAPPER mysql OPTIONS (USER 'default', PASSWORD '123', HOST '{host}', PORT {port}, DATABASE 'mysql_federated');" -e "CREATE SERVER clickhouse FOREIGN DATA WRAPPER mysql OPTIONS (USER 'default', PASSWORD '123', HOST '{host}', PORT {port}, DATABASE 'mysql_federated');"
@ -128,7 +147,7 @@ def test_mysql_federated(mysql_client, server_address):
assert code == 0 assert code == 0
code, (stdout, stderr) = mysql_client.exec_run(''' code, (stdout, stderr) = mysql_server.exec_run('''
mysql mysql
-e "CREATE TABLE mysql_federated.test(`col` int UNSIGNED) ENGINE=FEDERATED CONNECTION='clickhouse';" -e "CREATE TABLE mysql_federated.test(`col` int UNSIGNED) ENGINE=FEDERATED CONNECTION='clickhouse';"
-e "SELECT * FROM mysql_federated.test ORDER BY col;" -e "SELECT * FROM mysql_federated.test ORDER BY col;"
@ -136,7 +155,7 @@ def test_mysql_federated(mysql_client, server_address):
assert stdout == '\n'.join(['col', '0', '1', '5', '']) assert stdout == '\n'.join(['col', '0', '1', '5', ''])
code, (stdout, stderr) = mysql_client.exec_run(''' code, (stdout, stderr) = mysql_server.exec_run('''
mysql mysql
-e "INSERT INTO mysql_federated.test VALUES (0), (1), (5);" -e "INSERT INTO mysql_federated.test VALUES (0), (1), (5);"
-e "SELECT * FROM mysql_federated.test ORDER BY col;" -e "SELECT * FROM mysql_federated.test ORDER BY col;"
@ -233,13 +252,12 @@ def test_php_client(server_address, php_container):
def test_mysqljs_client(server_address, nodejs_container): def test_mysqljs_client(server_address, nodejs_container):
code, (_, stderr) = nodejs_container.exec_run('node test.js {host} {port} default 123'.format(host=server_address, port=server_port), demux=True) code, (_, stderr) = nodejs_container.exec_run('node test.js {host} {port} user_with_sha256 abacaba'.format(host=server_address, port=server_port), demux=True)
assert code == 1 assert code == 1
assert 'MySQL is requesting the sha256_password authentication method, which is not supported.' in stderr assert 'MySQL is requesting the sha256_password authentication method, which is not supported.' in stderr
code, (_, stderr) = nodejs_container.exec_run('node test.js {host} {port} user_with_empty_password ""'.format(host=server_address, port=server_port), demux=True) code, (_, stderr) = nodejs_container.exec_run('node test.js {host} {port} user_with_empty_password ""'.format(host=server_address, port=server_port), demux=True)
assert code == 1 assert code == 0
assert 'MySQL is requesting the sha256_password authentication method, which is not supported.' in stderr
code, (_, _) = nodejs_container.exec_run('node test.js {host} {port} user_with_double_sha1 abacaba'.format(host=server_address, port=server_port), demux=True) code, (_, _) = nodejs_container.exec_run('node test.js {host} {port} user_with_double_sha1 abacaba'.format(host=server_address, port=server_port), demux=True)
assert code == 0 assert code == 0

View File

@ -1,8 +1,8 @@
111194.93 111195.05
111194.93 111195.05
110567.33 110567.33
111699.25 111699.25
10007543 10007554
10007543 10007554
10007543 10007554
10001780 10001780

View File

@ -0,0 +1,101 @@
1
-179 -0.06
-178 -0.02
-177 -0.02
-176 -0.01
-174 -0.01
174 -0.01
176 -0.01
177 -0.02
178 -0.02
179 -0.06
██
████
██████▏
████████▎
██████████▎
████████████▍
██████████████▍
████████████████▌
██████████████████▌
████████████████████▌
██████████████████████▋
████████████████████████▋
██████████████████████████▌
████████████████████████████▍
██████████████████████████████▍
████████████████████████████████▎
██████████████████████████████████▎
████████████████████████████████████▏
██████████████████████████████████████
███████████████████████████████████████▊
█████████████████████████████████████████▋
███████████████████████████████████████████▌
█████████████████████████████████████████████▎
███████████████████████████████████████████████
████████████████████████████████████████████████▋
██████████████████████████████████████████████████▌
████████████████████████████████████████████████████▏
█████████████████████████████████████████████████████▋
███████████████████████████████████████████████████████▍
█████████████████████████████████████████████████████████
██████████████████████████████████████████████████████████▌
████████████████████████████████████████████████████████████
█████████████████████████████████████████████████████████████▌
██████████████████████████████████████████████████████████████▊
████████████████████████████████████████████████████████████████▎
█████████████████████████████████████████████████████████████████▌
██████████████████████████████████████████████████████████████████▋
████████████████████████████████████████████████████████████████████
█████████████████████████████████████████████████████████████████████▏
██████████████████████████████████████████████████████████████████████▎
███████████████████████████████████████████████████████████████████████▎
████████████████████████████████████████████████████████████████████████▎
█████████████████████████████████████████████████████████████████████████▎
██████████████████████████████████████████████████████████████████████████▏
███████████████████████████████████████████████████████████████████████████
███████████████████████████████████████████████████████████████████████████▋
████████████████████████████████████████████████████████████████████████████▍
█████████████████████████████████████████████████████████████████████████████
█████████████████████████████████████████████████████████████████████████████▌
█████████████████████████████████████████████████████████████████████████████▊
██████████████████████████████████████████████████████████████████████████████▎
██████████████████████████████████████████████████████████████████████████████▌
██████████████████████████████████████████████████████████████████████████████▋
██████████████████████████████████████████████████████████████████████████████▊
██████████████████████████████████████████████████████████████████████████████▊
██████████████████████████████████████████████████████████████████████████████▋
██████████████████████████████████████████████████████████████████████████████▋
██████████████████████████████████████████████████████████████████████████████▍
██████████████████████████████████████████████████████████████████████████████
█████████████████████████████████████████████████████████████████████████████▌
█████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████▎
███████████████████████████████████████████████████████████████████████████▌
██████████████████████████████████████████████████████████████████████████▌
█████████████████████████████████████████████████████████████████████████▌
████████████████████████████████████████████████████████████████████████▎
███████████████████████████████████████████████████████████████████████
█████████████████████████████████████████████████████████████████████▋
████████████████████████████████████████████████████████████████████
██████████████████████████████████████████████████████████████████▍
████████████████████████████████████████████████████████████████▌
██████████████████████████████████████████████████████████████▌
████████████████████████████████████████████████████████████▍
██████████████████████████████████████████████████████████▏
███████████████████████████████████████████████████████▋
█████████████████████████████████████████████████████▏
██████████████████████████████████████████████████▍
███████████████████████████████████████████████▌
████████████████████████████████████████████▌
█████████████████████████████████████████▎
█████████████████████████████████████▊
██████████████████████████████████▍
██████████████████████████████▋
██████████████████████████▋
██████████████████████▋
██████████████████▌
██████████████▏
█████████▌
████▊

View File

@ -0,0 +1,3 @@
WITH number - 90 AS lat SELECT DISTINCT greatCircleAngle(0, 0, 0, lat) = abs(lat) FROM numbers(180);
WITH number - 180 AS lon SELECT lon, round(greatCircleAngle(0, 0, lon, 0) - abs(lon) AS err, 2) FROM numbers(360) WHERE abs(err) > 0.01;
SELECT bar((greatCircleAngle(0, 0, number, number) - number) * 100, 0, 2000, 100) FROM numbers(90);

View File

@ -1 +0,0 @@
../en/index.md

142
docs/ja/index.md Normal file
View File

@ -0,0 +1,142 @@
# ClickHouseとは?
ClickHouseは、クエリのオンライン分析処理OLAP用の列指向のデータベース管理システムDBMSです。
「通常の」行指向のDBMSでは、データは次の順序で保存されます。
| Row | WatchID | JavaEnable | Title | GoodEvent | EventTime |
| ------ | ------------------- | ---------- | ------------------ | --------- | ------------------- |
| #0 | 89354350662 | 1 | Investor Relations | 1 | 2016-05-18 05:19:20 |
| #1 | 90329509958 | 0 | Contact us | 1 | 2016-05-18 08:10:20 |
| #2 | 89953706054 | 1 | Mission | 1 | 2016-05-18 07:38:00 |
| #N | ... | ... | ... | ... | ... |
つまり、行に関連するすべての値は物理的に隣り合わせに格納されます。
行指向のDBMSの例MySQL, Postgres および MS SQL Server
{: .grey }
列指向のDBMSでは、データは次のように保存されます
| Row: | #0 | #1 | #2 | #N |
| ----------- | ------------------- | ------------------- | ------------------- | ------------------- |
| WatchID: | 89354350662 | 90329509958 | 89953706054 | ... |
| JavaEnable: | 1 | 0 | 1 | ... |
| Title: | Investor Relations | Contact us | Mission | ... |
| GoodEvent: | 1 | 1 | 1 | ... |
| EventTime: | 2016-05-18 05:19:20 | 2016-05-18 08:10:20 | 2016-05-18 07:38:00 | ... |
これらの例は、データが配置される順序のみを示しています。
異なる列の値は別々に保存され、同じ列のデータは一緒に保存されます。
列指向DBMSの例Vertica, Paraccel (Actian Matrix and Amazon Redshift), Sybase IQ, Exasol, Infobright, InfiniDB, MonetDB (VectorWise and Actian Vector), LucidDB, SAP HANA, Google Dremel, Google PowerDrill, Druid および kdb+
{: .grey }
異なったデータ格納の順序は、異なったシナリオにより適します。
データアクセスシナリオとは、クエリの実行内容、頻度、割合を指します。クエリで読み取られるの各種データの量(行、列、バイト)。データの読み取りと更新の関係。作業データのサイズとローカルでの使用方法。トランザクションが使用されるかどうか、およびそれらがどの程度分離されているか。データ複製と論理的整合性の要件。クエリの種類ごとの遅延とスループットの要件など。
システムの負荷が高いほど、使用シナリオの要件に一致するようにセットアップされたシステムをカスタマイズすることがより重要になり、このカスタマイズはより細かくなります。大きく異なるシナリオに等しく適したシステムはありません。システムがさまざまなシナリオに適応可能である場合、高負荷下では、システムはすべてのシナリオを同等に不十分に処理するか、1つまたはいくつかの可能なシナリオでうまく機能します。
## OLAPシナリオの主要なプロパティ
- リクエストの大部分は読み取りアクセス用である。
- データは、単一行ではなく、かなり大きなバッチ(> 1000行で更新されます。または、まったく更新されない。
- データはDBに追加されるが、変更されない。
- 読み取りの場合、非常に多くの行がDBから抽出されるが、一部の列のみ。
- テーブルは「幅が広く」、多数の列が含まれる。
- クエリは比較的まれ(通常、サーバーあたり毎秒数百あるいはそれ以下の数のクエリ)。
- 単純なクエリでは、約50ミリ秒の遅延が容認される。
- 列の値はかなり小さく、数値や短い文字列たとえば、URLごとに60バイト
- 単一のクエリを処理する場合、高いスループットが必要(サーバーあたり毎秒最大数十億行)。
- トランザクションは必要ない。
- データの一貫性の要件が低い。
- クエリごとに1つの大きなテーブルがある。 1つを除くすべてのテーブルは小さい。
- クエリ結果は、ソースデータよりも大幅に小さくなる。つまり、データはフィルター処理または集計されるため、結果は単一サーバーのRAMに収まる。
OLAPシナリオは、他の一般的なシナリオOLTPやKey-Valueアクセスなどとは非常に異なることが容易にわかります。 したがって、まともなパフォーマンスを得るには、OLTPまたはKey-Value DBを使用して分析クエリを処理しようとするのは無意味です。 たとえば、分析にMongoDBまたはRedisを使用しようとすると、OLAPデータベースに比べてパフォーマンスが非常に低下します。
## OLAPシナリオで列指向データベースがよりよく機能する理由
列指向データベースは、OLAPシナリオにより適しています。ほとんどのクエリの処理が少なくとも100倍高速です。 理由を以下に詳しく説明しますが、その根拠は視覚的に簡単に説明できます:
**行指向DBMS**
![Row-oriented](images/row_oriented.gif#)
**列指向DBMS**
![Column-oriented](images/column_oriented.gif#)
違いがわかりましたか?
### Input/output
1. 分析クエリでは、少数のテーブル列のみを読み取る必要があります。列指向のデータベースでは、必要なデータのみを読み取ることができます。たとえば、100のうち5つの列が必要な場合、I/Oが20倍削減されることが期待できます。
2. データはパケットで読み取られるため、圧縮が容易です。列のデータも圧縮が簡単です。これにより、I/Oボリュームがさらに削減されます。
3. I/Oの削減により、より多くのデータがシステムキャッシュに収まります。
たとえば、「各広告プラットフォームのレコード数をカウントする」クエリでは、1つの「広告プラットフォームID」列を読み取る必要がありますが、これは非圧縮では1バイトの領域を要します。トラフィックのほとんどが広告プラットフォームからのものではない場合、この列は少なくとも10倍の圧縮が期待できます。高速な圧縮アルゴリズムを使用すれば、1秒あたり少なくとも非圧縮データに換算して数ギガバイトの速度でデータを展開できます。つまり、このクエリは、単一のサーバーで1秒あたり約数十億行の速度で処理できます。この速度はまさに実際に達成されます。
<details markdown="1"><summary>Example</summary>
```
$ clickhouse-client
ClickHouse client version 0.0.52053.
Connecting to localhost:9000.
Connected to ClickHouse server version 0.0.52053.
:) SELECT CounterID, count() FROM hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20
SELECT
CounterID,
count()
FROM hits
GROUP BY CounterID
ORDER BY count() DESC
LIMIT 20
┌─CounterID─┬──count()─┐
│ 114208 │ 56057344 │
│ 115080 │ 51619590 │
│ 3228 │ 44658301 │
│ 38230 │ 42045932 │
│ 145263 │ 42042158 │
│ 91244 │ 38297270 │
│ 154139 │ 26647572 │
│ 150748 │ 24112755 │
│ 242232 │ 21302571 │
│ 338158 │ 13507087 │
│ 62180 │ 12229491 │
│ 82264 │ 12187441 │
│ 232261 │ 12148031 │
│ 146272 │ 11438516 │
│ 168777 │ 11403636 │
│ 4120072 │ 11227824 │
│ 10938808 │ 10519739 │
│ 74088 │ 9047015 │
│ 115079 │ 8837972 │
│ 337234 │ 8205961 │
└───────────┴──────────┘
20 rows in set. Elapsed: 0.153 sec. Processed 1.00 billion rows, 4.00 GB (6.53 billion rows/s., 26.10 GB/s.)
:)
```
</details>
### CPU
クエリを実行するには大量の行を処理する必要があるため、個別の行ではなくベクター全体のすべての操作をディスパッチするか、ディスパッチコストがほとんどないようにクエリエンジンを実装すると効率的です。 適切なディスクサブシステムでこれを行わないと、クエリインタープリターが必然的にCPUを失速させます。
データを列に格納し、可能な場合は列ごとに処理することは理にかなっています。
これを行うには2つの方法があります:
1. ベクトルエンジン。 すべての操作は、個別の値ではなく、ベクトルに対して記述されます。 これは、オペレーションを頻繁に呼び出す必要がなく、ディスパッチコストが無視できることを意味します。 操作コードには、最適化された内部サイクルが含まれています。
2. コード生成。 クエリ用に生成されたコードには、すべての間接的な呼び出しが含まれています。
これは、単純なクエリを実行する場合には意味がないため、「通常の」データベースでは実行されません。 ただし、例外があります。 たとえば、MemSQLはコード生成を使用して、SQLクエリを処理する際の遅延を減らします。 比較のために、分析DBMSではレイテンシではなくスループットの最適化が必要です。
CPU効率のために、クエリ言語は宣言型SQLまたはMDX、または少なくともベクトルJ、Kでなければなりません。 クエリには、最適化を可能にする暗黙的なループのみを含める必要があります。
[Original article](https://clickhouse.yandex/docs/ja/) <!--hide-->

View File

@ -52,11 +52,11 @@
### 1.8. Перенос между разделами по TTL. ### 1.8. Перенос между разделами по TTL.
Делает [Владимир Чеботарёв](https://github.com/excitoon), Altinity. Делает [Владимир Чеботарёв](https://github.com/excitoon), Altinity. Декабрь 2019.
### 1.9. Использование TTL для прореживания данных. ### 1.9. Использование TTL для прореживания данных.
В очереди. Будет делать Сорокин Николай, ВШЭ и Яндекс.
Сейчас пользователь может задать в таблице выражение, которое определяет, сколько времени хранятся данные. Обычно это выражение задаётся относительно значения столбца с датой - например: удалять данные через три месяца. https://clickhouse.yandex/docs/ru/operations/table_engines/mergetree/#table_engine-mergetree-ttl Сейчас пользователь может задать в таблице выражение, которое определяет, сколько времени хранятся данные. Обычно это выражение задаётся относительно значения столбца с датой - например: удалять данные через три месяца. https://clickhouse.yandex/docs/ru/operations/table_engines/mergetree/#table_engine-mergetree-ttl
@ -160,7 +160,7 @@ ClickHouse использует небольшое подмножество фу
### 2.9. Логгировние в format-стиле. ### 2.9. Логгировние в format-стиле.
В задаче заинтересован [Александр Кузьменков](https://github.com/akuzm). Нет прогресса. Делает [Иван Лежанкин](https://github.com/abyss7). Низкий приоритет.
### 2.10. Запрашивать у таблиц не столбцы, а срезы. ### 2.10. Запрашивать у таблиц не столбцы, а срезы.
@ -205,7 +205,7 @@ ClickHouse использует небольшое подмножество фу
### 3.4. Добавить японский язык в документацию. ### 3.4. Добавить японский язык в документацию.
Эту задачу сделает [Иван Блинков](https://github.com/blinkov/), до конца ноября 2019. Эту задачу сделает [Иван Блинков](https://github.com/blinkov/), до конца декабря 2019.
## 4. Сетевое взаимодействие. ## 4. Сетевое взаимодействие.
@ -257,7 +257,7 @@ ClickHouse использует небольшое подмножество фу
### 5.3. Встроенная ручка для Prometheus и, возможно, Solomon. ### 5.3. Встроенная ручка для Prometheus и, возможно, Solomon.
Простая задача. Простая задача. https://github.com/Vdimir
### 5.4. Opt-in сообщать в клиенте, если вышла новая версия. ### 5.4. Opt-in сообщать в клиенте, если вышла новая версия.
@ -270,7 +270,8 @@ ClickHouse использует небольшое подмножество фу
### 6.1. Исправления сэмплирующего профайлера запросов. ### 6.1. Исправления сэмплирующего профайлера запросов.
Михаил Филимонов, Altinity. Ноябрь 2019. Михаил Филимонов, Altinity. Ноябрь 2019. Сделано.
Осталось ещё проверить работоспособность профайлера в первом потоке (что важно для INSERT).
### 6.2. Добавление memory profiler. ### 6.2. Добавление memory profiler.
@ -364,6 +365,7 @@ UBSan включен в функциональных тестах, но не в
Мы используем -Wall -Wextra -Weverything -Werror. Мы используем -Wall -Wextra -Weverything -Werror.
При сборке с clang, -Weverything уже включено. Но в gcc есть уникальные warning-и, отсутствующие в clang. При сборке с clang, -Weverything уже включено. Но в gcc есть уникальные warning-и, отсутствующие в clang.
Wolf Kreuzerkrieg. Возможно, его уже не интересует эта задача. Wolf Kreuzerkrieg. Возможно, его уже не интересует эта задача.
Низкий приоритет. Возможно, будет отменено.
### 7.14. Альтернатива для readline и libedit. ### 7.14. Альтернатива для readline и libedit.
@ -562,7 +564,7 @@ Fuzzing тестирование - это тестирование случай
### 8.4. Унификация File, HDFS, S3 под URL. ### 8.4. Унификация File, HDFS, S3 под URL.
### 8.5. Аутентификация в S3. ### 8.5. + Аутентификация в S3.
[Владимир Чеботарёв](https://github.com/excitoon), Altinity. [Владимир Чеботарёв](https://github.com/excitoon), Altinity.
@ -570,16 +572,18 @@ Fuzzing тестирование - это тестирование случай
Андрей Коняев, ArenaData. Андрей Коняев, ArenaData.
### 8.7. Исправление мелочи HDFS на очень старых ядрах Linux. ### 8.7. + Исправление мелочи HDFS на очень старых ядрах Linux.
В ядрах 2.6 отсутствует один системный вызов, который библиотека hdfs3 использует без необходимости. В ядрах 2.6 отсутствует один системный вызов, который библиотека hdfs3 использует без необходимости.
Тривиально, но исполнителя ещё нет. Сделал Amos Bird.
### 8.8. Поддержка виртуальных столбцов с именем файла и путём. ### 8.8. Поддержка виртуальных столбцов с именем файла и путём.
[Ольга Хвостикова](https://github.com/stavrolia). [Ольга Хвостикова](https://github.com/stavrolia).
### 8.9. Поддержка сжатых файлов (gz, bz) на чтение и запись. ### 8.9. + Поддержка сжатых файлов (gz, bz) на чтение и запись.
Сделал [Andrey Bodrov](https://github.com/apbodrov)
### 8.10. Запись в табличную функцию ODBC. ### 8.10. Запись в табличную функцию ODBC.
@ -617,7 +621,9 @@ Fuzzing тестирование - это тестирование случай
Встроенная в ClickHouse возможность работать в качестве реплики MySQL даст преимущества для дальнейшего развития. Встроенная в ClickHouse возможность работать в качестве реплики MySQL даст преимущества для дальнейшего развития.
### 8.18. ClickHouse как Federated MySQL. ### 8.18. + ClickHouse как Federated MySQL.
Maxim Fedotov, Wargaming + Yuri Baranov, Яндекс.
### 8.19. Интеграция с RabbitMQ. ### 8.19. Интеграция с RabbitMQ.
@ -642,7 +648,7 @@ Fuzzing тестирование - это тестирование случай
## 9. Безопасность. ## 9. Безопасность.
### 9.1. Ограничение на хосты в запросах ко внешним системам. ### 9.1. + Ограничение на хосты в запросах ко внешним системам.
Михаил Коротов. Михаил Коротов.
@ -760,7 +766,7 @@ ClickHouse предоставляет возможность обратитьс
### 11.9. Доработки ODBC драйвера. ### 11.9. Доработки ODBC драйвера.
Денис Глазачев, Altinity. Денис Глазачев, Altinity. Хороший прогресс по этой задаче.
### 11.10. Преднастроенные HTTP handlers для запросов. ### 11.10. Преднастроенные HTTP handlers для запросов.
@ -873,7 +879,9 @@ zhang2014
### 14.17. Ввести понятие stateful функций. ### 14.17. Ввести понятие stateful функций.
zhang2014.
Для runningDifference, neighbour - их учёт в оптимизаторе запросов. Для runningDifference, neighbour - их учёт в оптимизаторе запросов.
В интерфейсе уже сделано. Надо проверить, что учитывается в нужных местах (например, что работает predicate pushdown сквозь ORDER BY, если таких функций нет).
### 14.18. UNION DISTINCT и возможность включить его по-умолчанию. ### 14.18. UNION DISTINCT и возможность включить его по-умолчанию.
@ -911,7 +919,7 @@ zhang2014
### 15.5. Использование ключа таблицы для оптимизации merge JOIN. ### 15.5. Использование ключа таблицы для оптимизации merge JOIN.
### 15.6. SEMI и ANTI JOIN. ### 15.6. + SEMI и ANTI JOIN.
Артём Зуйков. Артём Зуйков.
@ -954,7 +962,7 @@ ClickHouse не является geospatial СУБД. Тем не менее, в
Реализовать в ClickHouse типы данных для задач обработки геоинформационных данных: Point, Line, MultiLine, Polygon и операции над ними - проверка вхождения, пересечения. Вариантом минимум будет реализация этих операций в евклидовой системе координат. Дополнительно - на сфере и WGS84. Реализовать в ClickHouse типы данных для задач обработки геоинформационных данных: Point, Line, MultiLine, Polygon и операции над ними - проверка вхождения, пересечения. Вариантом минимум будет реализация этих операций в евклидовой системе координат. Дополнительно - на сфере и WGS84.
### 17.3. Ускорение greatCircleDistance. ### 17.3. + Ускорение greatCircleDistance.
[Ольга Хвостикова](https://github.com/stavrolia), основано на коде Андрея Аксёнова, получено разрешение на использование кода. [Ольга Хвостикова](https://github.com/stavrolia), основано на коде Андрея Аксёнова, получено разрешение на использование кода.
@ -1050,9 +1058,9 @@ Hold. Полезно для заказчиков внутри Яндекса, н
## 21. Оптимизации производительности. ## 21. Оптимизации производительности.
### 21.1. Параллельный парсинг форматов. ### 21.1. + Параллельный парсинг форматов.
Начинал Олег Ершов, доделывает Никита Михайлов, помогает [Александр Кузьменков](https://github.com/akuzm). Почти всё готово. Начинал Олег Ершов, доделывает Никита Михайлов, помогает [Александр Кузьменков](https://github.com/akuzm). Готово.
### 21.2. Параллельное форматирование форматов. ### 21.2. Параллельное форматирование форматов.
@ -1100,7 +1108,7 @@ Hold. Полезно для заказчиков внутри Яндекса, н
[Николай Кочетов](https://github.com/KochetovNicolai). Требует 2.1. [Николай Кочетов](https://github.com/KochetovNicolai). Требует 2.1.
### 21.10. Улучшение эвристики PREWHERE. ### 21.10. + Улучшение эвристики PREWHERE.
Amos Bird. Amos Bird.
@ -1237,29 +1245,32 @@ zhang2014.
Требует 6.3., но можно улучшить отдельными хаками. Нужно Метрике и БК. Требует 6.3., но можно улучшить отдельными хаками. Нужно Метрике и БК.
### 22.11. Более простая ser/de настроек запросов. ### 22.11. + Более простая ser/de настроек запросов.
[Виталий Баранов](https://github.com/vitlibar), почти всё готово. И пропуск неизвестных настроек. Важно для Метрики для упрощения апгрейда без изменения конфига.
[Виталий Баранов](https://github.com/vitlibar), готово.
### 22.12. Исправление низкой производительности чтения из Kafka. ### 22.12. + Исправление низкой производительности чтения из Kafka.
[Иван Лежанкин](https://github.com/abyss7).
Для ClickHouse нехарактерно наличие кода, обладающего столь низкой производительностью. Практики разработки не подразумевают, что такой код должен попасть в продакшен без надлежащего тестирования производительности. Для ClickHouse нехарактерно наличие кода, обладающего столь низкой производительностью. Практики разработки не подразумевают, что такой код должен попасть в продакшен без надлежащего тестирования производительности.
Изначально было назначено на [Ивана Лежанкина](https://github.com/abyss7), но по неизвестной причине было не сделано в течение нескольких месяцев.
Сделал Михаил Филимонов, Altinity.
### 22.13. Посмотреть, почему не работают некоторые collations. ### 22.13. Посмотреть, почему не работают некоторые collations.
[Иван Лежанкин](https://github.com/abyss7), совмещается с 7.1. Изначально было назначено на [Ивана Лежанкина](https://github.com/abyss7), но в результате сделал Александр Сапин.
### 22.14. Посмотреть, почему не работает StorageSet для MergeTree таблиц при некоторых условиях. ### 22.14. Посмотреть, почему не работает StorageSet для MergeTree таблиц при некоторых условиях.
### 22.15. Нормализация коммитов в Kafka и идемпотентности операций. ### 22.15. Нормализация коммитов в Kafka и идемпотентности операций.
[Иван Лежанкин](https://github.com/abyss7), если он не сдастся. [Иван Лежанкин](https://github.com/abyss7), если он не сдастся.
### 22.16. Исправление низкой производительности кодека DoubleDelta. ### 22.16. Исправление низкой производительности кодека DoubleDelta.
Василий Немков, Altinity - временно приостановлено, но намерения остаются в силе. Василий Немков, Altinity - в процессе.
Мы считаем важным, что код в ClickHouse содержит разумные оптимизации, основанные на анализе производительности. Но иногда бывают досадные исключения. Мы считаем важным, что код в ClickHouse содержит разумные оптимизации, основанные на анализе производительности. Но иногда бывают досадные исключения.
@ -1269,11 +1280,11 @@ zhang2014.
Василий Немков, Altinity. Василий Немков, Altinity.
### 22.19. Одновременное использование SAMPLE и PREWHERE. ### 22.19. + Одновременное использование SAMPLE и PREWHERE.
Нужно для Метрики. [Николай Кочетов](https://github.com/KochetovNicolai), ноябрь 2019. Нужно для Метрики. [Николай Кочетов](https://github.com/KochetovNicolai), ноябрь 2019.
### 22.20. Неправильная работа PREWHERE при некоторых условиях. ### 22.20. + Неправильная работа PREWHERE при некоторых условиях.
[Николай Кочетов](https://github.com/KochetovNicolai), декабрь 2019. [Николай Кочетов](https://github.com/KochetovNicolai), декабрь 2019.
@ -1310,6 +1321,10 @@ https://github.com/ClickHouse/ClickHouse/issues/2655
[Иван Лежанкин](https://github.com/abyss7). [Иван Лежанкин](https://github.com/abyss7).
### 22.29. Уязвимость DDL для словарей executable.
[Александр Сапин](https://github.com/alesapin)
## 23. Default Festival. ## 23. Default Festival.
@ -1582,13 +1597,14 @@ Amos Bird, но его решение слишком громоздкое и п
## 25. DevRel ## 25. DevRel
### 25.1. Перевод инструкции для начинающих разработчиков. ### 25.1. + Перевод инструкции для начинающих разработчиков.
Александр Казаков, ноябрь 2019. Александр Казаков, ноябрь 2019.
### 25.2. Вычитка и выкладка статьи про обфускацию данных на английском. ### 25.2. Вычитка и выкладка статьи про обфускацию данных на английском.
Эми, Александр Казаков, Алексей Миловидов, ноябрь 2019. Эми, Александр Казаков, Алексей Миловидов, ноябрь 2019.
Готово к выкладке.
### 25.3. Подготовка статьи "Секреты оптимизации производительности ClickHouse". ### 25.3. Подготовка статьи "Секреты оптимизации производительности ClickHouse".
@ -1608,7 +1624,7 @@ Amos Bird, но его решение слишком громоздкое и п
Эми Эми
### 25.8. Выступление keynote на BDTC. ### 25.8. + Выступление keynote на BDTC.
Алексей Миловидов Алексей Миловидов

View File

@ -45,6 +45,9 @@ def build_for_lang(lang, args):
os.environ['SINGLE_PAGE'] = '0' os.environ['SINGLE_PAGE'] = '0'
config_path = os.path.join(args.docs_dir, 'toc_%s.yml' % lang) config_path = os.path.join(args.docs_dir, 'toc_%s.yml' % lang)
if args.is_stable_release and not os.path.exists(config_path):
logging.warn('Skipping %s docs, because %s does not exist' % (lang, config_path))
return
try: try:
theme_cfg = { theme_cfg = {
@ -249,6 +252,7 @@ if __name__ == '__main__':
arg_parser.add_argument('--output-dir', default='build') arg_parser.add_argument('--output-dir', default='build')
arg_parser.add_argument('--enable-stable-releases', action='store_true') arg_parser.add_argument('--enable-stable-releases', action='store_true')
arg_parser.add_argument('--version-prefix', type=str, default='') arg_parser.add_argument('--version-prefix', type=str, default='')
arg_parser.add_argument('--is-stable-release', action='store_true')
arg_parser.add_argument('--skip-single-page', action='store_true') arg_parser.add_argument('--skip-single-page', action='store_true')
arg_parser.add_argument('--skip-pdf', action='store_true') arg_parser.add_argument('--skip-pdf', action='store_true')
arg_parser.add_argument('--skip-website', action='store_true') arg_parser.add_argument('--skip-website', action='store_true')
@ -260,8 +264,6 @@ if __name__ == '__main__':
from github import choose_latest_releases from github import choose_latest_releases
args.stable_releases = choose_latest_releases() if args.enable_stable_releases else [] args.stable_releases = choose_latest_releases() if args.enable_stable_releases else []
logging.basicConfig( logging.basicConfig(
level=logging.DEBUG if args.verbose else logging.INFO, level=logging.DEBUG if args.verbose else logging.INFO,

View File

@ -15,7 +15,7 @@ def choose_latest_releases():
candidates = requests.get('https://api.github.com/repos/ClickHouse/ClickHouse/tags?per_page=100').json() candidates = requests.get('https://api.github.com/repos/ClickHouse/ClickHouse/tags?per_page=100').json()
for tag in candidates: for tag in candidates:
name = tag.get('name', '') name = tag.get('name', '')
if 'v18' in name or 'stable' not in name: if ('v18' in name) or ('stable' not in name) or ('prestable' in name):
continue continue
major_version = '.'.join((name.split('.', 2))[:2]) major_version = '.'.join((name.split('.', 2))[:2])
if major_version not in seen: if major_version not in seen:
@ -33,6 +33,7 @@ def process_release(args, callback, release):
tar.extractall(base_dir) tar.extractall(base_dir)
args = copy.deepcopy(args) args = copy.deepcopy(args)
args.version_prefix = name args.version_prefix = name
args.is_stable_release = True
args.docs_dir = os.path.join(base_dir, os.listdir(base_dir)[0], 'docs') args.docs_dir = os.path.join(base_dir, os.listdir(base_dir)[0], 'docs')
callback(args) callback(args)

View File

@ -31,6 +31,9 @@ if (GLIBC_COMPATIBILITY)
list(APPEND glibc_compatibility_sources libcxxabi/cxa_thread_atexit.cpp) list(APPEND glibc_compatibility_sources libcxxabi/cxa_thread_atexit.cpp)
endif() endif()
# Need to omit frame pointers to match the performance of glibc
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer")
add_library(glibc-compatibility STATIC ${glibc_compatibility_sources}) add_library(glibc-compatibility STATIC ${glibc_compatibility_sources})
target_include_directories(glibc-compatibility PRIVATE libcxxabi ${musl_arch_include_dir}) target_include_directories(glibc-compatibility PRIVATE libcxxabi ${musl_arch_include_dir})

View File

@ -0,0 +1,3 @@
#define VDSO_USEFUL
#define VDSO_CGT_SYM "__kernel_clock_gettime"
#define VDSO_CGT_VER "LINUX_2.6.39"

View File

@ -0,0 +1,108 @@
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include "atomic.h"
#include "musl_features.h"
#include "syscall.h"
#ifdef VDSO_CGT_SYM
static void *volatile vdso_func;
#ifdef VDSO_CGT32_SYM
static void *volatile vdso_func_32;
static int cgt_time32_wrap(clockid_t clk, struct timespec *ts)
{
long ts32[2];
int (*f)(clockid_t, long[2]) =
(int (*)(clockid_t, long[2]))vdso_func_32;
int r = f(clk, ts32);
if (!r) {
/* Fallback to syscalls if time32 overflowed. Maybe
* we lucked out and somehow migrated to a kernel with
* time64 syscalls available. */
if (ts32[0] < 0) {
a_cas_p(&vdso_func, (void *)cgt_time32_wrap, 0);
return -ENOSYS;
}
ts->tv_sec = ts32[0];
ts->tv_nsec = ts32[1];
}
return r;
}
#endif
static int cgt_init(clockid_t clk, struct timespec *ts)
{
void *p = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM);
#ifdef VDSO_CGT32_SYM
if (!p) {
void *q = __vdsosym(VDSO_CGT32_VER, VDSO_CGT32_SYM);
if (q) {
a_cas_p(&vdso_func_32, 0, q);
p = cgt_time32_wrap;
}
}
#endif
int (*f)(clockid_t, struct timespec *) =
(int (*)(clockid_t, struct timespec *))p;
a_cas_p(&vdso_func, (void *)cgt_init, p);
return f ? f(clk, ts) : -ENOSYS;
}
static void *volatile vdso_func = (void *)cgt_init;
#endif
int __clock_gettime(clockid_t clk, struct timespec *ts)
{
int r;
#ifdef VDSO_CGT_SYM
int (*f)(clockid_t, struct timespec *) =
(int (*)(clockid_t, struct timespec *))vdso_func;
if (f) {
r = f(clk, ts);
if (!r) return r;
if (r == -EINVAL) return __syscall_ret(r);
/* Fall through on errors other than EINVAL. Some buggy
* vdso implementations return ENOSYS for clocks they
* can't handle, rather than making the syscall. This
* also handles the case where cgt_init fails to find
* a vdso function to use. */
}
#endif
#ifdef SYS_clock_gettime64
r = -ENOSYS;
if (sizeof(time_t) > 4)
r = __syscall(SYS_clock_gettime64, clk, ts);
if (SYS_clock_gettime == SYS_clock_gettime64 || r!=-ENOSYS)
return __syscall_ret(r);
long ts32[2];
r = __syscall(SYS_clock_gettime, clk, ts32);
if (r==-ENOSYS && clk==CLOCK_REALTIME) {
r = __syscall(SYS_gettimeofday, ts32, 0);
ts32[1] *= 1000;
}
if (!r) {
ts->tv_sec = ts32[0];
ts->tv_nsec = ts32[1];
return r;
}
return __syscall_ret(r);
#else
r = __syscall(SYS_clock_gettime, clk, ts);
if (r == -ENOSYS) {
if (clk == CLOCK_REALTIME) {
__syscall(SYS_gettimeofday, ts, 0);
ts->tv_nsec = (int)ts->tv_nsec * 1000;
return 0;
}
r = -EINVAL;
}
return __syscall_ret(r);
#endif
}
weak_alias(__clock_gettime, clock_gettime);

View File

@ -0,0 +1,27 @@
#include <errno.h>
#include <pthread.h>
#include <time.h>
#include "musl_features.h"
#include "syscall.h"
int __clock_nanosleep(clockid_t clk, int flags, const struct timespec * req, struct timespec * rem)
{
if (clk == CLOCK_THREAD_CPUTIME_ID)
return EINVAL;
int old_cancel_type;
int status;
/// We cannot port __syscall_cp because musl has very limited cancellation point implementation.
/// For example, c++ destructors won't get called and exception unwinding isn't implemented.
/// Instead, we use normal __syscall here and turn on the asynchrous cancel mode to allow
/// cancel. This works because nanosleep doesn't contain any resource allocations or
/// deallocations.
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_cancel_type);
if (clk == CLOCK_REALTIME && !flags)
status = -__syscall(SYS_nanosleep, req, rem);
else
status = -__syscall(SYS_clock_nanosleep, clk, flags, req, rem);
pthread_setcanceltype(old_cancel_type, NULL);
return status;
}
weak_alias(__clock_nanosleep, clock_nanosleep);

View File

@ -1,14 +1,11 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <errno.h> #include <errno.h>
#include <sched.h> #include <sched.h>
#include <syscall.h>
#include "syscall.h" #include "syscall.h"
#include "atomic.h" #include "atomic.h"
#ifdef VDSO_GETCPU_SYM #ifdef VDSO_GETCPU_SYM
void *__vdsosym(const char *, const char *);
static void *volatile vdso_func; static void *volatile vdso_func;
typedef long (*getcpu_f)(unsigned *, unsigned *, void *); typedef long (*getcpu_f)(unsigned *, unsigned *, void *);

View File

@ -1,5 +1,8 @@
#pragma once #pragma once
#include <sys/syscall.h>
#include <syscall_arch.h>
typedef long syscall_arg_t; typedef long syscall_arg_t;
__attribute__((visibility("hidden"))) __attribute__((visibility("hidden")))
@ -7,3 +10,6 @@ long __syscall_ret(unsigned long);
__attribute__((visibility("hidden"))) __attribute__((visibility("hidden")))
long __syscall(syscall_arg_t, ...); long __syscall(syscall_arg_t, ...);
__attribute__((visibility("hidden")))
void *__vdsosym(const char *, const char *);

View File

@ -0,0 +1,105 @@
#include <elf.h>
#include <link.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include <sys/auxv.h>
#include "syscall.h"
#ifdef VDSO_USEFUL
#if ULONG_MAX == 0xffffffff
typedef Elf32_Ehdr Ehdr;
typedef Elf32_Phdr Phdr;
typedef Elf32_Sym Sym;
typedef Elf32_Verdef Verdef;
typedef Elf32_Verdaux Verdaux;
#else
typedef Elf64_Ehdr Ehdr;
typedef Elf64_Phdr Phdr;
typedef Elf64_Sym Sym;
typedef Elf64_Verdef Verdef;
typedef Elf64_Verdaux Verdaux;
#endif
static int checkver(Verdef *def, int vsym, const char *vername, char *strings)
{
vsym &= 0x7fff;
for (;;) {
if (!(def->vd_flags & VER_FLG_BASE)
&& (def->vd_ndx & 0x7fff) == vsym)
break;
if (def->vd_next == 0)
return 0;
def = (Verdef *)((char *)def + def->vd_next);
}
Verdaux *aux = (Verdaux *)((char *)def + def->vd_aux);
return !strcmp(vername, strings + aux->vda_name);
}
#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON)
#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE)
extern char** environ;
static Ehdr *eh = NULL;
void *__vdsosym(const char *vername, const char *name);
// We don't have libc struct available here. Compute aux vector manually.
__attribute__((constructor)) static void auxv_init()
{
size_t i, *auxv;
for (i=0; environ[i]; i++);
auxv = (void *)(environ+i+1);
for (i=0; auxv[i] != AT_SYSINFO_EHDR; i+=2)
if (!auxv[i]) return;
if (!auxv[i+1]) return;
eh = (void *)auxv[i+1];
}
void *__vdsosym(const char *vername, const char *name)
{
size_t i;
if (!eh) return 0;
Phdr *ph = (void *)((char *)eh + eh->e_phoff);
size_t *dynv=0, base=-1;
for (i=0; i<eh->e_phnum; i++, ph=(void *)((char *)ph+eh->e_phentsize)) {
if (ph->p_type == PT_LOAD)
base = (size_t)eh + ph->p_offset - ph->p_vaddr;
else if (ph->p_type == PT_DYNAMIC)
dynv = (void *)((char *)eh + ph->p_offset);
}
if (!dynv || base==(size_t)-1) return 0;
char *strings = 0;
Sym *syms = 0;
Elf_Symndx *hashtab = 0;
uint16_t *versym = 0;
Verdef *verdef = 0;
for (i=0; dynv[i]; i+=2) {
void *p = (void *)(base + dynv[i+1]);
switch(dynv[i]) {
case DT_STRTAB: strings = p; break;
case DT_SYMTAB: syms = p; break;
case DT_HASH: hashtab = p; break;
case DT_VERSYM: versym = p; break;
case DT_VERDEF: verdef = p; break;
}
}
if (!strings || !syms || !hashtab) return 0;
if (!verdef) versym = 0;
for (i=0; i<hashtab[1]; i++) {
if (!(1<<(syms[i].st_info&0xf) & OK_TYPES)) continue;
if (!(1<<(syms[i].st_info>>4) & OK_BINDS)) continue;
if (!syms[i].st_shndx) continue;
if (strcmp(name, strings+syms[i].st_name)) continue;
if (versym && !checkver(verdef, versym[i], vername, strings))
continue;
return (void *)(base + syms[i].st_value);
}
return 0;
}
#endif

View File

@ -0,0 +1,5 @@
#define VDSO_USEFUL
#define VDSO_CGT_SYM "__vdso_clock_gettime"
#define VDSO_CGT_VER "LINUX_2.6"
#define VDSO_GETCPU_SYM "__vdso_getcpu"
#define VDSO_GETCPU_VER "LINUX_2.6"