Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
2dot5
ClickHouse
提交
ea734b6b
C
ClickHouse
项目概览
2dot5
/
ClickHouse
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
ClickHouse
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
ea734b6b
编写于
7月 30, 2016
作者:
A
Alexey Milovidov
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Removed compatibility layer with OLAPServer [#METR-2944].
上级
c6da35b7
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
12 addition
and
2328 deletion
+12
-2328
dbms/CMakeLists.txt
dbms/CMakeLists.txt
+0
-4
dbms/include/DB/Interpreters/Context.h
dbms/include/DB/Interpreters/Context.h
+0
-1
dbms/src/Server/CMakeLists.txt
dbms/src/Server/CMakeLists.txt
+0
-3
dbms/src/Server/OLAPAttributesMetadata.h
dbms/src/Server/OLAPAttributesMetadata.h
+0
-805
dbms/src/Server/OLAPHTTPHandler.cpp
dbms/src/Server/OLAPHTTPHandler.cpp
+0
-138
dbms/src/Server/OLAPHTTPHandler.h
dbms/src/Server/OLAPHTTPHandler.h
+0
-32
dbms/src/Server/OLAPQueryConverter.cpp
dbms/src/Server/OLAPQueryConverter.cpp
+0
-739
dbms/src/Server/OLAPQueryConverter.h
dbms/src/Server/OLAPQueryConverter.h
+0
-87
dbms/src/Server/OLAPQueryParser.cpp
dbms/src/Server/OLAPQueryParser.cpp
+0
-341
dbms/src/Server/OLAPQueryParser.h
dbms/src/Server/OLAPQueryParser.h
+0
-135
dbms/src/Server/Server.cpp
dbms/src/Server/Server.cpp
+0
-23
dbms/src/Server/Server.h
dbms/src/Server/Server.h
+9
-14
dbms/src/Server/TCPHandler.cpp
dbms/src/Server/TCPHandler.cpp
+1
-4
libs/libcommon/include/common/DateLUTImpl.h
libs/libcommon/include/common/DateLUTImpl.h
+2
-2
未找到文件。
dbms/CMakeLists.txt
浏览文件 @
ea734b6b
...
...
@@ -16,14 +16,10 @@ else()
endif
()
add_library
(
dbms
src/Server/OLAPAttributesMetadata.h
src/Server/InterserverIOHTTPHandler.h
src/Server/OLAPHTTPHandler.h
src/Server/OLAPQueryConverter.h
src/Server/Server.h
src/Server/TCPHandler.h
src/Server/HTTPHandler.h
src/Server/OLAPQueryParser.h
src/Server/MetricsTransmitter.h
src/Server/UsersConfigReloader.h
src/Server/StatusFile.h
...
...
dbms/include/DB/Interpreters/Context.h
浏览文件 @
ea734b6b
...
...
@@ -67,7 +67,6 @@ public:
{
TCP
=
1
,
HTTP
=
2
,
OLAP_HTTP
=
3
,
};
enum
class
HTTPMethod
...
...
dbms/src/Server/CMakeLists.txt
浏览文件 @
ea734b6b
...
...
@@ -3,9 +3,6 @@ add_executable(clickhouse-server
HTTPHandler.cpp
TCPHandler.cpp
InterserverIOHTTPHandler.cpp
OLAPHTTPHandler.cpp
OLAPQueryParser.cpp
OLAPQueryConverter.cpp
MetricsTransmitter.cpp
UsersConfigReloader.cpp
StatusFile.cpp
...
...
dbms/src/Server/OLAPAttributesMetadata.h
已删除
100644 → 0
浏览文件 @
c6da35b7
#pragma once
#include <math.h> // log2()
#include <openssl/md5.h>
#include <boost/algorithm/string.hpp>
#include <Poco/StringTokenizer.h>
#include <Poco/ByteOrder.h>
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/ReadHelpers.h>
#include <common/DateLUT.h>
#include <DB/Dictionaries/Embedded/RegionsHierarchy.h>
#include <DB/Dictionaries/Embedded/TechDataHierarchy.h>
/// Код в основном взят из из OLAP-server. Здесь нужен только для парсинга значений атрибутов.
namespace
DB
{
namespace
OLAP
{
using
BinaryData
=
Int64
;
/** Информация о типе атрибута */
struct
IAttributeMetadata
{
/// получение значения из строки в запросе
virtual
BinaryData
parse
(
const
std
::
string
&
s
)
const
=
0
;
virtual
~
IAttributeMetadata
()
{}
};
/// атрибут - заглушка, всегда равен нулю, подходит для подстановки в агрегатную функцию count
struct
DummyAttribute
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
0
;
}
};
/// базовый класс для атрибутов, которые являются просто UInt8, UInt16, UInt32 или UInt64 (таких тоже много)
struct
AttributeUIntBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
static_cast
<
BinaryData
>
(
DB
::
parse
<
UInt64
>
(
s
));
}
};
/// базовый класс для атрибутов, которые являются Int8, Int16, Int32 или Int64
struct
AttributeIntBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
Int64
>
(
s
);
}
};
/** Базовые классы для атрибутов, получаемых из времени (unix timestamp, 4 байта) */
struct
AttributeDateTimeBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
struct
tm
tm
;
memset
(
&
tm
,
0
,
sizeof
(
tm
));
sscanf
(
s
.
c_str
(),
"%04d-%02d-%02d %02d:%02d:%02d"
,
&
tm
.
tm_year
,
&
tm
.
tm_mon
,
&
tm
.
tm_mday
,
&
tm
.
tm_hour
,
&
tm
.
tm_min
,
&
tm
.
tm_sec
);
tm
.
tm_mon
--
;
tm
.
tm_year
-=
1900
;
tm
.
tm_isdst
=
-
1
;
time_t
res
=
mktime
(
&
tm
);
return
res
>=
0
?
res
:
0
;
}
};
struct
AttributeDateBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
struct
tm
tm
;
memset
(
&
tm
,
0
,
sizeof
(
tm
));
sscanf
(
s
.
c_str
(),
"%04d-%02d-%02d"
,
&
tm
.
tm_year
,
&
tm
.
tm_mon
,
&
tm
.
tm_mday
);
tm
.
tm_mon
--
;
tm
.
tm_year
-=
1900
;
tm
.
tm_isdst
=
-
1
;
time_t
res
=
mktime
(
&
tm
);
return
res
>=
0
?
res
:
0
;
}
};
struct
AttributeTimeBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
struct
tm
tm
;
memset
(
&
tm
,
0
,
sizeof
(
tm
));
sscanf
(
s
.
c_str
(),
"%02d:%02d:%02d"
,
&
tm
.
tm_hour
,
&
tm
.
tm_min
,
&
tm
.
tm_sec
);
time_t
res
=
mktime
(
&
tm
);
return
res
>=
0
?
res
:
0
;
}
};
using
AttributeYearBase
=
AttributeUIntBase
;
using
AttributeMonthBase
=
AttributeUIntBase
;
using
AttributeDayOfWeekBase
=
AttributeUIntBase
;
using
AttributeDayOfMonthBase
=
AttributeUIntBase
;
using
AttributeWeekBase
=
AttributeDateBase
;
using
AttributeHourBase
=
AttributeUIntBase
;
using
AttributeMinuteBase
=
AttributeUIntBase
;
using
AttributeSecondBase
=
AttributeUIntBase
;
struct
AttributeShortStringBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
std
::
string
tmp
=
s
;
tmp
.
resize
(
sizeof
(
BinaryData
));
return
*
reinterpret_cast
<
const
BinaryData
*>
(
tmp
.
data
());
}
};
/** Атрибуты, относящиеся к времени начала визита */
using
VisitStartDateTime
=
AttributeDateTimeBase
;
using
VisitStartDateTimeRoundedToMinute
=
AttributeDateTimeBase
;
using
VisitStartDateTimeRoundedToHour
=
AttributeDateTimeBase
;
using
VisitStartDateTime
=
AttributeDateTimeBase
;
using
VisitStartDate
=
AttributeDateBase
;
using
VisitStartDateRoundedToMonth
=
AttributeDateBase
;
using
VisitStartWeek
=
AttributeWeekBase
;
using
VisitStartTime
=
AttributeTimeBase
;
using
VisitStartTimeRoundedToMinute
=
AttributeTimeBase
;
using
VisitStartYear
=
AttributeYearBase
;
using
VisitStartMonth
=
AttributeMonthBase
;
using
VisitStartDayOfWeek
=
AttributeDayOfWeekBase
;
using
VisitStartDayOfMonth
=
AttributeDayOfMonthBase
;
using
VisitStartHour
=
AttributeHourBase
;
using
VisitStartMinute
=
AttributeMinuteBase
;
using
VisitStartSecond
=
AttributeSecondBase
;
/** Атрибуты, относящиеся к времени начала первого визита */
using
FirstVisitDateTime
=
AttributeDateTimeBase
;
using
FirstVisitDate
=
AttributeDateBase
;
using
FirstVisitWeek
=
AttributeWeekBase
;
using
FirstVisitTime
=
AttributeTimeBase
;
using
FirstVisitYear
=
AttributeYearBase
;
using
FirstVisitMonth
=
AttributeMonthBase
;
using
FirstVisitDayOfWeek
=
AttributeDayOfWeekBase
;
using
FirstVisitDayOfMonth
=
AttributeDayOfMonthBase
;
using
FirstVisitHour
=
AttributeHourBase
;
using
FirstVisitMinute
=
AttributeMinuteBase
;
using
FirstVisitSecond
=
AttributeSecondBase
;
/** Атрибуты, относящиеся к времени начала предпоследнего визита */
using
PredLastVisitDate
=
AttributeDateBase
;
using
PredLastVisitWeek
=
AttributeWeekBase
;
using
PredLastVisitYear
=
AttributeYearBase
;
using
PredLastVisitMonth
=
AttributeMonthBase
;
using
PredLastVisitDayOfWeek
=
AttributeDayOfWeekBase
;
using
PredLastVisitDayOfMonth
=
AttributeDayOfMonthBase
;
/** Атрибуты, относящиеся к времени на компьютере посетителя */
using
ClientDateTime
=
AttributeDateTimeBase
;
using
ClientTime
=
AttributeTimeBase
;
using
ClientTimeHour
=
AttributeHourBase
;
using
ClientTimeMinute
=
AttributeMinuteBase
;
using
ClientTimeSecond
=
AttributeSecondBase
;
/** Базовый класс для атрибутов, для которых хранится хэш. */
struct
AttributeHashBase
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
union
{
unsigned
char
char_data
[
16
];
Poco
::
UInt64
uint64_data
;
}
buf
;
MD5_CTX
ctx
;
MD5_Init
(
&
ctx
);
MD5_Update
(
&
ctx
,
reinterpret_cast
<
const
unsigned
char
*>
(
s
.
data
()),
s
.
size
());
MD5_Final
(
buf
.
char_data
,
&
ctx
);
return
Poco
::
ByteOrder
::
flipBytes
(
buf
.
uint64_data
);
}
};
using
EndURLHash
=
AttributeHashBase
;
using
RefererHash
=
AttributeHashBase
;
using
SearchPhraseHash
=
AttributeHashBase
;
using
RefererDomainHash
=
AttributeHashBase
;
using
StartURLHash
=
AttributeHashBase
;
using
StartURLDomainHash
=
AttributeHashBase
;
using
RegionID
=
AttributeUIntBase
;
using
RegionCity
=
AttributeUIntBase
;
using
RegionArea
=
AttributeUIntBase
;
using
RegionCountry
=
AttributeUIntBase
;
using
TraficSourceID
=
AttributeIntBase
;
using
CorrectedTraficSourceID
=
AttributeIntBase
;
using
CorrectedSearchEngineID
=
AttributeUIntBase
;
using
IsNewUser
=
AttributeUIntBase
;
using
UserNewness
=
AttributeUIntBase
;
struct
UserNewnessInterval
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
using
UserReturnTime
=
AttributeUIntBase
;
struct
UserReturnTimeInterval
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
using
UserVisitsPeriod
=
AttributeUIntBase
;
struct
UserVisitsPeriodInterval
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
using
VisitTime
=
AttributeUIntBase
;
using
VisitTimeInterval
=
AttributeUIntBase
;
using
PageViews
=
AttributeUIntBase
;
using
PageViewsInterval
=
AttributeUIntBase
;
using
Bounce
=
AttributeUIntBase
;
using
BouncePrecise
=
AttributeUIntBase
;
using
IsYandex
=
AttributeUIntBase
;
using
UserID
=
AttributeUIntBase
;
using
UserIDCreateDateTime
=
AttributeDateTimeBase
;
using
UserIDCreateDate
=
AttributeDateBase
;
using
UserIDAge
=
AttributeIntBase
;
using
UserIDAgeInterval
=
AttributeIntBase
;
using
TotalVisits
=
AttributeUIntBase
;
using
TotalVisitsInterval
=
AttributeUIntBase
;
using
Age
=
AttributeUIntBase
;
using
AgeInterval
=
AttributeUIntBase
;
using
Sex
=
AttributeUIntBase
;
using
Income
=
AttributeUIntBase
;
using
AdvEngineID
=
AttributeUIntBase
;
struct
DotNet
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"."
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
8
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
8
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])));
}
};
struct
DotNetMajor
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
struct
Flash
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"."
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
8
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
8
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])));
}
};
struct
FlashExists
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
struct
FlashMajor
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
struct
Silverlight
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"."
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
56
)
:
(
tokenizer
.
count
()
==
2
?
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
56
)
|
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
48
))
:
(
tokenizer
.
count
()
==
3
?
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
56
)
|
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
48
)
|
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
2
])
<<
16
))
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
56
)
|
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
48
)
|
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
2
])
<<
16
)
|
DB
::
parse
<
UInt64
>
(
tokenizer
[
3
])))));
}
};
using
SilverlightMajor
=
AttributeUIntBase
;
using
Hits
=
AttributeUIntBase
;
using
HitsInterval
=
AttributeUIntBase
;
using
JavaEnable
=
AttributeUIntBase
;
using
CookieEnable
=
AttributeUIntBase
;
using
JavascriptEnable
=
AttributeUIntBase
;
using
IsMobile
=
AttributeUIntBase
;
using
MobilePhoneID
=
AttributeUIntBase
;
using
MobilePhoneModelHash
=
AttributeHashBase
;
using
MobilePhoneModel
=
AttributeShortStringBase
;
struct
BrowserLanguage
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
std
::
string
tmp
=
s
;
tmp
.
resize
(
sizeof
(
UInt16
));
return
*
reinterpret_cast
<
const
UInt16
*>
(
tmp
.
data
());
}
};
using
BrowserCountry
=
BrowserLanguage
;
using
TopLevelDomain
=
AttributeShortStringBase
;
using
URLScheme
=
AttributeShortStringBase
;
using
IPNetworkID
=
AttributeUIntBase
;
using
ClientTimeZone
=
AttributeIntBase
;
using
OSID
=
AttributeUIntBase
;
using
OSMostAncestor
=
AttributeUIntBase
;
struct
ClientIP
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"."
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
:
(
tokenizer
.
count
()
==
2
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
16
)
:
(
tokenizer
.
count
()
==
3
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
16
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
2
])
<<
8
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
16
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
2
])
<<
8
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
3
])))));
}
};
struct
Resolution
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"x"
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
:
(
tokenizer
.
count
()
==
2
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
8
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
8
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
2
]))));
}
};
struct
ResolutionWidthHeight
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"x"
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
16
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
16
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])));
}
};
struct
ResolutionWidth
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
struct
ResolutionHeight
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
using
ResolutionWidthInterval
=
ResolutionWidth
;
using
ResolutionHeightInterval
=
ResolutionHeight
;
struct
ResolutionColor
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
struct
WindowClientArea
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"x"
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
16
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
16
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])));
}
};
using
WindowClientAreaInterval
=
WindowClientArea
;
using
WindowClientWidth
=
AttributeUIntBase
;
using
WindowClientWidthInterval
=
WindowClientWidth
;
using
WindowClientHeight
=
AttributeUIntBase
;
using
WindowClientHeightInterval
=
WindowClientHeight
;
using
SearchEngineID
=
AttributeUIntBase
;
using
SearchEngineMostAncestor
=
AttributeUIntBase
;
using
CodeVersion
=
AttributeUIntBase
;
/// формат строки вида "10 7.5b", где первое число - UserAgentID, дальше - версия.
struct
UserAgent
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
" ."
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
:
(
tokenizer
.
count
()
==
2
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
16
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
24
)
+
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])
<<
16
)
+
(
static_cast
<
UInt32
>
(
tokenizer
[
2
][
1
])
<<
8
)
+
(
tokenizer
[
2
][
0
]))));
}
};
struct
UserAgentVersion
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
"."
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
16
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
16
)
+
(
static_cast
<
UInt32
>
(
tokenizer
[
1
][
1
])
<<
8
)
+
tokenizer
[
1
][
0
]));
}
};
struct
UserAgentMajor
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
Poco
::
StringTokenizer
tokenizer
(
s
,
" "
);
return
tokenizer
.
count
()
==
0
?
0
:
(
tokenizer
.
count
()
==
1
?
(
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
8
)
:
((
DB
::
parse
<
UInt64
>
(
tokenizer
[
0
])
<<
8
)
+
DB
::
parse
<
UInt64
>
(
tokenizer
[
1
])));
}
};
struct
UserAgentID
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
return
DB
::
parse
<
UInt64
>
(
s
);
}
};
using
ClickGoodEvent
=
AttributeIntBase
;
using
ClickPriorityID
=
AttributeIntBase
;
using
ClickBannerID
=
AttributeIntBase
;
using
ClickPageID
=
AttributeIntBase
;
using
ClickPlaceID
=
AttributeIntBase
;
using
ClickTypeID
=
AttributeIntBase
;
using
ClickResourceID
=
AttributeIntBase
;
using
ClickDomainID
=
AttributeUIntBase
;
using
ClickCost
=
AttributeUIntBase
;
using
ClickURLHash
=
AttributeHashBase
;
using
ClickOrderID
=
AttributeUIntBase
;
using
GoalReachesAny
=
AttributeIntBase
;
using
GoalReachesDepth
=
AttributeIntBase
;
using
GoalReachesURL
=
AttributeIntBase
;
using
ConvertedAny
=
AttributeIntBase
;
using
ConvertedDepth
=
AttributeIntBase
;
using
ConvertedURL
=
AttributeIntBase
;
using
GoalReaches
=
AttributeIntBase
;
using
Converted
=
AttributeIntBase
;
using
CounterID
=
AttributeUIntBase
;
using
VisitID
=
AttributeUIntBase
;
struct
Interests
:
public
IAttributeMetadata
{
BinaryData
parse
(
const
std
::
string
&
s
)
const
{
if
(
s
.
empty
())
return
0
;
using
namespace
boost
::
algorithm
;
BinaryData
value
=
0
;
///коряво
for
(
split_iterator
<
std
::
string
::
const_iterator
>
i
=
make_split_iterator
(
s
,
token_finder
(
is_any_of
(
","
),
token_compress_on
));
i
!=
split_iterator
<
std
::
string
::
const_iterator
>
();
++
i
)
{
UInt16
interest
=
DB
::
parse
<
UInt64
>
(
boost
::
copy_range
<
std
::
string
>
(
*
i
));
value
|=
(
interest
==
0x2000
?
0x2000
:
(
interest
==
0x1000
?
0x1000
:
(
interest
==
0x800
?
0x800
:
(
interest
==
0x400
?
0x400
:
(
interest
==
0x200
?
0x200
:
(
interest
==
0x100
?
0x100
:
(
interest
==
0x80
?
0x80
:
(
interest
==
0x40
?
0x40
:
(
interest
==
0x20
?
0x20
:
(
interest
==
0x10
?
0x10
:
(
interest
==
8
?
8
:
(
interest
==
4
?
4
:
(
interest
==
2
?
2
:
(
interest
==
1
?
1
:
0
))))))))))))));
}
return
value
;
}
};
using
HasInterestPhoto
=
AttributeUIntBase
;
using
HasInterestMoviePremieres
=
AttributeUIntBase
;
using
HasInterestTourism
=
AttributeUIntBase
;
using
HasInterestFamilyAndChildren
=
AttributeUIntBase
;
using
HasInterestFinance
=
AttributeUIntBase
;
using
HasInterestB2B
=
AttributeUIntBase
;
using
HasInterestCars
=
AttributeUIntBase
;
using
HasInterestMobileAndInternetCommunications
=
AttributeUIntBase
;
using
HasInterestBuilding
=
AttributeUIntBase
;
using
HasInterestCulinary
=
AttributeUIntBase
;
using
HasInterestSoftware
=
AttributeUIntBase
;
using
HasInterestEstate
=
AttributeUIntBase
;
using
HasInterestHealthyLifestyle
=
AttributeUIntBase
;
using
HasInterestLiterature
=
AttributeUIntBase
;
using
OpenstatServiceNameHash
=
AttributeHashBase
;
using
OpenstatCampaignIDHash
=
AttributeHashBase
;
using
OpenstatAdIDHash
=
AttributeHashBase
;
using
OpenstatSourceIDHash
=
AttributeHashBase
;
using
UTMSourceHash
=
AttributeHashBase
;
using
UTMMediumHash
=
AttributeHashBase
;
using
UTMCampaignHash
=
AttributeHashBase
;
using
UTMContentHash
=
AttributeHashBase
;
using
UTMTermHash
=
AttributeHashBase
;
using
FromHash
=
AttributeHashBase
;
using
CLID
=
AttributeUIntBase
;
using
SocialSourceNetworkID
=
AttributeUIntBase
;
/** Информация о типах атрибутов */
using
AttributeMetadatas
=
std
::
map
<
std
::
string
,
Poco
::
SharedPtr
<
IAttributeMetadata
>>
;
inline
AttributeMetadatas
GetOLAPAttributeMetadata
()
{
return
{
{
"DummyAttribute"
,
new
DummyAttribute
},
{
"VisitStartDateTime"
,
new
VisitStartDateTime
},
{
"VisitStartDateTimeRoundedToMinute"
,
new
VisitStartDateTimeRoundedToMinute
},
{
"VisitStartDateTimeRoundedToHour"
,
new
VisitStartDateTimeRoundedToHour
},
{
"VisitStartDate"
,
new
VisitStartDate
},
{
"VisitStartDateRoundedToMonth"
,
new
VisitStartDateRoundedToMonth
},
{
"VisitStartTime"
,
new
VisitStartTime
},
{
"VisitStartTimeRoundedToMinute"
,
new
VisitStartTimeRoundedToMinute
},
{
"VisitStartYear"
,
new
VisitStartYear
},
{
"VisitStartMonth"
,
new
VisitStartMonth
},
{
"VisitStartDayOfWeek"
,
new
VisitStartDayOfWeek
},
{
"VisitStartDayOfMonth"
,
new
VisitStartDayOfMonth
},
{
"VisitStartHour"
,
new
VisitStartHour
},
{
"VisitStartMinute"
,
new
VisitStartMinute
},
{
"VisitStartSecond"
,
new
VisitStartSecond
},
{
"VisitStartWeek"
,
new
VisitStartWeek
},
{
"FirstVisitDateTime"
,
new
FirstVisitDateTime
},
{
"FirstVisitDate"
,
new
FirstVisitDate
},
{
"FirstVisitTime"
,
new
FirstVisitTime
},
{
"FirstVisitYear"
,
new
FirstVisitYear
},
{
"FirstVisitMonth"
,
new
FirstVisitMonth
},
{
"FirstVisitDayOfWeek"
,
new
FirstVisitDayOfWeek
},
{
"FirstVisitDayOfMonth"
,
new
FirstVisitDayOfMonth
},
{
"FirstVisitHour"
,
new
FirstVisitHour
},
{
"FirstVisitMinute"
,
new
FirstVisitMinute
},
{
"FirstVisitSecond"
,
new
FirstVisitSecond
},
{
"FirstVisitWeek"
,
new
FirstVisitWeek
},
{
"PredLastVisitDate"
,
new
PredLastVisitDate
},
{
"PredLastVisitYear"
,
new
PredLastVisitYear
},
{
"PredLastVisitMonth"
,
new
PredLastVisitMonth
},
{
"PredLastVisitDayOfWeek"
,
new
PredLastVisitDayOfWeek
},
{
"PredLastVisitDayOfMonth"
,
new
PredLastVisitDayOfMonth
},
{
"PredLastVisitWeek"
,
new
PredLastVisitWeek
},
{
"RegionID"
,
new
RegionID
},
{
"RegionCity"
,
new
RegionCity
},
{
"RegionArea"
,
new
RegionArea
},
{
"RegionCountry"
,
new
RegionCountry
},
{
"TraficSourceID"
,
new
TraficSourceID
},
{
"UserNewness"
,
new
UserNewness
},
{
"UserNewnessInterval"
,
new
UserNewnessInterval
},
{
"UserReturnTime"
,
new
UserReturnTime
},
{
"UserReturnTimeInterval"
,
new
UserReturnTimeInterval
},
{
"UserVisitsPeriod"
,
new
UserVisitsPeriod
},
{
"UserVisitsPeriodInterval"
,
new
UserVisitsPeriodInterval
},
{
"VisitTime"
,
new
VisitTime
},
{
"VisitTimeInterval"
,
new
VisitTimeInterval
},
{
"PageViews"
,
new
PageViews
},
{
"PageViewsInterval"
,
new
PageViewsInterval
},
{
"UserID"
,
new
UserID
},
{
"TotalVisits"
,
new
TotalVisits
},
{
"TotalVisitsInterval"
,
new
TotalVisitsInterval
},
{
"Age"
,
new
Age
},
{
"AgeInterval"
,
new
AgeInterval
},
{
"Sex"
,
new
Sex
},
{
"Income"
,
new
Income
},
{
"AdvEngineID"
,
new
AdvEngineID
},
{
"DotNet"
,
new
DotNet
},
{
"DotNetMajor"
,
new
DotNetMajor
},
{
"EndURLHash"
,
new
EndURLHash
},
{
"Flash"
,
new
Flash
},
{
"FlashMajor"
,
new
FlashMajor
},
{
"FlashExists"
,
new
FlashExists
},
{
"Hits"
,
new
Hits
},
{
"HitsInterval"
,
new
HitsInterval
},
{
"JavaEnable"
,
new
JavaEnable
},
{
"OSID"
,
new
OSID
},
{
"ClientIP"
,
new
ClientIP
},
{
"RefererHash"
,
new
RefererHash
},
{
"RefererDomainHash"
,
new
RefererDomainHash
},
{
"Resolution"
,
new
Resolution
},
{
"ResolutionWidthHeight"
,
new
ResolutionWidthHeight
},
{
"ResolutionWidth"
,
new
ResolutionWidth
},
{
"ResolutionHeight"
,
new
ResolutionHeight
},
{
"ResolutionWidthInterval"
,
new
ResolutionWidthInterval
},
{
"ResolutionHeightInterval"
,
new
ResolutionHeightInterval
},
{
"ResolutionColor"
,
new
ResolutionColor
},
{
"CookieEnable"
,
new
CookieEnable
},
{
"JavascriptEnable"
,
new
JavascriptEnable
},
{
"IsMobile"
,
new
IsMobile
},
{
"MobilePhoneID"
,
new
MobilePhoneID
},
{
"MobilePhoneModel"
,
new
MobilePhoneModel
},
{
"MobilePhoneModelHash"
,
new
MobilePhoneModelHash
},
{
"IPNetworkID"
,
new
IPNetworkID
},
{
"WindowClientArea"
,
new
WindowClientArea
},
{
"WindowClientWidth"
,
new
WindowClientWidth
},
{
"WindowClientHeight"
,
new
WindowClientHeight
},
{
"WindowClientAreaInterval"
,
new
WindowClientAreaInterval
},
{
"WindowClientWidthInterval"
,
new
WindowClientWidthInterval
},
{
"WindowClientHeightInterval"
,
new
WindowClientHeightInterval
},
{
"ClientTimeZone"
,
new
ClientTimeZone
},
{
"ClientDateTime"
,
new
ClientDateTime
},
{
"ClientTime"
,
new
ClientTime
},
{
"ClientTimeHour"
,
new
ClientTimeHour
},
{
"ClientTimeMinute"
,
new
ClientTimeMinute
},
{
"ClientTimeSecond"
,
new
ClientTimeSecond
},
{
"Silverlight"
,
new
Silverlight
},
{
"SilverlightMajor"
,
new
SilverlightMajor
},
{
"SearchEngineID"
,
new
SearchEngineID
},
{
"SearchPhraseHash"
,
new
SearchPhraseHash
},
{
"StartURLHash"
,
new
StartURLHash
},
{
"StartURLDomainHash"
,
new
StartURLDomainHash
},
{
"UserAgent"
,
new
UserAgent
},
{
"UserAgentVersion"
,
new
UserAgentVersion
},
{
"UserAgentMajor"
,
new
UserAgentMajor
},
{
"UserAgentID"
,
new
UserAgentID
},
{
"ClickGoodEvent"
,
new
ClickGoodEvent
},
{
"ClickPriorityID"
,
new
ClickPriorityID
},
{
"ClickBannerID"
,
new
ClickBannerID
},
{
"ClickPageID"
,
new
ClickPageID
},
{
"ClickPlaceID"
,
new
ClickPlaceID
},
{
"ClickTypeID"
,
new
ClickTypeID
},
{
"ClickResourceID"
,
new
ClickResourceID
},
{
"ClickDomainID"
,
new
ClickDomainID
},
{
"ClickCost"
,
new
ClickCost
},
{
"ClickURLHash"
,
new
ClickURLHash
},
{
"ClickOrderID"
,
new
ClickOrderID
},
{
"GoalReaches"
,
new
GoalReaches
},
{
"GoalReachesAny"
,
new
GoalReachesAny
},
{
"GoalReachesDepth"
,
new
GoalReachesDepth
},
{
"GoalReachesURL"
,
new
GoalReachesURL
},
{
"Converted"
,
new
Converted
},
{
"ConvertedAny"
,
new
ConvertedAny
},
{
"ConvertedDepth"
,
new
ConvertedDepth
},
{
"ConvertedURL"
,
new
ConvertedURL
},
{
"Bounce"
,
new
Bounce
},
{
"BouncePrecise"
,
new
BouncePrecise
},
{
"IsNewUser"
,
new
IsNewUser
},
{
"CodeVersion"
,
new
CodeVersion
},
{
"CounterID"
,
new
CounterID
},
{
"VisitID"
,
new
VisitID
},
{
"IsYandex"
,
new
IsYandex
},
{
"TopLevelDomain"
,
new
TopLevelDomain
},
{
"URLScheme"
,
new
URLScheme
},
{
"UserIDCreateDateTime"
,
new
UserIDCreateDateTime
},
{
"UserIDCreateDate"
,
new
UserIDCreateDate
},
{
"UserIDAge"
,
new
UserIDAge
},
{
"UserIDAgeInterval"
,
new
UserIDAgeInterval
},
{
"OSMostAncestor"
,
new
OSMostAncestor
},
{
"SearchEngineMostAncestor"
,
new
SearchEngineMostAncestor
},
{
"BrowserLanguage"
,
new
BrowserLanguage
},
{
"BrowserCountry"
,
new
BrowserCountry
},
{
"Interests"
,
new
Interests
},
{
"HasInterestPhoto"
,
new
HasInterestPhoto
},
{
"HasInterestMoviePremieres"
,
new
HasInterestMoviePremieres
},
{
"HasInterestMobileAndInternetCommunications"
,
new
HasInterestMobileAndInternetCommunications
},
{
"HasInterestFinance"
,
new
HasInterestFinance
},
{
"HasInterestFamilyAndChildren"
,
new
HasInterestFamilyAndChildren
},
{
"HasInterestCars"
,
new
HasInterestCars
},
{
"HasInterestB2B"
,
new
HasInterestB2B
},
{
"HasInterestTourism"
,
new
HasInterestTourism
},
{
"HasInterestBuilding"
,
new
HasInterestBuilding
},
{
"HasInterestCulinary"
,
new
HasInterestCulinary
},
{
"HasInterestSoftware"
,
new
HasInterestSoftware
},
{
"HasInterestEstate"
,
new
HasInterestEstate
},
{
"HasInterestHealthyLifestyle"
,
new
HasInterestHealthyLifestyle
},
{
"HasInterestLiterature"
,
new
HasInterestLiterature
},
{
"OpenstatServiceNameHash"
,
new
OpenstatServiceNameHash
},
{
"OpenstatCampaignIDHash"
,
new
OpenstatCampaignIDHash
},
{
"OpenstatAdIDHash"
,
new
OpenstatAdIDHash
},
{
"OpenstatSourceIDHash"
,
new
OpenstatSourceIDHash
},
{
"UTMSourceHash"
,
new
UTMSourceHash
},
{
"UTMMediumHash"
,
new
UTMMediumHash
},
{
"UTMCampaignHash"
,
new
UTMCampaignHash
},
{
"UTMContentHash"
,
new
UTMContentHash
},
{
"UTMTermHash"
,
new
UTMTermHash
},
{
"FromHash"
,
new
FromHash
},
{
"CLID"
,
new
CLID
},
{
"SocialSourceNetworkID"
,
new
SocialSourceNetworkID
},
{
"CorrectedTraficSourceID"
,
new
CorrectedTraficSourceID
},
{
"CorrectedSearchEngineID"
,
new
CorrectedSearchEngineID
},
};
}
}
}
dbms/src/Server/OLAPHTTPHandler.cpp
已删除
100644 → 0
浏览文件 @
c6da35b7
#include <Poco/Net/HTTPBasicCredentials.h>
#include <DB/Interpreters/executeQuery.h>
#include <DB/Interpreters/Quota.h>
#include <DB/IO/WriteBufferFromHTTPServerResponse.h>
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/ReadBufferFromIStream.h>
#include <DB/IO/ReadBufferFromString.h>
#include "OLAPQueryParser.h"
#include "OLAPQueryConverter.h"
#include "OLAPHTTPHandler.h"
#include <DB/Common/Stopwatch.h>
#include <iomanip>
namespace
DB
{
namespace
ErrorCodes
{
extern
const
int
POCO_EXCEPTION
;
extern
const
int
STD_EXCEPTION
;
extern
const
int
UNKNOWN_EXCEPTION
;
}
void
OLAPHTTPHandler
::
processQuery
(
Poco
::
Net
::
HTTPServerRequest
&
request
,
Poco
::
Net
::
HTTPServerResponse
&
response
)
{
HTMLForm
params
(
request
);
std
::
ostringstream
request_ostream
;
request_ostream
<<
request
.
stream
().
rdbuf
();
std
::
string
request_string
=
request_ostream
.
str
();
LOG_TRACE
(
log
,
"Request URI: "
<<
request
.
getURI
());
LOG_TRACE
(
log
,
"Request body: "
<<
request_string
);
std
::
istringstream
request_istream
(
request_string
);
BlockInputStreamPtr
query_plan
;
/// Имя пользователя и пароль могут быть заданы как в параметрах URL, так и с помощью HTTP Basic authentification (и то, и другое не секъюрно).
std
::
string
user
=
params
.
get
(
"user"
,
"default"
);
std
::
string
password
=
params
.
get
(
"password"
,
""
);
std
::
string
quota_key
=
params
.
get
(
"quota_key"
,
""
);
if
(
request
.
hasCredentials
())
{
Poco
::
Net
::
HTTPBasicCredentials
credentials
(
request
);
user
=
credentials
.
getUsername
();
password
=
credentials
.
getPassword
();
}
Context
context
=
*
server
.
global_context
;
context
.
setGlobalContext
(
*
server
.
global_context
);
context
.
setSetting
(
"profile"
,
profile
);
context
.
setUser
(
user
,
password
,
request
.
clientAddress
().
host
(),
quota_key
);
context
.
setInterface
(
Context
::
Interface
::
OLAP_HTTP
);
context
.
setHTTPMethod
(
Context
::
HTTPMethod
::
POST
);
OLAP
::
QueryParseResult
olap_query
=
server
.
olap_parser
->
parse
(
request_istream
);
std
::
string
clickhouse_query
;
server
.
olap_converter
->
OLAPServerQueryToClickHouse
(
olap_query
,
context
,
clickhouse_query
);
LOG_TRACE
(
log
,
"Converted query: "
<<
clickhouse_query
);
ReadBufferFromString
in
(
clickhouse_query
);
WriteBufferFromHTTPServerResponse
out
(
response
);
Stopwatch
watch
;
executeQuery
(
in
,
out
,
context
,
query_plan
,
[
&
response
]
(
const
String
&
content_type
)
{
response
.
setContentType
(
content_type
);
});
watch
.
stop
();
/// Если не было эксепшена и данные ещё не отправлены - отправляются HTTP заголовки с кодом 200.
out
.
finalize
();
}
void
OLAPHTTPHandler
::
handleRequest
(
Poco
::
Net
::
HTTPServerRequest
&
request
,
Poco
::
Net
::
HTTPServerResponse
&
response
)
{
/// Для того, чтобы работал keep-alive.
if
(
request
.
getVersion
()
==
Poco
::
Net
::
HTTPServerRequest
::
HTTP_1_1
)
response
.
setChunkedTransferEncoding
(
true
);
try
{
processQuery
(
request
,
response
);
LOG_INFO
(
log
,
"Done processing query"
);
}
catch
(
Exception
&
e
)
{
response
.
setStatusAndReason
(
Poco
::
Net
::
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
);
std
::
stringstream
s
;
s
<<
"Code: "
<<
e
.
code
()
<<
", e.displayText() = "
<<
e
.
displayText
()
<<
", e.what() = "
<<
e
.
what
();
if
(
!
response
.
sent
())
response
.
send
()
<<
s
.
str
()
<<
std
::
endl
;
LOG_ERROR
(
log
,
s
.
str
());
}
catch
(
Poco
::
Exception
&
e
)
{
response
.
setStatusAndReason
(
Poco
::
Net
::
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
);
std
::
stringstream
s
;
s
<<
"Code: "
<<
ErrorCodes
::
POCO_EXCEPTION
<<
", e.code() = "
<<
e
.
code
()
<<
", e.displayText() = "
<<
e
.
displayText
()
<<
", e.what() = "
<<
e
.
what
();
if
(
!
response
.
sent
())
response
.
send
()
<<
s
.
str
()
<<
std
::
endl
;
LOG_ERROR
(
log
,
s
.
str
());
}
catch
(
std
::
exception
&
e
)
{
response
.
setStatusAndReason
(
Poco
::
Net
::
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
);
std
::
stringstream
s
;
s
<<
"Code: "
<<
ErrorCodes
::
STD_EXCEPTION
<<
". "
<<
e
.
what
();
if
(
!
response
.
sent
())
response
.
send
()
<<
s
.
str
()
<<
std
::
endl
;
LOG_ERROR
(
log
,
s
.
str
());
}
catch
(...)
{
response
.
setStatusAndReason
(
Poco
::
Net
::
HTTPResponse
::
HTTP_INTERNAL_SERVER_ERROR
);
std
::
stringstream
s
;
s
<<
"Code: "
<<
ErrorCodes
::
UNKNOWN_EXCEPTION
<<
". Unknown exception."
;
if
(
!
response
.
sent
())
response
.
send
()
<<
s
.
str
()
<<
std
::
endl
;
LOG_ERROR
(
log
,
s
.
str
());
}
}
}
dbms/src/Server/OLAPHTTPHandler.h
已删除
100644 → 0
浏览文件 @
c6da35b7
#pragma once
#include "Server.h"
#include <DB/Common/CurrentMetrics.h>
namespace
DB
{
/// Обработчик http-запросов в формате OLAP-server.
class
OLAPHTTPHandler
:
public
Poco
::
Net
::
HTTPRequestHandler
{
public:
OLAPHTTPHandler
(
Server
&
server_
)
:
server
(
server_
),
log
(
&
Logger
::
get
(
"OLAPHTTPHandler"
)),
profile
(
Poco
::
Util
::
Application
::
instance
().
config
().
getString
(
"olap_compatibility.profile"
))
{
}
void
handleRequest
(
Poco
::
Net
::
HTTPServerRequest
&
request
,
Poco
::
Net
::
HTTPServerResponse
&
response
);
private:
Server
&
server
;
Logger
*
log
;
const
String
profile
;
CurrentMetrics
::
Increment
metric_increment
{
CurrentMetrics
::
HTTPConnection
};
void
processQuery
(
Poco
::
Net
::
HTTPServerRequest
&
request
,
Poco
::
Net
::
HTTPServerResponse
&
response
);
};
}
dbms/src/Server/OLAPQueryConverter.cpp
已删除
100644 → 0
浏览文件 @
c6da35b7
#include "OLAPQueryConverter.h"
#include <DB/IO/WriteHelpers.h>
#include <DB/IO/WriteBufferFromString.h>
namespace
DB
{
namespace
ErrorCodes
{
extern
const
int
UNSUPPORTED_PARAMETER
;
extern
const
int
UNKNOWN_IDENTIFIER
;
extern
const
int
UNKNOWN_RELATION
;
}
namespace
OLAP
{
QueryConverter
::
QueryConverter
(
Poco
::
Util
::
AbstractConfiguration
&
config
)
:
QueryConverter
(
config
.
getString
(
"olap_compatibility.table_for_single_counter"
),
config
.
getString
(
"olap_compatibility.table_for_all_counters"
))
{
}
QueryConverter
::
QueryConverter
(
const
String
&
table_for_single_counter
,
const
String
&
table_for_all_counters
)
:
table_for_single_counter
(
table_for_single_counter
),
table_for_all_counters
(
table_for_all_counters
)
{
fillFormattedAttributeMap
();
fillNumericAttributeMap
();
fillFormattingAggregatedAttributeMap
();
attribute_metadatas
=
GetOLAPAttributeMetadata
();
}
static
std
::
string
firstWord
(
std
::
string
s
)
{
for
(
size_t
i
=
0
;
i
<
s
.
length
();
++
i
)
{
if
((
s
[
i
]
<
'a'
||
s
[
i
]
>
'z'
)
&&
(
s
[
i
]
<
'A'
||
s
[
i
]
>
'Z'
))
{
s
.
erase
(
s
.
begin
()
+
i
,
s
.
end
());
return
s
;
}
}
return
s
;
}
void
QueryConverter
::
OLAPServerQueryToClickHouse
(
const
QueryParseResult
&
query
,
Context
&
inout_context
,
std
::
string
&
out_query
)
const
{
/// Пустая строка, или строка вида ", 'ua'".
std
::
string
regions_point_of_view_formatted
;
if
(
!
query
.
regions_point_of_view
.
empty
())
{
std
::
stringstream
tmp
;
tmp
<<
", "
<<
mysqlxx
::
quote
<<
query
.
regions_point_of_view
;
regions_point_of_view_formatted
=
tmp
.
str
();
}
/// Проверим, умеем ли мы выполнять такой запрос.
if
(
query
.
format
!=
FORMAT_TAB
)
throw
Exception
(
"Only tab-separated output format is supported"
,
ErrorCodes
::
UNSUPPORTED_PARAMETER
);
/// Учтем некоторые настройки (далеко не все).
Settings
new_settings
=
inout_context
.
getSettings
();
if
(
query
.
concurrency
!=
0
)
new_settings
.
max_threads
=
query
.
concurrency
;
if
(
query
.
max_execution_time
!=
0
)
new_settings
.
limits
.
max_execution_time
=
Poco
::
Timespan
(
query
.
max_execution_time
,
0
);
if
(
query
.
max_result_size
!=
0
)
new_settings
.
limits
.
max_rows_to_group_by
=
query
.
max_result_size
;
if
(
query
.
has_overflow_mode
)
{
switch
(
query
.
overflow_mode
)
{
case
OLAP
::
OVERFLOW_MODE_THROW
:
new_settings
.
limits
.
group_by_overflow_mode
=
DB
::
OverflowMode
::
THROW
;
break
;
case
OLAP
::
OVERFLOW_MODE_BREAK
:
new_settings
.
limits
.
group_by_overflow_mode
=
DB
::
OverflowMode
::
BREAK
;
break
;
case
OLAP
::
OVERFLOW_MODE_ANY
:
new_settings
.
limits
.
group_by_overflow_mode
=
DB
::
OverflowMode
::
ANY
;
break
;
}
}
inout_context
.
setSettings
(
new_settings
);
/// Составим запрос.
out_query
=
"SELECT "
;
std
::
vector
<
std
::
string
>
selected_expressions
;
/// Что выбирать: ключи агрегации и агрегированные значения.
for
(
size_t
i
=
0
;
i
<
query
.
key_attributes
.
size
();
++
i
)
{
const
QueryParseResult
::
KeyAttribute
&
key
=
query
.
key_attributes
[
i
];
std
::
string
s
=
convertAttributeFormatted
(
key
.
attribute
,
key
.
parameter
,
regions_point_of_view_formatted
);
if
(
i
>
0
)
out_query
+=
", "
;
out_query
+=
s
+
" AS _"
+
firstWord
(
key
.
attribute
)
+
(
key
.
parameter
?
"_"
+
toString
(
key
.
parameter
)
:
""
);
selected_expressions
.
push_back
(
s
);
}
for
(
size_t
i
=
0
;
i
<
query
.
aggregates
.
size
();
++
i
)
{
const
QueryParseResult
::
Aggregate
&
aggregate
=
query
.
aggregates
[
i
];
std
::
string
s
=
convertAggregateFunction
(
aggregate
.
attribute
,
aggregate
.
parameter
,
aggregate
.
function
,
query
,
regions_point_of_view_formatted
);
if
(
query
.
key_attributes
.
size
()
+
i
>
0
)
out_query
+=
", "
;
out_query
+=
s
+
" AS _"
+
firstWord
(
aggregate
.
function
)
+
"_"
+
firstWord
(
aggregate
.
attribute
)
+
(
aggregate
.
parameter
?
"_"
+
toString
(
aggregate
.
parameter
)
:
""
);
selected_expressions
.
push_back
(
s
);
}
/// Из какой таблицы.
out_query
+=
" FROM "
+
getTableName
(
query
.
CounterID
,
query
.
local
);
/// Добавляем сэмплирование.
if
(
query
.
sample
!=
1
)
out_query
+=
" SAMPLE "
+
toString
(
query
.
sample
);
/// Условия.
out_query
+=
" WHERE "
;
/// Диапазон дат.
out_query
+=
convertDateRange
(
query
.
date_first
,
query
.
date_last
);
/// Счетчик.
if
(
query
.
CounterID
!=
0
)
out_query
+=
" AND "
+
convertCounterID
(
query
.
CounterID
);
/// Произвольные условия.
for
(
size_t
i
=
0
;
i
<
query
.
where_conditions
.
size
();
++
i
)
{
const
QueryParseResult
::
WhereCondition
&
condition
=
query
.
where_conditions
[
i
];
out_query
+=
" AND "
+
convertCondition
(
condition
.
attribute
,
condition
.
parameter
,
condition
.
relation
,
condition
.
rhs
,
regions_point_of_view_formatted
);
}
/// Группировка.
if
(
!
query
.
key_attributes
.
empty
())
{
out_query
+=
" GROUP BY "
;
for
(
size_t
i
=
0
;
i
<
query
.
key_attributes
.
size
();
++
i
)
{
if
(
i
>
0
)
out_query
+=
", "
;
out_query
+=
selected_expressions
[
i
];
}
}
/// Условие для групп.
out_query
+=
" "
+
getHavingSection
();
/// Сортировка.
if
(
!
query
.
sort_columns
.
empty
())
{
out_query
+=
" ORDER BY "
;
for
(
size_t
i
=
0
;
i
<
query
.
sort_columns
.
size
();
++
i
)
{
const
QueryParseResult
::
SortColumn
&
column
=
query
.
sort_columns
[
i
];
if
(
i
>
0
)
out_query
+=
", "
;
out_query
+=
selected_expressions
[
column
.
index
-
1
];
out_query
+=
" "
+
convertSortDirection
(
column
.
direction
);
}
}
/// Ограничение на количество выводимых строк.
if
(
query
.
limit
!=
0
)
out_query
+=
" LIMIT "
+
toString
(
query
.
limit
);
}
std
::
string
QueryConverter
::
convertAttributeFormatted
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
{
if
(
formatted_attribute_map
.
count
(
attribute
))
return
Poco
::
format
(
formatted_attribute_map
.
at
(
attribute
),
parameter
);
/** Для атрибутов по регионам, выражение содержит подстановку %s,
* куда должна быть подставлена regions_point_of_view_formatted.
*/
if
(
regions_attributes_set
.
count
(
attribute
))
return
Poco
::
format
(
numeric_attribute_map
.
at
(
attribute
),
regions_point_of_view_formatted
);
if
(
numeric_attribute_map
.
count
(
attribute
))
{
std
::
string
numeric
=
Poco
::
format
(
numeric_attribute_map
.
at
(
attribute
),
parameter
);
if
(
formatting_aggregated_attribute_map
.
count
(
attribute
))
return
Poco
::
format
(
formatting_aggregated_attribute_map
.
at
(
attribute
),
std
::
string
(
"("
)
+
numeric
+
")"
);
else
return
numeric
;
}
throw
Exception
(
"Unknown attribute: "
+
attribute
,
ErrorCodes
::
UNKNOWN_IDENTIFIER
);
}
std
::
string
QueryConverter
::
convertAttributeNumeric
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
{
/** Для атрибутов по регионам, выражение содержит подстановку %s,
* куда должна быть подставлена regions_point_of_view_formatted.
*/
if
(
regions_attributes_set
.
count
(
attribute
))
return
Poco
::
format
(
numeric_attribute_map
.
at
(
attribute
),
regions_point_of_view_formatted
);
if
(
numeric_attribute_map
.
count
(
attribute
))
return
Poco
::
format
(
numeric_attribute_map
.
at
(
attribute
),
parameter
);
throw
Exception
(
"Unknown attribute: "
+
attribute
,
ErrorCodes
::
UNKNOWN_IDENTIFIER
);
}
static
bool
StartsWith
(
const
std
::
string
&
str
,
const
std
::
string
&
prefix
)
{
return
str
.
length
()
>=
prefix
.
length
()
&&
str
.
substr
(
0
,
prefix
.
length
())
==
prefix
;
}
std
::
string
QueryConverter
::
convertAggregateFunction
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
name
,
const
QueryParseResult
&
query
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
{
bool
float_value
=
false
;
/// если включено сэмплирование, то надо умножить агрегатные функции на 1./sample
if
(
name
==
"count"
)
{
if
(
query
.
sample
!=
1
)
{
float_value
=
true
;
return
"sum(Sign)*"
+
toString
(
1.
/
query
.
sample
);
}
else
return
"sum(Sign)"
;
}
std
::
string
numeric
=
convertAttributeNumeric
(
attribute
,
parameter
,
regions_point_of_view_formatted
);
if
(
name
==
"uniq"
||
name
==
"uniq_sort"
||
name
==
"uniq_hash"
||
name
==
"uniq_approx"
||
name
==
"sequental_uniq"
||
StartsWith
(
name
,
"uniq_approx"
))
return
"uniq("
+
numeric
+
")"
;
if
(
name
==
"uniq_state"
)
return
"uniqState("
+
numeric
+
")"
;
if
(
name
==
"uniq_hll12"
)
return
"uniqHLL12("
+
numeric
+
")"
;
if
(
name
==
"uniq_hll12_state"
)
return
"uniqHLL12State("
+
numeric
+
")"
;
if
(
name
==
"count_non_zero"
)
{
if
(
query
.
sample
!=
1
)
{
float_value
=
true
;
return
"sum(("
+
numeric
+
") == 0 ? toInt64(0) : toInt64(Sign)) * "
+
toString
(
1
/
query
.
sample
);
}
else
return
"sum(("
+
numeric
+
") == 0 ? toInt64(0) : toInt64(Sign))"
;
}
if
(
name
==
"count_non_minus_one"
)
{
if
(
query
.
sample
!=
1
)
{
float_value
=
true
;
return
"sum(("
+
numeric
+
") == -1 ? toInt64(0) : toInt64(Sign)) * "
+
toString
(
1
/
query
.
sample
);
}
else
return
"sum(("
+
numeric
+
") == -1 ? toInt64(0) : toInt64(Sign))"
;
}
bool
trivial_format
;
std
::
string
format
;
if
(
formatting_aggregated_attribute_map
.
count
(
attribute
))
{
format
=
formatting_aggregated_attribute_map
.
at
(
attribute
);
trivial_format
=
false
;
}
else
{
format
=
"%s"
;
trivial_format
=
true
;
}
std
::
string
s
;
if
(
name
==
"sum"
)
{
if
(
query
.
sample
!=
1
)
{
s
=
"sum(("
+
numeric
+
") * Sign) * "
+
toString
(
1
/
query
.
sample
);
float_value
=
true
;
}
else
s
=
"sum(("
+
numeric
+
") * Sign)"
;
}
if
(
name
==
"sum_non_minus_one"
)
{
if
(
query
.
sample
!=
1
)
{
s
=
"sum(("
+
numeric
+
") == -1 ? toInt64(0) : toInt64("
+
numeric
+
") * Sign) * "
+
toString
(
1
/
query
.
sample
);
float_value
=
true
;
}
else
s
=
"sum(("
+
numeric
+
") == -1 ? toInt64(0) : toInt64("
+
numeric
+
") * Sign)"
;
}
if
(
name
==
"avg"
)
{
s
=
"sum(("
+
numeric
+
") * Sign) / sum(Sign)"
;
float_value
=
true
;
}
if
(
name
==
"avg_non_zero"
)
{
s
=
"sum(("
+
numeric
+
") * Sign) / sum(("
+
numeric
+
") == 0 ? toInt64(0) : toInt64(Sign))"
;
float_value
=
true
;
}
if
(
name
==
"avg_non_minus_one"
)
{
s
=
"sum(("
+
numeric
+
") == -1 ? toInt64(0) : toInt64("
+
numeric
+
") * Sign) / sum(("
+
numeric
+
") == -1 ? toInt64(0) : toInt64(Sign))"
;
float_value
=
true
;
}
if
(
name
==
"min"
)
s
=
"min("
+
numeric
+
")"
;
if
(
name
==
"max"
)
s
=
"max("
+
numeric
+
")"
;
/// Если агрегатная функция возвращает дробное число, и атрибут имеет нетривиальное форматирование, после агрегации приведем дробное число к целому.
bool
need_cast
=
!
trivial_format
&&
float_value
;
return
Poco
::
format
(
format
,
std
::
string
()
+
(
need_cast
?
"toInt64"
:
""
)
+
"("
+
s
+
")"
);
}
std
::
string
QueryConverter
::
convertConstant
(
const
std
::
string
&
attribute
,
const
std
::
string
&
value
)
const
{
if
(
!
attribute_metadatas
.
count
(
attribute
))
throw
Exception
(
"Unknown attribute "
+
attribute
,
ErrorCodes
::
UNKNOWN_IDENTIFIER
);
return
toString
(
attribute_metadatas
.
at
(
attribute
)
->
parse
(
value
));
}
std
::
string
QueryConverter
::
convertCondition
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
name
,
const
std
::
string
&
rhs
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
{
std
::
string
value
=
convertAttributeNumeric
(
attribute
,
parameter
,
regions_point_of_view_formatted
);
std
::
string
constant
=
convertConstant
(
attribute
,
rhs
);
if
(
name
==
"equals"
)
return
"("
+
value
+
")"
+
" == "
+
constant
;
if
(
name
==
"not_equals"
)
return
"("
+
value
+
")"
+
" != "
+
constant
;
if
(
name
==
"less"
)
return
"("
+
value
+
")"
+
" < "
+
constant
;
if
(
name
==
"greater"
)
return
"("
+
value
+
")"
+
" > "
+
constant
;
if
(
name
==
"less_or_equals"
)
return
"("
+
value
+
")"
+
" <= "
+
constant
;
if
(
name
==
"greater_or_equals"
)
return
"("
+
value
+
")"
+
" >= "
+
constant
;
if
(
name
==
"region_in"
)
return
"regionIn("
+
value
+
", toUInt32("
+
constant
+
")"
+
regions_point_of_view_formatted
+
")"
;
if
(
name
==
"region_not_in"
)
return
"NOT regionIn("
+
value
+
", toUInt32("
+
constant
+
")"
+
regions_point_of_view_formatted
+
")"
;
if
(
name
==
"os_in"
)
return
"OSIn("
+
value
+
", "
+
constant
+
")"
;
if
(
name
==
"os_not_in"
)
return
"NOT OSIn("
+
value
+
", "
+
constant
+
")"
;
if
(
name
==
"se_in"
)
return
"SEIn(toUInt8("
+
value
+
"), toUInt8("
+
constant
+
"))"
;
if
(
name
==
"se_not_in"
)
return
"NOT SEIn(toUInt8("
+
value
+
"), toUInt8("
+
constant
+
"))"
;
if
(
name
==
"interest_has_all_from"
)
return
"bitwiseAnd("
+
value
+
", "
+
constant
+
") == "
+
constant
;
if
(
name
==
"interest_not_has_all_from"
)
return
"bitwiseAnd("
+
value
+
", "
+
constant
+
") != "
+
constant
;
if
(
name
==
"interest_has_any_from"
)
return
"bitwiseAnd("
+
value
+
", "
+
constant
+
") != 0"
;
if
(
name
==
"interest_not_has_any_from"
)
return
"bitwiseAnd("
+
value
+
", "
+
constant
+
") == 0"
;
throw
Exception
(
"Unknown relation "
+
name
,
ErrorCodes
::
UNKNOWN_RELATION
);
}
std
::
string
QueryConverter
::
convertSortDirection
(
const
std
::
string
&
direction
)
const
{
if
(
direction
==
"descending"
)
return
"DESC"
;
else
return
"ASC"
;
}
std
::
string
QueryConverter
::
convertDateRange
(
time_t
date_first
,
time_t
date_last
)
const
{
std
::
string
first_str
;
std
::
string
last_str
;
{
WriteBufferFromString
first_buf
(
first_str
);
WriteBufferFromString
last_buf
(
last_str
);
writeDateText
(
DateLUT
::
instance
().
toDayNum
(
date_first
),
first_buf
);
writeDateText
(
DateLUT
::
instance
().
toDayNum
(
date_last
),
last_buf
);
}
return
"StartDate >= toDate('"
+
first_str
+
"') AND StartDate <= toDate('"
+
last_str
+
"')"
;
}
std
::
string
QueryConverter
::
convertCounterID
(
CounterID_t
CounterID
)
const
{
return
"CounterID == "
+
toString
(
CounterID
);
}
std
::
string
QueryConverter
::
getTableName
(
CounterID_t
CounterID
,
bool
local
)
const
{
if
(
CounterID
==
0
&&
!
local
)
return
table_for_all_counters
;
else
return
table_for_single_counter
;
}
std
::
string
QueryConverter
::
getHavingSection
()
const
{
return
"HAVING sum(Sign) > 0"
;
}
void
QueryConverter
::
fillNumericAttributeMap
()
{
#define M(a, b) numeric_attribute_map[a] = b;
M
(
"DummyAttribute"
,
"0"
)
M
(
"VisitStartDateTime"
,
"toInt32(StartTime)"
)
M
(
"VisitStartDateTimeRoundedToMinute"
,
"toInt32(toStartOfMinute(StartTime))"
)
M
(
"VisitStartDateTimeRoundedToHour"
,
"toInt32(toStartOfHour(StartTime))"
)
M
(
"VisitStartDate"
,
"toInt32(toDateTime(StartDate))"
)
M
(
"VisitStartDateRoundedToMonth"
,
"toInt32(toDateTime(toStartOfMonth(StartDate)))"
)
M
(
"VisitStartWeek"
,
"toInt32(toDateTime(toMonday(StartDate)))"
)
M
(
"VisitStartTime"
,
"toInt32(toTime(StartTime))"
)
M
(
"VisitStartTimeRoundedToMinute"
,
"toInt32(toStartOfMinute(toTime(StartTime)))"
)
M
(
"VisitStartYear"
,
"toYear(StartDate)"
)
M
(
"VisitStartMonth"
,
"toMonth(StartDate)"
)
M
(
"VisitStartDayOfWeek"
,
"toDayOfWeek(StartDate)"
)
M
(
"VisitStartDayOfMonth"
,
"toDayOfMonth(StartDate)"
)
M
(
"VisitStartHour"
,
"toHour(StartTime)"
)
M
(
"VisitStartMinute"
,
"toMinute(StartTime)"
)
M
(
"VisitStartSecond"
,
"toSecond(StartTime)"
)
M
(
"FirstVisitDateTime"
,
"toInt32(FirstVisit)"
)
M
(
"FirstVisitDate"
,
"toInt32(toDateTime(toDate(FirstVisit)))"
)
M
(
"FirstVisitWeek"
,
"toInt32(toDateTime(toMonday(FirstVisit)))"
)
M
(
"FirstVisitTime"
,
"toInt32(toTime(FirstVisit))"
)
M
(
"FirstVisitYear"
,
"toYear(FirstVisit)"
)
M
(
"FirstVisitMonth"
,
"toMonth(FirstVisit)"
)
M
(
"FirstVisitDayOfWeek"
,
"toDayOfWeek(FirstVisit)"
)
M
(
"FirstVisitDayOfMonth"
,
"toDayOfMonth(FirstVisit)"
)
M
(
"FirstVisitHour"
,
"toHour(FirstVisit)"
)
M
(
"FirstVisitMinute"
,
"toMinute(FirstVisit)"
)
M
(
"FirstVisitSecond"
,
"toSecond(FirstVisit)"
)
M
(
"PredLastVisitDate"
,
"toInt32(toDateTime(PredLastVisit))"
)
M
(
"PredLastVisitWeek"
,
"toInt32(toDateTime(toMonday(PredLastVisit)))"
)
M
(
"PredLastVisitYear"
,
"toYear(PredLastVisit)"
)
M
(
"PredLastVisitMonth"
,
"toMonth(PredLastVisit)"
)
M
(
"PredLastVisitDayOfWeek"
,
"toDayOfWeek(PredLastVisit)"
)
M
(
"PredLastVisitDayOfMonth"
,
"toDayOfMonth(PredLastVisit)"
)
M
(
"ClientDateTime"
,
"toInt32(ClientEventTime)"
)
M
(
"ClientTime"
,
"toInt32(toTime(ClientEventTime))"
)
M
(
"ClientTimeHour"
,
"toHour(ClientEventTime)"
)
M
(
"ClientTimeMinute"
,
"toMinute(ClientEventTime)"
)
M
(
"ClientTimeSecond"
,
"toSecond(ClientEventTime)"
)
M
(
"SearchPhraseHash"
,
"SearchPhraseHash"
)
M
(
"RefererDomainHash"
,
"RefererDomainHash"
)
M
(
"StartURLHash"
,
"NormalizedStartURLHash"
)
M
(
"StartURLDomainHash"
,
"StartURLDomainHash"
)
M
(
"RegionID"
,
"RegionID"
)
M
(
"RegionCity"
,
"regionToCity(RegionID%s)"
)
M
(
"RegionArea"
,
"regionToArea(RegionID%s)"
)
M
(
"RegionCountry"
,
"regionToCountry(RegionID%s)"
)
M
(
"TraficSourceID"
,
"TraficSourceID"
)
M
(
"IsNewUser"
,
"intDiv(toUInt32(FirstVisit), 1800) == intDiv(toUInt32(StartTime), 1800)"
)
M
(
"UserNewness"
,
"intDiv(toUInt64(StartTime)-toUInt64(FirstVisit), 86400)"
)
M
(
"UserNewnessInterval"
,
"roundToExp2(intDiv(toUInt64(StartTime)-toUInt64(FirstVisit), 86400))"
)
M
(
"UserReturnTime"
,
"toInt32(toDate(StartTime))-toInt32(PredLastVisit)"
)
M
(
"UserReturnTimeInterval"
,
"roundToExp2(toInt32(toDate(StartTime))-toInt32(PredLastVisit))"
)
M
(
"UserVisitsPeriod"
,
"(TotalVisits <= 1 ? toUInt16(0) : toUInt16((toInt64(StartTime)-toInt64(FirstVisit)) / (86400 * (TotalVisits - 1))))"
)
M
(
"UserVisitsPeriodInterval"
,
"(TotalVisits <= 1 ? toUInt16(0) : roundToExp2(toUInt16((toInt64(StartTime)-toInt64(FirstVisit)) / (86400 * (TotalVisits - 1)))))"
)
M
(
"VisitTime"
,
"Duration"
)
M
(
"VisitTimeInterval"
,
"roundDuration(Duration)"
)
M
(
"PageViews"
,
"PageViews"
)
M
(
"PageViewsInterval"
,
"roundToExp2(PageViews)"
)
M
(
"Bounce"
,
"PageViews <= 1"
)
M
(
"BouncePrecise"
,
"IsBounce"
)
M
(
"IsYandex"
,
"IsYandex"
)
M
(
"UserID"
,
"UserID"
)
M
(
"UserIDCreateDateTime"
,
"(UserID > 10000000000000000000 OR UserID %% 10000000000 > 2000000000 OR UserID %% 10000000000 < 1000000000 ? toUInt64(0) : UserID %% 10000000000)"
)
M
(
"UserIDCreateDate"
,
"(UserID > 10000000000000000000 OR UserID %% 10000000000 > 2000000000 OR UserID %% 10000000000 < 1000000000 ? toUInt64(0) : UserID %% 10000000000)"
)
M
(
"UserIDAge"
,
"(UserID > 10000000000000000000 OR UserID %% 10000000000 < 1000000000 OR UserID %% 10000000000 > toUInt64(StartTime) ? toInt64(-1) : intDiv(toInt64(StartTime) - UserID %% 10000000000, 86400))"
)
M
(
"UserIDAgeInterval"
,
"(UserID > 10000000000000000000 OR UserID %% 10000000000 < 1000000000 OR UserID %% 10000000000 > toUInt64(StartTime) ? toInt64(-1) : toInt64(roundToExp2(intDiv(toUInt64(StartTime) - UserID %% 10000000000, 86400))))"
)
M
(
"TotalVisits"
,
"TotalVisits"
)
M
(
"TotalVisitsInterval"
,
"roundToExp2(TotalVisits)"
)
M
(
"Age"
,
"Age"
)
M
(
"AgeInterval"
,
"roundAge(Age)"
)
M
(
"Sex"
,
"Sex"
)
M
(
"Income"
,
"Income"
)
M
(
"AdvEngineID"
,
"AdvEngineID"
)
M
(
"DotNet"
,
"NetMajor * 256 + NetMinor"
)
M
(
"DotNetMajor"
,
"NetMajor"
)
M
(
"Flash"
,
"FlashMajor * 256 + FlashMinor"
)
M
(
"FlashExists"
,
"FlashMajor > 0"
)
M
(
"FlashMajor"
,
"FlashMajor"
)
M
(
"Silverlight"
,
"SilverlightVersion1 * 72057594037927936 + SilverlightVersion2 * 281474976710656 + SilverlightVersion3 * 65536 + SilverlightVersion4"
)
M
(
"SilverlightMajor"
,
"SilverlightVersion1"
)
M
(
"Hits"
,
"Hits"
)
M
(
"HitsInterval"
,
"roundToExp2(Hits)"
)
M
(
"JavaEnable"
,
"JavaEnable"
)
M
(
"CookieEnable"
,
"CookieEnable"
)
M
(
"JavascriptEnable"
,
"JavascriptEnable"
)
M
(
"IsMobile"
,
"IsMobile"
)
M
(
"MobilePhoneID"
,
"MobilePhone"
)
M
(
"MobilePhoneModelHash"
,
"halfMD5(MobilePhoneModel)"
)
M
(
"MobilePhoneModel"
,
"reinterpretAsUInt64(MobilePhoneModel)"
)
M
(
"BrowserLanguage"
,
"BrowserLanguage"
)
M
(
"BrowserCountry"
,
"BrowserCountry"
)
M
(
"TopLevelDomain"
,
"TopLevelDomain"
)
M
(
"URLScheme"
,
"URLScheme"
)
M
(
"IPNetworkID"
,
"IPNetworkID"
)
M
(
"ClientTimeZone"
,
"ClientTimeZone"
)
M
(
"OSID"
,
"OS"
)
M
(
"OSMostAncestor"
,
"OSToRoot(OS)"
)
M
(
"ClientIP"
,
"ClientIP"
)
M
(
"Resolution"
,
"ResolutionWidth * 16777216 + ResolutionHeight * 256 + ResolutionDepth"
)
M
(
"ResolutionWidthHeight"
,
"ResolutionWidth * 65536 + ResolutionHeight"
)
M
(
"ResolutionWidth"
,
"ResolutionWidth"
)
M
(
"ResolutionHeight"
,
"ResolutionHeight"
)
M
(
"ResolutionWidthInterval"
,
"intDiv(ResolutionWidth, 100) * 100"
)
M
(
"ResolutionHeightInterval"
,
"intDiv(ResolutionHeight, 100) * 100"
)
M
(
"ResolutionColor"
,
"ResolutionDepth"
)
M
(
"WindowClientArea"
,
"WindowClientWidth * 65536 + WindowClientHeight"
)
M
(
"WindowClientAreaInterval"
,
"intDiv(WindowClientWidth, 100) * 6553600 + intDiv(WindowClientHeight, 100) * 100"
)
M
(
"WindowClientWidth"
,
"WindowClientWidth"
)
M
(
"WindowClientWidthInterval"
,
"intDiv(WindowClientWidth, 100) * 100"
)
M
(
"WindowClientHeight"
,
"WindowClientHeight"
)
M
(
"WindowClientHeightInterval"
,
"intDiv(WindowClientHeight, 100) * 100"
)
M
(
"SearchEngineID"
,
"SearchEngineID"
)
M
(
"SearchEngineMostAncestor"
,
"SEToRoot(toUInt8(SearchEngineID))"
)
M
(
"CodeVersion"
,
"CodeVersion"
)
M
(
"UserAgent"
,
"UserAgent * 16777216 + UserAgentMajor * 65536 + UserAgentMinor"
)
M
(
"UserAgentVersion"
,
"UserAgentMajor * 65536 + UserAgentMinor"
)
M
(
"UserAgentMajor"
,
"UserAgent * 256 + UserAgentMajor"
)
M
(
"UserAgentID"
,
"UserAgent"
)
M
(
"ClickGoodEvent"
,
"ClickGoodEvent"
)
M
(
"ClickPriorityID"
,
"ClickPriorityID"
)
M
(
"ClickBannerID"
,
"ClickBannerID"
)
M
(
"ClickPageID"
,
"ClickPageID"
)
M
(
"ClickPlaceID"
,
"ClickPlaceID"
)
M
(
"ClickTypeID"
,
"ClickTypeID"
)
M
(
"ClickResourceID"
,
"ClickResourceID"
)
M
(
"ClickDomainID"
,
"ClickDomainID"
)
M
(
"ClickCost"
,
"ClickCost"
)
M
(
"ClickURLHash"
,
"ClickURLHash"
)
M
(
"ClickOrderID"
,
"ClickOrderID"
)
M
(
"GoalReachesAny"
,
"GoalReachesAny"
)
M
(
"GoalReachesDepth"
,
"GoalReachesDepth"
)
M
(
"GoalReachesURL"
,
"GoalReachesURL"
)
M
(
"ConvertedAny"
,
"(GoalReachesAny > 1 ? toInt32(1) : GoalReachesAny)"
)
M
(
"ConvertedDepth"
,
"(GoalReachesDepth > 1 ? toInt32(1) : GoalReachesDepth)"
)
M
(
"ConvertedURL"
,
"(GoalReachesURL > 1 ? toInt32(1) : GoalReachesURL)"
)
M
(
"GoalReaches"
,
"countEqual(Goals.ID, toUInt32(%u))"
)
M
(
"Converted"
,
"has(Goals.ID, toUInt32(%u))"
)
M
(
"CounterID"
,
"CounterID"
)
M
(
"VisitID"
,
"VisitID"
)
M
(
"Interests"
,
"Interests"
)
M
(
"HasInterestPhoto"
,
"modulo(intDiv(Interests, 128), 2)"
)
M
(
"HasInterestMoviePremieres"
,
"modulo(intDiv(Interests, 64), 2)"
)
M
(
"HasInterestTourism"
,
"modulo(intDiv(Interests, 32), 2)"
)
M
(
"HasInterestFamilyAndChildren"
,
"modulo(intDiv(Interests, 16), 2)"
)
M
(
"HasInterestFinance"
,
"modulo(intDiv(Interests, 8), 2)"
)
M
(
"HasInterestB2B"
,
"modulo(intDiv(Interests, 4), 2)"
)
M
(
"HasInterestCars"
,
"modulo(intDiv(Interests, 2), 2)"
)
M
(
"HasInterestMobileAndInternetCommunications"
,
"modulo(Interests, 2)"
)
M
(
"HasInterestBuilding"
,
"modulo(intDiv(Interests, 256), 2)"
)
M
(
"HasInterestCulinary"
,
"modulo(intDiv(Interests, 512), 2)"
)
M
(
"OpenstatServiceNameHash"
,
"OpenstatServiceNameHash"
)
M
(
"OpenstatCampaignIDHash"
,
"OpenstatCampaignIDHash"
)
M
(
"OpenstatAdIDHash"
,
"OpenstatAdIDHash"
)
M
(
"OpenstatSourceIDHash"
,
"OpenstatSourceIDHash"
)
M
(
"UTMSourceHash"
,
"UTMSourceHash"
)
M
(
"UTMMediumHash"
,
"UTMMediumHash"
)
M
(
"UTMCampaignHash"
,
"UTMCampaignHash"
)
M
(
"UTMContentHash"
,
"UTMContentHash"
)
M
(
"UTMTermHash"
,
"UTMTermHash"
)
M
(
"FromHash"
,
"FromHash"
)
M
(
"CLID"
,
"CLID"
)
M
(
"SocialSourceNetworkID"
,
"SocialSourceNetworkID"
)
/// где 26 это Яндекс (db_dumps/SearchEngines).
M
(
"CorrectedTraficSourceID"
,
"(IsYandex AND SEIn(toUInt8(SearchEngineID), 26)) ? -1 : TraficSourceID"
)
M
(
"CorrectedSearchEngineID"
,
"(IsYandex AND SEIn(toUInt8(SearchEngineID), 26)) ? 0 : toUInt8(SearchEngineID)"
)
#undef M
}
void
QueryConverter
::
fillFormattedAttributeMap
()
{
#define M(a, b) formatted_attribute_map[a] = b;
M
(
"VisitStartDateTime"
,
"StartTime"
)
M
(
"VisitStartDate"
,
"StartDate"
)
M
(
"VisitStartWeek"
,
"toMonday(StartDate)"
)
M
(
"VisitStartTime"
,
"substring(toString(toTime(StartTime)), 12, 8)"
)
M
(
"VisitStartDateTimeRoundedToMinute"
,
"toStartOfMinute(StartTime)"
)
M
(
"VisitStartDateTimeRoundedToHour"
,
"toStartOfHour(StartTime)"
)
M
(
"VisitStartDateRoundedToMonth"
,
"toDateTime(toStartOfMonth(StartDate))"
)
M
(
"VisitStartTimeRoundedToMinute"
,
"substring(toString(toStartOfMinute(toTime(StartTime))), 12, 8)"
)
M
(
"FirstVisitDateTime"
,
"FirstVisit"
)
M
(
"FirstVisitDate"
,
"toDate(FirstVisit)"
)
M
(
"FirstVisitWeek"
,
"toMonday(FirstVisit)"
)
M
(
"FirstVisitTime"
,
"substring(toString(FirstVisit), 12, 8)"
)
M
(
"PredLastVisitDate"
,
"PredLastVisit"
)
M
(
"PredLastVisitWeek"
,
"toMonday(PredLastVisit)"
)
M
(
"ClientDateTime"
,
"ClientEventTime"
)
M
(
"ClientTime"
,
"substring(toString(ClientEventTime), 12, 8)"
)
M
(
"DotNet"
,
"concat(concat(toString(NetMajor), '.'), toString(NetMinor))"
)
M
(
"Flash"
,
"concat(concat(toString(FlashMajor),'.'),toString(FlashMinor))"
)
M
(
"Silverlight"
,
"concat(concat(concat(concat(concat(concat(toString(SilverlightVersion1), '.'), toString(SilverlightVersion2)), '.'), toString(SilverlightVersion3)), '.'), toString(SilverlightVersion4))"
)
M
(
"MobilePhoneModel"
,
"MobilePhoneModel"
)
M
(
"ClientIP"
,
"IPv4NumToString(ClientIP)"
)
M
(
"Resolution"
,
"concat(concat(concat(concat(toString(ResolutionWidth),'x'),toString(ResolutionHeight)),'x'),toString(ResolutionDepth))"
)
M
(
"ResolutionWidthHeight"
,
"concat(concat(toString(ResolutionWidth),'x'),toString(ResolutionHeight))"
)
M
(
"WindowClientArea"
,
"concat(concat(toString(WindowClientWidth),'x'),toString(WindowClientHeight))"
)
M
(
"UserAgent"
,
"concat(concat(concat(toString(UserAgent), ' '), toString(UserAgentMajor)), UserAgentMinor == 0 ? '' : concat('.', reinterpretAsString(UserAgentMinor)))"
)
M
(
"UserAgentVersion"
,
"concat(toString(UserAgentMajor), UserAgentMinor == 0 ? '' : concat('.', reinterpretAsString(UserAgentMinor)))"
)
M
(
"UserAgentMajor"
,
"concat(concat(toString(UserAgent), ' '), toString(UserAgentMajor))"
)
#undef M
}
void
QueryConverter
::
fillFormattingAggregatedAttributeMap
()
{
#define M(a, b) formatting_aggregated_attribute_map[a] = b;
std
::
string
todate
=
"toDate(toDateTime(%s))"
;
std
::
string
todatetime
=
"toDateTime(%s)"
;
std
::
string
cuttime
=
"substring(toString(toDateTime(%s)), 12, 8)"
;
std
::
string
tostring
=
"reinterpretAsString(%s)"
;
M
(
"VisitStartDateTime"
,
todatetime
)
M
(
"VisitStartDate"
,
todate
)
M
(
"VisitStartWeek"
,
todate
)
M
(
"VisitStartTime"
,
cuttime
)
M
(
"VisitStartDateTimeRoundedToMinute"
,
todatetime
)
M
(
"VisitStartDateTimeRoundedToHour"
,
todatetime
)
M
(
"VisitStartDateRoundedToMonth"
,
todate
)
M
(
"VisitStartTimeRoundedToMinute"
,
cuttime
)
M
(
"FirstVisitDateTime"
,
todatetime
)
M
(
"FirstVisitDate"
,
todate
)
M
(
"FirstVisitWeek"
,
todate
)
M
(
"FirstVisitTime"
,
cuttime
)
M
(
"PredLastVisitDate"
,
todate
)
M
(
"PredLastVisitWeek"
,
todate
)
M
(
"ClientDateTime"
,
todatetime
)
M
(
"ClientTime"
,
cuttime
)
M
(
"UserIDCreateDateTime"
,
todatetime
)
M
(
"UserIDCreateDate"
,
todate
)
M
(
"DotNet"
,
"concat(concat(toString(intDiv(toUInt32(%[0]s), 256)), '.'), toString(modulo(toUInt32(%[0]s), 256)))"
)
M
(
"Flash"
,
"concat(concat(toString(intDiv(toUInt32(%[0]s), 256)), '.'), toString(modulo(toUInt32(%[0]s), 256)))"
)
M
(
"Silverlight"
,
"concat(concat(concat(concat(concat(concat(toString(intDiv(toUInt64(%[0]s), 72057594037927936)), '.'), toString(modulo(intDiv(toUInt64(%[0]s), 281474976710656), 256))), '.'), toString(modulo(intDiv(toUInt64(%[0]s), 65536), 4294967296))), '.'), toString(modulo(toUInt64(%[0]s), 65536)))"
)
M
(
"MobilePhoneModel"
,
tostring
)
M
(
"BrowserLanguage"
,
tostring
)
M
(
"BrowserCountry"
,
tostring
)
M
(
"TopLevelDomain"
,
tostring
)
M
(
"URLScheme"
,
tostring
)
M
(
"ClientIP"
,
"IPv4NumToString(%[0]s)"
)
M
(
"Resolution"
,
"concat(concat(concat(concat(toString(intDiv(toUInt64(%[0]s), 16777216)),'x'),toString(intDiv(toUInt64(%[0]s), 256) %% 65536)),'x'),toString(toUInt64(%[0]s) %% 256))"
)
M
(
"ResolutionWidthHeight"
,
"concat(concat(toString(intDiv(toUInt64(%[0]s), 65536)),'x'),toString(toUInt64(%[0]s) %% 65536))"
)
M
(
"WindowClientArea"
,
"concat(concat(toString(intDiv(toUInt64(%[0]s), 65536)),'x'),toString(toUInt64(%[0]s) %% 65536))"
)
M
(
"UserAgent"
,
"concat(concat(concat(toString(intDiv(toUInt32(%[0]s), 16777216)), ' '), toString(intDiv(toUInt32(%[0]s), 65536) %% 256)), (toUInt32(%[0]s) %% 65536) == 0 ? '' : concat('.', reinterpretAsString(toUInt32(%[0]s) %% 65536)))"
)
M
(
"UserAgentVersion"
,
"concat(toString(intDiv(toUInt32(%[0]s), 65536)), (toUInt32(%[0]s) %% 65536) == 0 ? '' : concat('.', reinterpretAsString(toUInt32(%[0]s) %% 65536)))"
)
M
(
"UserAgentMajor"
,
"concat(concat(toString(intDiv(toUInt32(%[0]s), 256)), ' '), toString(toUInt32(%[0]s) %% 256))"
)
M
(
"Interests"
,
"bitmaskToList(%s)"
)
#undef M
}
}
}
dbms/src/Server/OLAPQueryConverter.h
已删除
100644 → 0
浏览文件 @
c6da35b7
#pragma once
#include "OLAPQueryParser.h"
#include <DB/Interpreters/Context.h>
#include <Poco/Util/AbstractConfiguration.h>
#include "OLAPAttributesMetadata.h"
namespace
DB
{
namespace
OLAP
{
/// Конвертирует распаршенный XML-запрос в формате OLAP-server в SQL-подобный запрос для clickhouse.
class
QueryConverter
{
public:
QueryConverter
(
Poco
::
Util
::
AbstractConfiguration
&
config
);
QueryConverter
(
const
String
&
table_for_single_counter
,
const
String
&
table_for_all_counters
);
/// Получает из запроса в формате OLAP-server запрос и настройки для clickhouse.
void
OLAPServerQueryToClickHouse
(
const
QueryParseResult
&
query
,
Context
&
inout_context
,
std
::
string
&
out_query
)
const
;
private:
/// Значение атрибута, подходящее для вывода в ответ и для группировки по нему.
std
::
string
convertAttributeFormatted
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
;
/// Числовое значение атрибута, подходящее для подстановки в условия, агрегатные функции и ключи сортировки.
std
::
string
convertAttributeNumeric
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
;
/// <aggregates><aggregate> => SELECT x
std
::
string
convertAggregateFunction
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
function
,
const
QueryParseResult
&
query
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
;
/// <where><condition><rhs> => SELECT ... where F(A, x)
std
::
string
convertConstant
(
const
std
::
string
&
attribute
,
const
std
::
string
&
value
)
const
;
/// <where><condition> => SELECT ... WHERE x
std
::
string
convertCondition
(
const
std
::
string
&
attribute
,
unsigned
parameter
,
const
std
::
string
&
relation
,
const
std
::
string
&
rhs
,
const
std
::
string
&
regions_point_of_view_formatted
)
const
;
/// ASC или DESC
std
::
string
convertSortDirection
(
const
std
::
string
&
direction
)
const
;
/// <dates> => SELECT ... WHERE x
std
::
string
convertDateRange
(
time_t
date_first
,
time_t
date_last
)
const
;
/// <counter_id> => SELECT ... WHERE x
std
::
string
convertCounterID
(
CounterID_t
CounterID
)
const
;
std
::
string
getTableName
(
CounterID_t
CounterID
,
bool
local
)
const
;
std
::
string
getHavingSection
()
const
;
void
fillFormattedAttributeMap
();
void
fillNumericAttributeMap
();
void
fillFormattingAggregatedAttributeMap
();
std
::
string
table_for_single_counter
;
std
::
string
table_for_all_counters
;
/// Форматная строка для convertAttributeNumeric. Есть для всех атрибутов.
std
::
map
<
std
::
string
,
std
::
string
>
numeric_attribute_map
;
/// Форматная строка для получения выводимого значения из агрегированного числового значения.
std
::
map
<
std
::
string
,
std
::
string
>
formatting_aggregated_attribute_map
;
/// Форматная строка для convertAttributeFormatted.
std
::
map
<
std
::
string
,
std
::
string
>
formatted_attribute_map
;
/// Список атрибутов-регионов, для которых нужна передача параметра regions_point_of_view.
std
::
set
<
std
::
string
>
regions_attributes_set
=
{
"RegionCity"
,
"RegionArea"
,
"RegionCountry"
,
};
/// Парсеры значений атрибутов.
AttributeMetadatas
attribute_metadatas
;
};
}
}
dbms/src/Server/OLAPQueryParser.cpp
已删除
100644 → 0
浏览文件 @
c6da35b7
#include <common/DateLUT.h>
#include <Poco/DateTimeParser.h>
#include <Poco/AutoPtr.h>
#include "OLAPQueryParser.h"
#include <DB/Common/Exception.h>
#include <DB/IO/ReadHelpers.h>
#include <DB/IO/WriteHelpers.h>
namespace
DB
{
namespace
ErrorCodes
{
extern
const
int
NOT_FOUND_NODE
;
extern
const
int
FOUND_MORE_THAN_ONE_NODE
;
extern
const
int
SYNTAX_ERROR
;
extern
const
int
UNKNOWN_FORMAT
;
extern
const
int
FIRST_DATE_IS_BIGGER_THAN_LAST_DATE
;
extern
const
int
UNKNOWN_OVERFLOW_MODE
;
extern
const
int
NOT_FOUND_FUNCTION_ELEMENT_FOR_AGGREGATE
;
extern
const
int
NOT_FOUND_RELATION_ELEMENT_FOR_CONDITION
;
extern
const
int
NOT_FOUND_RHS_ELEMENT_FOR_CONDITION
;
extern
const
int
NO_ATTRIBUTES_LISTED
;
extern
const
int
UNKNOWN_DIRECTION_OF_SORTING
;
extern
const
int
INDEX_OF_COLUMN_IN_SORT_CLAUSE_IS_OUT_OF_RANGE
;
}
namespace
OLAP
{
static
std
::
string
getValueOfOneTextElement
(
Poco
::
XML
::
Document
*
document
,
const
std
::
string
&
tag_name
)
{
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
node_list
=
document
->
getElementsByTagName
(
tag_name
);
if
(
0
==
node_list
->
length
())
throw
Exception
(
std
::
string
(
"Not found node "
)
+
tag_name
,
ErrorCodes
::
NOT_FOUND_NODE
);
else
if
(
1
!=
node_list
->
length
())
throw
Exception
(
std
::
string
(
"Found more than one node "
)
+
tag_name
,
ErrorCodes
::
FOUND_MORE_THAN_ONE_NODE
);
return
node_list
->
item
(
0
)
->
innerText
();
}
QueryParser
::
AttributeWithParameter
QueryParser
::
parseAttributeWithParameter
(
const
std
::
string
&
s
)
{
AttributeWithParameter
res
;
Poco
::
RegularExpression
::
MatchVec
matches
;
if
(
parse_attribute_with_parameter_regexp
.
match
(
s
,
0
,
matches
))
{
if
(
matches
.
size
()
==
3
)
{
res
.
first
=
s
.
substr
(
matches
[
1
].
offset
,
matches
[
1
].
length
);
res
.
second
=
DB
::
parse
<
unsigned
>
(
s
.
substr
(
matches
[
2
].
offset
,
matches
[
2
].
length
));
return
res
;
}
}
throw
Exception
(
std
::
string
(
"Invalid attribute syntax: "
)
+
s
,
ErrorCodes
::
SYNTAX_ERROR
);
}
QueryParseResult
QueryParser
::
parse
(
std
::
istream
&
s
)
{
QueryParseResult
result
;
Poco
::
XML
::
DOMParser
parser
;
Poco
::
XML
::
InputSource
source
(
s
);
result
.
max_result_size
=
0
;
result
.
max_execution_time
=
0
;
result
.
sample
=
1.0
;
result
.
query
=
parser
.
parse
(
&
source
);
std
::
string
format_element_name
(
"format"
);
std
::
string
CounterID_element_name
(
"counter_id"
);
std
::
string
date_first_element_name
(
"first"
);
std
::
string
date_last_element_name
(
"last"
);
result
.
format
=
FORMAT_XML
;
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
node_list
=
result
.
query
->
getElementsByTagName
(
format_element_name
);
if
(
node_list
->
length
()
>
1
)
throw
Exception
(
std
::
string
(
"Found more than one node "
)
+
format_element_name
,
ErrorCodes
::
FOUND_MORE_THAN_ONE_NODE
);
if
(
node_list
->
length
()
==
1
)
{
if
(
node_list
->
item
(
0
)
->
innerText
()
!=
"xml"
&&
node_list
->
item
(
0
)
->
innerText
()
!=
"tab"
&&
node_list
->
item
(
0
)
->
innerText
()
!=
"bin"
)
throw
Exception
(
std
::
string
(
"Unknown format: "
)
+
node_list
->
item
(
0
)
->
innerText
(),
ErrorCodes
::
UNKNOWN_FORMAT
);
result
.
format
=
(
node_list
->
item
(
0
)
->
innerText
()
==
"xml"
)
?
FORMAT_XML
:
((
node_list
->
item
(
0
)
->
innerText
()
==
"tab"
)
?
FORMAT_TAB
:
FORMAT_BIN
);
}
result
.
CounterID
=
0
;
if
(
result
.
query
->
getElementsByTagName
(
CounterID_element_name
)
->
length
()
>
0
)
result
.
CounterID
=
DB
::
parse
<
unsigned
>
(
getValueOfOneTextElement
(
result
.
query
,
CounterID_element_name
));
int
time_zone_diff
=
0
;
result
.
date_first
=
DateLUT
::
instance
().
toDate
(
Poco
::
DateTimeParser
::
parse
(
getValueOfOneTextElement
(
result
.
query
,
date_first_element_name
),
time_zone_diff
).
timestamp
().
epochTime
());
result
.
date_last
=
DateLUT
::
instance
().
toDate
(
Poco
::
DateTimeParser
::
parse
(
getValueOfOneTextElement
(
result
.
query
,
date_last_element_name
),
time_zone_diff
).
timestamp
().
epochTime
());
if
(
result
.
date_first
>
result
.
date_last
)
throw
Exception
(
"First date is bigger than last date."
,
ErrorCodes
::
FIRST_DATE_IS_BIGGER_THAN_LAST_DATE
);
const
auto
&
date_lut
=
DateLUT
::
instance
();
result
.
days
=
1
+
date_lut
.
toDayNum
(
result
.
date_last
)
-
date_lut
.
toDayNum
(
result
.
date_first
);
result
.
cut_date_last
=
false
;
result
.
cut_dates_for_goals
=
false
;
result
.
concurrency
=
0
;
result
.
max_threads_per_counter
=
0
;
result
.
limit
=
0
;
result
.
local
=
false
;
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
settings_nodes
=
result
.
query
->
getElementsByTagName
(
"settings"
);
if
(
settings_nodes
->
length
()
>
1
)
throw
Exception
(
std
::
string
(
"Found more than one node settings"
),
ErrorCodes
::
FOUND_MORE_THAN_ONE_NODE
);
if
(
settings_nodes
->
length
()
==
1
)
{
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
settings_child_nodes
=
settings_nodes
->
item
(
0
)
->
childNodes
();
for
(
unsigned
i
=
0
;
i
<
settings_child_nodes
->
length
();
i
++
)
{
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"max_result_size"
)
{
/// выставить дополнительное локальное ограничение на максимальный размер результата
result
.
max_result_size
=
DB
::
parse
<
unsigned
>
(
settings_child_nodes
->
item
(
i
)
->
innerText
());
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"max_execution_time"
)
{
/// выставить дополнительное локальное ограничение на максимальное время выполнения запроса
result
.
max_execution_time
=
DB
::
parse
<
unsigned
>
(
settings_child_nodes
->
item
(
i
)
->
innerText
());
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"cut_date_last"
)
{
/** обрезать запрошенный период до максимальной даты, за которую есть данные
* вместо того, чтобы сообщить об ошибке, если дата конца периода больше максимальной даты
*/
result
.
cut_date_last
=
true
;
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"cut_dates_for_goals"
)
{
/** если за какой-либо день не существовало цели, то пропускать этот день
*/
result
.
cut_dates_for_goals
=
true
;
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"overflow_mode"
)
{
/** определяет, что делать, если количество строк превышает max_result_size
*/
std
::
string
overflow_mode_str
=
settings_child_nodes
->
item
(
i
)
->
innerText
();
if
(
overflow_mode_str
!=
"throw"
&&
overflow_mode_str
!=
"break"
&&
overflow_mode_str
!=
"any"
)
throw
Exception
(
std
::
string
(
"Unknown overflow mode: "
)
+
overflow_mode_str
,
ErrorCodes
::
UNKNOWN_OVERFLOW_MODE
);
result
.
has_overflow_mode
=
true
;
result
.
overflow_mode
=
overflow_mode_str
==
"throw"
?
OVERFLOW_MODE_THROW
:
(
overflow_mode_str
==
"break"
?
OVERFLOW_MODE_BREAK
:
OVERFLOW_MODE_ANY
);
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"concurrency"
)
{
/// выставить количество потоков для обработки запроса
result
.
concurrency
=
DB
::
parse
<
unsigned
>
(
settings_child_nodes
->
item
(
i
)
->
innerText
());
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"max_threads_per_counter"
)
{
/** Выставить локальное ограничение на максимальное количество обрабатываемых запросов
* Оно может быть больше, чем ограничение по умолчанию.
*/
result
.
max_threads_per_counter
=
DB
::
parse
<
unsigned
>
(
settings_child_nodes
->
item
(
i
)
->
innerText
());
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"local"
)
{
result
.
local
=
true
;
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"sample"
)
{
result
.
sample
=
DB
::
parse
<
Float32
>
(
settings_child_nodes
->
item
(
i
)
->
innerText
());
if
(
result
.
sample
<=
0
||
result
.
sample
>
1.
)
throw
Exception
(
std
::
string
(
"Wrong sample = "
)
+
DB
::
toString
(
result
.
sample
)
+
". Sampling must be in range (0, 1]"
);
}
else
if
(
settings_child_nodes
->
item
(
i
)
->
nodeName
()
==
"regions_point_of_view"
)
{
result
.
regions_point_of_view
=
settings_child_nodes
->
item
(
i
)
->
innerText
();
}
}
}
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
limit_nodes
=
result
.
query
->
getElementsByTagName
(
"limit"
);
if
(
limit_nodes
->
length
()
>
1
)
throw
Exception
(
std
::
string
(
"Found more than one node limit"
),
ErrorCodes
::
FOUND_MORE_THAN_ONE_NODE
);
if
(
limit_nodes
->
length
()
==
1
)
result
.
limit
=
DB
::
parse
<
unsigned
>
(
limit_nodes
->
item
(
0
)
->
innerText
());
LOG_DEBUG
(
log
,
"CounterID: "
<<
result
.
CounterID
<<
", dates: "
<<
LocalDate
(
result
.
date_first
)
<<
" - "
<<
LocalDate
(
result
.
date_last
));
/// получаем список имён атрибутов
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
attributes
=
result
.
query
->
getElementsByTagName
(
"attribute"
);
for
(
unsigned
i
=
0
;
i
<
attributes
->
length
();
i
++
)
{
std
::
string
attribute_string
=
attributes
->
item
(
i
)
->
innerText
();
AttributeWithParameter
attr_with_param
;
std
::
string
&
attribute_name
=
attr_with_param
.
first
;
unsigned
&
attribute_param
=
attr_with_param
.
second
;
attribute_param
=
0
;
if
(
attribute_string
.
find
(
'('
)
!=
std
::
string
::
npos
)
attr_with_param
=
parseAttributeWithParameter
(
attribute_string
);
else
attribute_name
=
attribute_string
;
if
(
attributes
->
item
(
i
)
->
parentNode
()
->
nodeName
()
==
"keys"
)
{
QueryParseResult
::
KeyAttribute
key_attribute
;
key_attribute
.
attribute
=
attribute_name
;
key_attribute
.
parameter
=
attribute_param
;
result
.
key_attributes
.
push_back
(
key_attribute
);
}
if
(
attributes
->
item
(
i
)
->
parentNode
()
->
nodeName
()
==
"aggregate"
)
{
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
aggregate_nodes
=
attributes
->
item
(
i
)
->
parentNode
()
->
childNodes
();
unsigned
j
;
for
(
j
=
0
;
j
<
aggregate_nodes
->
length
();
j
++
)
{
if
(
aggregate_nodes
->
item
(
j
)
->
nodeName
()
==
"function"
)
{
QueryParseResult
::
Aggregate
aggregate
;
aggregate
.
attribute
=
attribute_name
;
aggregate
.
parameter
=
attribute_param
;
aggregate
.
function
=
aggregate_nodes
->
item
(
j
)
->
innerText
();
result
.
aggregates
.
push_back
(
aggregate
);
break
;
}
}
if
(
j
==
aggregate_nodes
->
length
())
throw
Exception
(
std
::
string
(
"Not found 'function' element for aggregate with attribute "
)
+
attribute_name
,
ErrorCodes
::
NOT_FOUND_FUNCTION_ELEMENT_FOR_AGGREGATE
);
}
if
(
attributes
->
item
(
i
)
->
parentNode
()
->
nodeName
()
==
"condition"
)
{
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
condition_nodes
=
attributes
->
item
(
i
)
->
parentNode
()
->
childNodes
();
QueryParseResult
::
WhereCondition
condition
;
condition
.
attribute
=
attribute_name
;
condition
.
parameter
=
attribute_param
;
unsigned
j
;
for
(
j
=
0
;
j
<
condition_nodes
->
length
();
j
++
)
{
if
(
condition_nodes
->
item
(
j
)
->
nodeName
()
==
"relation"
)
{
condition
.
relation
=
condition_nodes
->
item
(
j
)
->
innerText
();
break
;
}
}
if
(
j
==
condition_nodes
->
length
())
throw
Exception
(
std
::
string
(
"Not found 'relation' element for condition with attribute "
)
+
attribute_name
,
ErrorCodes
::
NOT_FOUND_RELATION_ELEMENT_FOR_CONDITION
);
for
(
j
=
0
;
j
<
condition_nodes
->
length
();
j
++
)
{
if
(
condition_nodes
->
item
(
j
)
->
nodeName
()
==
"rhs"
)
{
condition
.
rhs
=
condition_nodes
->
item
(
j
)
->
innerText
();
break
;
}
}
if
(
j
==
condition_nodes
->
length
())
throw
Exception
(
std
::
string
(
"Not found 'rhs' element for condition with attribute "
)
+
attribute_name
,
ErrorCodes
::
NOT_FOUND_RHS_ELEMENT_FOR_CONDITION
);
result
.
where_conditions
.
push_back
(
condition
);
}
}
if
(
result
.
key_attributes
.
size
()
==
0
)
throw
Exception
(
"No attributes listed."
,
ErrorCodes
::
NO_ATTRIBUTES_LISTED
);
/// получаем условие сортировки
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
sort_nodes
=
result
.
query
->
getElementsByTagName
(
"sort"
);
if
(
sort_nodes
->
length
()
>=
1
)
{
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
column_nodes
=
sort_nodes
->
item
(
0
)
->
childNodes
();
for
(
unsigned
i
=
0
;
i
<
column_nodes
->
length
();
i
++
)
{
if
(
column_nodes
->
item
(
i
)
->
nodeName
()
!=
"column"
)
continue
;
QueryParseResult
::
SortColumn
column
;
column
.
direction
=
"ascending"
;
Poco
::
AutoPtr
<
Poco
::
XML
::
NodeList
>
index_direction_nodes
=
column_nodes
->
item
(
i
)
->
childNodes
();
for
(
unsigned
j
=
0
;
j
<
index_direction_nodes
->
length
();
j
++
)
{
if
(
index_direction_nodes
->
item
(
j
)
->
nodeName
()
==
"index"
)
{
column
.
index
=
DB
::
parse
<
unsigned
>
(
index_direction_nodes
->
item
(
j
)
->
innerText
());
if
(
column
.
index
<
1
||
column
.
index
>
result
.
key_attributes
.
size
()
+
result
.
aggregates
.
size
())
throw
Exception
(
"Index of column in sort clause is out of range."
,
ErrorCodes
::
INDEX_OF_COLUMN_IN_SORT_CLAUSE_IS_OUT_OF_RANGE
);
}
if
(
index_direction_nodes
->
item
(
j
)
->
nodeName
()
==
"direction"
)
{
column
.
direction
=
index_direction_nodes
->
item
(
j
)
->
innerText
();
if
(
column
.
direction
!=
"ascending"
&&
column
.
direction
!=
"descending"
)
throw
Exception
(
"Unknown direction of sorting."
,
ErrorCodes
::
UNKNOWN_DIRECTION_OF_SORTING
);
}
}
result
.
sort_columns
.
push_back
(
column
);
}
}
return
result
;
}
}
}
dbms/src/Server/OLAPQueryParser.h
已删除
100644 → 0
浏览文件 @
c6da35b7
#pragma once
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/DOMWriter.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/Text.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/SAX/InputSource.h>
#include <Poco/RegularExpression.h>
#include <Poco/AutoPtr.h>
#include <common/logger_useful.h>
#include <common/Common.h>
namespace
DB
{
namespace
OLAP
{
/// формат выдачи результата
enum
Format
{
FORMAT_XML
,
FORMAT_TAB
,
FORMAT_BIN
};
/// что делать, если размер результата больше max_result_size
enum
OverflowMode
{
OVERFLOW_MODE_THROW
,
/// прекратить выполнение запроса, вернуть ошибку (по-умолчанию)
OVERFLOW_MODE_BREAK
,
/// вернуть то, что успело посчитаться до переполнения
OVERFLOW_MODE_ANY
,
/** для тех ключей, которые на момент переполнения, попали в результат,
* посчитать до конца, остальные ключи игнорировать
* (то есть, выбрать "первые попавшиеся" max_result_size записи)
*/
};
/// результат парсинга XML-запроса в формате OLAP-server
struct
QueryParseResult
{
struct
KeyAttribute
{
std
::
string
attribute
;
unsigned
parameter
;
};
struct
Aggregate
{
std
::
string
attribute
;
std
::
string
function
;
unsigned
parameter
;
};
struct
WhereCondition
{
std
::
string
attribute
;
unsigned
parameter
;
std
::
string
relation
;
std
::
string
rhs
;
};
struct
SortColumn
{
unsigned
index
;
std
::
string
direction
;
};
/// 0, если не ограничено
unsigned
max_result_size
;
unsigned
max_execution_time
;
unsigned
max_threads_per_counter
;
unsigned
concurrency
;
unsigned
limit
;
/// максимальное количество записей для вывода (все остальные - игнорируются)
bool
cut_date_last
;
bool
cut_dates_for_goals
;
/// если за какую-то дату цели не существовало - то всего лишь пропускать эту дату
/// Использовать таблицу для одного слоя, даже если указан CounterID = 0.
bool
local
;
/// сэмплинг - по какой доле данных выполнять запрос. принимает значения в диапазоне (0, 1]
/// если равно 1 - то отключен
float
sample
;
Format
format
;
bool
has_overflow_mode
=
false
;
OverflowMode
overflow_mode
;
Poco
::
AutoPtr
<
Poco
::
XML
::
Document
>
query
;
CounterID_t
CounterID
;
time_t
date_first
;
time_t
date_last
;
unsigned
days
;
std
::
vector
<
KeyAttribute
>
key_attributes
;
std
::
vector
<
Aggregate
>
aggregates
;
std
::
vector
<
WhereCondition
>
where_conditions
;
std
::
vector
<
SortColumn
>
sort_columns
;
/// Какую иерархию регионов использовать.
std
::
string
regions_point_of_view
;
};
/// Парсер XML-запросов в формате OLAP-server.
class
QueryParser
{
private:
using
AttributeWithParameter
=
std
::
pair
<
std
::
string
,
unsigned
>
;
AttributeWithParameter
parseAttributeWithParameter
(
const
std
::
string
&
s
);
time_t
getLastDate
();
/// regexp для парсинга выражения типа "GoalReaches(111)"
Poco
::
RegularExpression
parse_attribute_with_parameter_regexp
;
Logger
*
log
;
public:
QueryParser
()
:
parse_attribute_with_parameter_regexp
(
"^
\\
s*(
\\
w+)
\\
s*
\\
((
\\
d+)
\\
)
\\
s*$"
),
log
(
&
Logger
::
get
(
"QueryParser"
))
{
}
QueryParseResult
parse
(
std
::
istream
&
s
);
};
}
}
dbms/src/Server/Server.cpp
浏览文件 @
ea734b6b
...
...
@@ -48,7 +48,6 @@
#include "HTTPHandler.h"
#include "ReplicasStatusHandler.h"
#include "InterserverIOHTTPHandler.h"
#include "OLAPHTTPHandler.h"
#include "TCPHandler.h"
#include "MetricsTransmitter.h"
#include "UsersConfigReloader.h"
...
...
@@ -424,30 +423,10 @@ int Server::main(const std::vector<std::string> & args)
http_params
);
}
/// OLAP HTTP
std
::
experimental
::
optional
<
Poco
::
Net
::
HTTPServer
>
olap_http_server
;
bool
use_olap_server
=
config
().
has
(
"olap_compatibility.port"
);
if
(
use_olap_server
)
{
olap_parser
=
std
::
make_unique
<
OLAP
::
QueryParser
>
();
olap_converter
=
std
::
make_unique
<
OLAP
::
QueryConverter
>
(
config
());
Poco
::
Net
::
ServerSocket
olap_http_socket
(
Poco
::
Net
::
SocketAddress
(
listen_host
,
config
().
getInt
(
"olap_compatibility.port"
)));
olap_http_socket
.
setReceiveTimeout
(
settings
.
receive_timeout
);
olap_http_socket
.
setSendTimeout
(
settings
.
send_timeout
);
olap_http_server
.
emplace
(
new
HTTPRequestHandlerFactory
<
OLAPHTTPHandler
>
(
*
this
,
"OLAPHTTPHandler-factory"
),
server_pool
,
olap_http_socket
,
http_params
);
}
http_server
.
start
();
tcp_server
.
start
();
if
(
interserver_io_http_server
)
interserver_io_http_server
->
start
();
if
(
olap_http_server
)
olap_http_server
->
start
();
LOG_INFO
(
log
,
"Ready for connections."
);
...
...
@@ -471,8 +450,6 @@ int Server::main(const std::vector<std::string> & args)
http_server
.
stop
();
tcp_server
.
stop
();
if
(
use_olap_server
)
olap_http_server
->
stop
();
);
/// try to load dictionaries immediately, throw on error and die
...
...
dbms/src/Server/Server.h
浏览文件 @
ea734b6b
...
...
@@ -22,17 +22,15 @@
#include <DB/Common/HTMLForm.h>
#include <DB/Interpreters/Context.h>
#include "OLAPQueryParser.h"
#include "OLAPQueryConverter.h"
/**
Сервер предоставляет три интерфейса
:
* 1. HTTP -
простой интерфейс для доступа из любых приложений
.
* 2. TCP -
интерфейс для доступа из родной библиотеки, родного клиента, и для межсерверного взаимодействия
.
*
Более эффективен, так как
* -
данные передаются по столбцам
;
* -
данные передаются со сжатием
;
*
Позволяет тонко управлять настройками и получать более подробную информацию в ответах
.
* 3.
OLAP-server HTTP - интерфейс для совместимости с устаревшим демоном OLAP-server
.
/**
Server provides three interfaces
:
* 1. HTTP -
simple interface for any applications
.
* 2. TCP -
interface for native clickhouse-client and for server to server internal communications
.
*
More rich and efficient, but less compatible
* -
data is transferred by columns
;
* -
data is transferred compressed
;
*
Allows to get more information in response
.
* 3.
Interserver HTTP - for replication
.
*/
...
...
@@ -42,12 +40,9 @@ namespace DB
class
Server
:
public
BaseDaemon
{
public:
///
Глобальные настройки севрера
///
Global settings of server.
std
::
unique_ptr
<
Context
>
global_context
;
std
::
unique_ptr
<
OLAP
::
QueryParser
>
olap_parser
;
std
::
unique_ptr
<
OLAP
::
QueryConverter
>
olap_converter
;
protected:
void
initialize
(
Application
&
self
)
{
...
...
dbms/src/Server/TCPHandler.cpp
浏览文件 @
ea734b6b
...
...
@@ -448,10 +448,7 @@ void TCPHandler::receiveHello()
{
writeString
(
"HTTP/1.0 400 Bad Request
\r\n\r\n
"
"Port "
+
server
.
config
().
getString
(
"tcp_port"
)
+
" is for clickhouse-client program.
\r\n
"
"You must use port "
+
server
.
config
().
getString
(
"http_port"
)
+
" for HTTP"
+
(
server
.
config
().
getBool
(
"use_olap_http_server"
,
false
)
?
"
\r\n
or port "
+
server
.
config
().
getString
(
"olap_http_port"
)
+
" for OLAPServer compatibility layer.
\r\n
"
:
".
\r\n
"
),
"You must use port "
+
server
.
config
().
getString
(
"http_port"
)
+
" for HTTP.
\r\n
"
,
*
out
);
throw
Exception
(
"Client has connected to wrong port"
,
ErrorCodes
::
CLIENT_HAS_CONNECTED_TO_WRONG_PORT
);
...
...
libs/libcommon/include/common/DateLUTImpl.h
浏览文件 @
ea734b6b
...
...
@@ -21,8 +21,8 @@
STRONG_TYPEDEF
(
UInt16
,
DayNum_t
);
/** Lookup
таблица для преобразования времени в дату, а также в месяц или в год или в день недели или в день месяца
.
*
Сейчас она используется для ускорения OLAPServer-а, который делает такие преобразования миллиардами
.
/** Lookup
table to conversion of time to date, and to month / year / day of week / day of month and so on
.
*
First time was implemented for OLAPServer, that needed to do billions of such transformations
.
*/
class
DateLUTImpl
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录