Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
2dot5
ClickHouse
提交
f850408a
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,发现更多精彩内容 >>
提交
f850408a
编写于
10月 15, 2019
作者:
A
alesapin
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Convert AST to Abstract configuration
上级
1ce0eb6e
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
622 addition
and
0 deletion
+622
-0
dbms/src/Common/ErrorCodes.cpp
dbms/src/Common/ErrorCodes.cpp
+1
-0
dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp
dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp
+423
-0
dbms/src/Dictionaries/tests/gtest_dictionary_configuration.cpp
...src/Dictionaries/tests/gtest_dictionary_configuration.cpp
+198
-0
未找到文件。
dbms/src/Common/ErrorCodes.cpp
浏览文件 @
f850408a
...
...
@@ -463,6 +463,7 @@ namespace ErrorCodes
extern
const
int
DICTIONARY_ALREADY_EXISTS
=
486
;
extern
const
int
UNKNOWN_DICTIONARY
=
487
;
extern
const
int
EMPTY_LIST_OF_ATTRIBUTES_PASSED
=
488
;
extern
const
int
INCORRECT_DICTIONARY_DEFINITION
=
489
;
extern
const
int
KEEPER_EXCEPTION
=
999
;
extern
const
int
POCO_EXCEPTION
=
1000
;
...
...
dbms/src/Dictionaries/getDictionaryConfigurationFromAST.cpp
0 → 100644
浏览文件 @
f850408a
#include <Dictionaries/getDictionaryConfigurationFromAST.h>
#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/Text.h>
#include <Poco/Util/AbstractConfiguration.h>
#include <Poco/Util/XMLConfiguration.h>
#include <IO/WriteHelpers.h>
#include <Parsers/queryToString.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTFunction.h>
#include <Core/Names.h>
#include <Parsers/ASTFunctionWithKeyValueArguments.h>
#include <Parsers/ASTDictionaryAttributeDeclaration.h>
namespace
DB
{
namespace
ErrorCodes
{
extern
const
int
INCORRECT_DICTIONARY_DEFINITION
;
}
/// There are a lot of code, but it's very simple and straightforward
/// We just convert
namespace
{
String
unescapeString
(
const
String
&
string
)
{
if
(
!
string
.
empty
()
&&
string
.
front
()
==
'\''
&&
string
.
back
()
==
'\''
)
return
string
.
substr
(
1
,
string
.
size
()
-
2
);
return
string
;
}
using
namespace
Poco
;
using
namespace
Poco
::
XML
;
/*
* Transforms next definition
* LIFETIME(MIN 10, MAX 100)
* to the next configuration
* <lifetime>
* <min>10</min>
* <max>100</max>
* </lifetime>
*/
void
buildLifetimeConfiguration
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTDictionaryLifetime
*
lifetime
)
{
AutoPtr
<
Element
>
lifetime_element
(
doc
->
createElement
(
"lifetime"
));
AutoPtr
<
Element
>
min_element
(
doc
->
createElement
(
"min"
));
AutoPtr
<
Element
>
max_element
(
doc
->
createElement
(
"max"
));
AutoPtr
<
Text
>
min_sec
(
doc
->
createTextNode
(
toString
(
lifetime
->
min_sec
)));
min_element
->
appendChild
(
min_sec
);
AutoPtr
<
Text
>
max_sec
(
doc
->
createTextNode
(
toString
(
lifetime
->
max_sec
)));
max_element
->
appendChild
(
max_sec
);
lifetime_element
->
appendChild
(
min_element
);
lifetime_element
->
appendChild
(
max_element
);
root
->
appendChild
(
lifetime_element
);
}
/*
* Transforms next definition
* LAYOUT(FLAT())
* to the next configuration
* <layout>
* <flat/>
* </layout>
*
* And next definition
* LAYOUT(CACHE(SIZE_IN_CELLS 1000))
* to the next one
* <layout>
* <cache>
* <size_in_cells>1000</size_in_cells>
* </cache>
* </layout>
*/
void
buildLayoutConfiguration
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTDictionaryLayout
*
layout
)
{
AutoPtr
<
Element
>
layout_element
(
doc
->
createElement
(
"layout"
));
root
->
appendChild
(
layout_element
);
AutoPtr
<
Element
>
layout_type_element
(
doc
->
createElement
(
layout
->
layout_type
));
layout_element
->
appendChild
(
layout_type_element
);
if
(
layout
->
parameter
.
has_value
())
{
const
auto
&
param
=
layout
->
parameter
;
AutoPtr
<
Element
>
layout_type_parameter_element
(
doc
->
createElement
(
param
->
first
));
const
ASTLiteral
&
literal
=
param
->
second
->
as
<
const
ASTLiteral
&>
();
AutoPtr
<
Text
>
value
(
doc
->
createTextNode
(
toString
(
literal
.
value
.
get
<
UInt64
>
())));
layout_type_parameter_element
->
appendChild
(
value
);
layout_type_element
->
appendChild
(
layout_type_parameter_element
);
}
}
/*
* Transforms next definition
* RANGE(MIN StartDate, MAX EndDate)
* to the next configuration
* <range_min><name>StartDate</name></range_min>
* <range_max><name>EndDate</name></range_max>
*/
void
buildRangeConfiguration
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTDictionaryRange
*
range
)
{
// appends <key><name>value</name></key> to root
auto
appendElem
=
[
&
doc
,
&
root
](
const
std
::
string
&
key
,
const
std
::
string
&
value
)
{
AutoPtr
<
Element
>
element
(
doc
->
createElement
(
key
));
AutoPtr
<
Element
>
name
(
doc
->
createElement
(
"name"
));
AutoPtr
<
Text
>
text
(
doc
->
createTextNode
(
value
));
name
->
appendChild
(
text
);
element
->
appendChild
(
name
);
root
->
appendChild
(
element
);
};
appendElem
(
"range_min"
,
range
->
min_attr_name
);
appendElem
(
"range_max"
,
range
->
max_attr_name
);
}
/// Get primary key columns names from AST
Names
getPrimaryKeyColumns
(
const
ASTExpressionList
*
primary_key
)
{
Names
result
;
const
auto
&
children
=
primary_key
->
children
;
for
(
size_t
index
=
0
;
index
!=
children
.
size
();
++
index
)
{
const
ASTIdentifier
*
key_part
=
children
[
index
]
->
as
<
const
ASTIdentifier
>
();
result
.
push_back
(
key_part
->
name
);
}
return
result
;
}
/**
* Transofrms single dictionary attribute to configuration
* third_column UInt8 DEFAULT 2 EXPRESSION rand() % 100 * 77
* to
* <attribute>
* <name>third_column</name>
* <type>UInt8</type>
* <null_value>2</null_value>
* <expression>(rand() % 100) * 77</expression>
* </attribute>
*/
void
buildSingleAttribute
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTDictionaryAttributeDeclaration
*
dict_attr
)
{
AutoPtr
<
Element
>
attribute_element
(
doc
->
createElement
(
"attribute"
));
root
->
appendChild
(
attribute_element
);
AutoPtr
<
Element
>
name_element
(
doc
->
createElement
(
"name"
));
AutoPtr
<
Text
>
name
(
doc
->
createTextNode
(
dict_attr
->
name
));
name_element
->
appendChild
(
name
);
attribute_element
->
appendChild
(
name_element
);
AutoPtr
<
Element
>
type_element
(
doc
->
createElement
(
"type"
));
AutoPtr
<
Text
>
type
(
doc
->
createTextNode
(
queryToString
(
dict_attr
->
type
)));
type_element
->
appendChild
(
type
);
attribute_element
->
appendChild
(
type_element
);
AutoPtr
<
Element
>
null_value_element
(
doc
->
createElement
(
"null_value"
));
String
null_value_str
;
if
(
dict_attr
->
default_value
)
null_value_str
=
queryToString
(
dict_attr
->
default_value
);
AutoPtr
<
Text
>
null_value
(
doc
->
createTextNode
(
null_value_str
));
null_value_element
->
appendChild
(
null_value
);
attribute_element
->
appendChild
(
null_value_element
);
if
(
dict_attr
->
expression
!=
nullptr
)
{
AutoPtr
<
Element
>
expression_element
(
doc
->
createElement
(
"expression"
));
AutoPtr
<
Text
>
expression
(
doc
->
createTextNode
(
queryToString
(
dict_attr
->
expression
)));
expression_element
->
appendChild
(
expression
);
attribute_element
->
appendChild
(
expression_element
);
}
if
(
dict_attr
->
hierarchical
)
{
AutoPtr
<
Element
>
hierarchical_element
(
doc
->
createElement
(
"hierarchical"
));
AutoPtr
<
Text
>
hierarchical
(
doc
->
createTextNode
(
"true"
));
hierarchical_element
->
appendChild
(
hierarchical
);
attribute_element
->
appendChild
(
hierarchical_element
);
}
if
(
dict_attr
->
injective
)
{
AutoPtr
<
Element
>
injective_element
(
doc
->
createElement
(
"injective"
));
AutoPtr
<
Text
>
injective
(
doc
->
createTextNode
(
"true"
));
injective_element
->
appendChild
(
injective
);
attribute_element
->
appendChild
(
injective_element
);
}
if
(
dict_attr
->
is_object_id
)
{
AutoPtr
<
Element
>
is_object_id_element
(
doc
->
createElement
(
"is_object_id"
));
AutoPtr
<
Text
>
is_object_id
(
doc
->
createTextNode
(
"true"
));
is_object_id_element
->
appendChild
(
is_object_id
);
attribute_element
->
appendChild
(
is_object_id_element
);
}
}
/**
* Transforms
* PRIMARY KEY Attr1 ,..., AttrN
* to the next configuration
* <id><name>Attr1</name></id>
* or
* <key>
* <attribute>
* <name>Attr1</name>
* <type>UInt8</type>
* </attribute>
* ...
* <attribute> fe
* </key>
*
*/
void
buildPrimaryKeyConfiguration
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
bool
complex
,
const
Names
&
key_names
,
const
ASTExpressionList
*
dictionary_attributes
)
{
if
(
!
complex
)
{
if
(
key_names
.
size
()
!=
1
)
throw
Exception
(
"Primary key for simple dictionary must contain exactly one element"
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
AutoPtr
<
Element
>
id_element
(
doc
->
createElement
(
"id"
));
root
->
appendChild
(
id_element
);
AutoPtr
<
Element
>
name_element
(
doc
->
createElement
(
"name"
));
id_element
->
appendChild
(
name_element
);
AutoPtr
<
Text
>
name
(
doc
->
createTextNode
(
*
key_names
.
begin
()));
name_element
->
appendChild
(
name
);
}
else
{
const
auto
&
children
=
dictionary_attributes
->
children
;
if
(
children
.
size
()
<
key_names
.
size
())
throw
Exception
(
"Primary key fields count is more, than dictionary attributes count."
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
AutoPtr
<
Element
>
key_element
(
doc
->
createElement
(
"key"
));
root
->
appendChild
(
key_element
);
for
(
const
auto
&
key_name
:
key_names
)
{
bool
found
=
false
;
for
(
const
auto
&
attr
:
children
)
{
const
ASTDictionaryAttributeDeclaration
*
dict_attr
=
attr
->
as
<
const
ASTDictionaryAttributeDeclaration
>
();
if
(
dict_attr
->
name
==
key_name
)
{
found
=
true
;
buildSingleAttribute
(
doc
,
key_element
,
dict_attr
);
break
;
}
}
}
}
}
/**
* Transforms list of ASTDictionaryAttributeDeclarations to list of dictionary attributes
*/
void
buildDictionaryAttributesConfiguration
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTExpressionList
*
dictionary_attributes
,
const
Names
&
key_columns
)
{
const
auto
&
children
=
dictionary_attributes
->
children
;
for
(
size_t
i
=
0
;
i
<
children
.
size
();
++
i
)
{
const
ASTDictionaryAttributeDeclaration
*
dict_attr
=
children
[
i
]
->
as
<
const
ASTDictionaryAttributeDeclaration
>
();
if
(
!
dict_attr
->
type
)
throw
Exception
(
"Dictionary attribute must has type"
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
if
(
std
::
find
(
key_columns
.
begin
(),
key_columns
.
end
(),
dict_attr
->
name
)
==
key_columns
.
end
())
buildSingleAttribute
(
doc
,
root
,
dict_attr
);
}
}
/** Transform function with key-value arguments to configuration
* (used for source transformation)
*/
void
buildConfigurationFromFunctionWithKeyValueArguments
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTExpressionList
*
ast_expr_list
)
{
const
auto
&
children
=
ast_expr_list
->
children
;
for
(
size_t
i
=
0
;
i
!=
children
.
size
();
++
i
)
{
const
ASTPair
*
pair
=
children
[
i
]
->
as
<
const
ASTPair
>
();
AutoPtr
<
Element
>
current_xml_element
(
doc
->
createElement
(
pair
->
first
));
root
->
appendChild
(
current_xml_element
);
if
(
auto
identifier
=
pair
->
second
->
as
<
const
ASTIdentifier
>
();
identifier
)
{
AutoPtr
<
Text
>
value
(
doc
->
createTextNode
(
identifier
->
name
));
current_xml_element
->
appendChild
(
value
);
}
else
if
(
auto
literal
=
pair
->
second
->
as
<
const
ASTLiteral
>
();
literal
)
{
String
str_literal
=
applyVisitor
(
FieldVisitorToString
(),
literal
->
value
);
AutoPtr
<
Text
>
value
(
doc
->
createTextNode
(
unescapeString
(
str_literal
)));
current_xml_element
->
appendChild
(
value
);
}
else
if
(
auto
list
=
pair
->
second
->
as
<
const
ASTExpressionList
>
();
list
)
{
buildConfigurationFromFunctionWithKeyValueArguments
(
doc
,
current_xml_element
,
list
);
}
else
{
throw
Exception
(
"Incorrect ASTPair contains wrong value, should be literal, identifier or list"
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
}
}
}
/** Build source definition from ast.
* SOURCE(MYSQL(HOST 'localhost' PORT 9000 USER 'default' REPLICA(HOST '127.0.0.1' PRIORITY 1) PASSWORD ''))
* to
* <source>
* <mysql>
* <host>localhost</host>
* ...
* <replica>
* <host>127.0.0.1</host>
* ...
* </replica>
* </mysql>
* </source>
*/
void
buildSourceConfiguration
(
AutoPtr
<
Document
>
doc
,
AutoPtr
<
Element
>
root
,
const
ASTFunctionWithKeyValueArguments
*
source
)
{
AutoPtr
<
Element
>
outer_element
(
doc
->
createElement
(
"source"
));
root
->
appendChild
(
outer_element
);
AutoPtr
<
Element
>
source_element
(
doc
->
createElement
(
source
->
name
));
outer_element
->
appendChild
(
source_element
);
buildConfigurationFromFunctionWithKeyValueArguments
(
doc
,
source_element
,
source
->
elements
->
as
<
const
ASTExpressionList
>
());
}
void
checkAST
(
const
ASTCreateQuery
&
query
)
{
if
(
!
query
.
is_dictionary
||
query
.
dictionary
==
nullptr
)
throw
Exception
(
"Cannot convert dictionary to configuration from non-dictionary AST."
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
if
(
query
.
dictionary_attributes_list
==
nullptr
||
query
.
dictionary_attributes_list
->
children
.
empty
())
throw
Exception
(
"Dictionary AST missing attributes list."
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
if
(
query
.
dictionary
->
layout
==
nullptr
)
throw
Exception
(
"Cannot create dictionary with empty layout."
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
if
(
query
.
dictionary
->
lifetime
==
nullptr
)
throw
Exception
(
"Dictionary AST missing lifetime section"
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
if
(
query
.
dictionary
->
primary_key
==
nullptr
)
throw
Exception
(
"Dictionary AST missing primary key"
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
if
(
query
.
dictionary
->
source
==
nullptr
)
throw
Exception
(
"Dictionary AST missing source"
,
ErrorCodes
::
INCORRECT_DICTIONARY_DEFINITION
);
/// Range can be empty
}
}
DictionaryConfigurationPtr
getDictionaryConfigurationFromAST
(
const
ASTCreateQuery
&
query
)
{
checkAST
(
query
);
AutoPtr
<
Poco
::
XML
::
Document
>
xml_document
(
new
Poco
::
XML
::
Document
());
AutoPtr
<
Poco
::
XML
::
Element
>
document_root
(
xml_document
->
createElement
(
"dictionaries"
));
xml_document
->
appendChild
(
document_root
);
AutoPtr
<
Poco
::
XML
::
Element
>
current_dictionary
(
xml_document
->
createElement
(
"dictionary"
));
document_root
->
appendChild
(
current_dictionary
);
AutoPtr
<
Poco
::
Util
::
XMLConfiguration
>
conf
(
new
Poco
::
Util
::
XMLConfiguration
());
AutoPtr
<
Poco
::
XML
::
Element
>
name_element
(
xml_document
->
createElement
(
"name"
));
current_dictionary
->
appendChild
(
name_element
);
AutoPtr
<
Text
>
name
(
xml_document
->
createTextNode
(
query
.
database
+
"."
+
query
.
table
));
name_element
->
appendChild
(
name
);
AutoPtr
<
Element
>
structure_element
(
xml_document
->
createElement
(
"structure"
));
current_dictionary
->
appendChild
(
structure_element
);
Names
pk_columns
=
getPrimaryKeyColumns
(
query
.
dictionary
->
primary_key
);
auto
dictionary_layout
=
query
.
dictionary
->
layout
;
bool
complex
=
startsWith
(
dictionary_layout
->
layout_type
,
"complex"
);
buildDictionaryAttributesConfiguration
(
xml_document
,
structure_element
,
query
.
dictionary_attributes_list
,
pk_columns
);
buildPrimaryKeyConfiguration
(
xml_document
,
structure_element
,
complex
,
pk_columns
,
query
.
dictionary_attributes_list
);
buildLayoutConfiguration
(
xml_document
,
current_dictionary
,
dictionary_layout
);
buildSourceConfiguration
(
xml_document
,
current_dictionary
,
query
.
dictionary
->
source
);
buildLifetimeConfiguration
(
xml_document
,
current_dictionary
,
query
.
dictionary
->
lifetime
);
if
(
query
.
dictionary
->
range
)
buildRangeConfiguration
(
xml_document
,
current_dictionary
,
query
.
dictionary
->
range
);
conf
->
load
(
xml_document
);
return
conf
;
}
}
dbms/src/Dictionaries/tests/gtest_dictionary_configuration.cpp
0 → 100644
浏览文件 @
f850408a
#include <iostream>
#include <sstream>
#include <Core/Types.h>
#include <Poco/Util/XMLConfiguration.h>
#include <Parsers/ASTCreateQuery.h>
#include <Parsers/ASTDropQuery.h>
#include <Parsers/DumpASTNode.h>
#include <Parsers/ParserCreateQuery.h>
#include <Parsers/ParserDictionary.h>
#include <Parsers/ParserDropQuery.h>
#include <Parsers/ParserTablePropertiesQuery.h>
#include <Parsers/TablePropertiesQueriesASTs.h>
#include <Parsers/formatAST.h>
#include <Parsers/parseQuery.h>
#include <Dictionaries/getDictionaryConfigurationFromAST.h>
#include <gtest/gtest.h>
using
namespace
DB
;
/// For debug
std
::
string
configurationToString
(
const
DictionaryConfigurationPtr
&
config
)
{
const
Poco
::
Util
::
XMLConfiguration
*
xml_config
=
dynamic_cast
<
const
Poco
::
Util
::
XMLConfiguration
*>
(
config
.
get
());
std
::
ostringstream
oss
;
xml_config
->
save
(
oss
);
return
oss
.
str
();
}
TEST
(
ConvertDictionaryAST
,
SimpleDictConfiguration
)
{
String
input
=
" CREATE DICTIONARY test.dict1"
" ("
" key_column UInt64 DEFAULT 0,"
" second_column UInt8 DEFAULT 1,"
" third_column UInt8 DEFAULT 2"
" )"
" PRIMARY KEY key_column"
" SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 USER 'default' PASSWORD '' DB 'test' TABLE 'table_for_dict'))"
" LAYOUT(FLAT())"
" LIFETIME(MIN 1 MAX 10)"
" RANGE(MIN second_column MAX third_column)"
;
ParserCreateDictionaryQuery
parser
;
ASTPtr
ast
=
parseQuery
(
parser
,
input
.
data
(),
input
.
data
()
+
input
.
size
(),
""
,
0
);
ASTCreateQuery
*
create
=
ast
->
as
<
ASTCreateQuery
>
();
DictionaryConfigurationPtr
config
=
getDictionaryConfigurationFromAST
(
*
create
);
/// name
EXPECT_EQ
(
config
->
getString
(
"dictionary.name"
),
"test.dict1"
);
/// lifetime
EXPECT_EQ
(
config
->
getInt
(
"dictionary.lifetime.min"
),
1
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.lifetime.max"
),
10
);
/// range
EXPECT_EQ
(
config
->
getString
(
"dictionary.range_min"
),
"second_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.range_max"
),
"third_column"
);
/// source
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.clickhouse.host"
),
"localhost"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.source.clickhouse.port"
),
9000
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.clickhouse.user"
),
"default"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.clickhouse.password"
),
""
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.clickhouse.db"
),
"test"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.clickhouse.table"
),
"table_for_dict"
);
/// attributes and key
Poco
::
Util
::
AbstractConfiguration
::
Keys
keys
;
config
->
keys
(
"dictionary.structure"
,
keys
);
EXPECT_EQ
(
keys
.
size
(),
3
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".name"
),
"second_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".type"
),
"UInt8"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.structure."
+
keys
[
0
]
+
".null_value"
),
1
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
1
]
+
".name"
),
"third_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
1
]
+
".type"
),
"UInt8"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.structure."
+
keys
[
1
]
+
".null_value"
),
2
);
EXPECT_EQ
(
keys
[
2
],
"id"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
2
]
+
".name"
),
"key_column"
);
/// layout
EXPECT_TRUE
(
config
->
has
(
"dictionary.layout.flat"
));
}
TEST
(
ConvertDictionaryAST
,
TrickyAttributes
)
{
String
input
=
" CREATE DICTIONARY dict2"
" ("
" key_column UInt64 IS_OBJECT_ID,"
" second_column UInt8 HIERARCHICAL INJECTIVE,"
" third_column UInt8 DEFAULT 2 EXPRESSION rand() % 100 * 77"
" )"
" PRIMARY KEY key_column"
" LAYOUT(hashed())"
" LIFETIME(MIN 1 MAX 10)"
" SOURCE(CLICKHOUSE(HOST 'localhost'))"
;
ParserCreateDictionaryQuery
parser
;
ASTPtr
ast
=
parseQuery
(
parser
,
input
.
data
(),
input
.
data
()
+
input
.
size
(),
""
,
0
);
ASTCreateQuery
*
create
=
ast
->
as
<
ASTCreateQuery
>
();
DictionaryConfigurationPtr
config
=
getDictionaryConfigurationFromAST
(
*
create
);
Poco
::
Util
::
AbstractConfiguration
::
Keys
keys
;
config
->
keys
(
"dictionary.structure"
,
keys
);
EXPECT_EQ
(
keys
.
size
(),
3
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".name"
),
"second_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".type"
),
"UInt8"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".null_value"
),
""
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".hierarchical"
),
"true"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
0
]
+
".injective"
),
"true"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
1
]
+
".name"
),
"third_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
1
]
+
".type"
),
"UInt8"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.structure."
+
keys
[
1
]
+
".null_value"
),
2
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
1
]
+
".expression"
),
"(rand() % 100) * 77"
);
EXPECT_EQ
(
keys
[
2
],
"id"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
keys
[
2
]
+
".name"
),
"key_column"
);
}
TEST
(
ConvertDictionaryAST
,
ComplexKeyAndLayoutWithParams
)
{
String
input
=
" CREATE DICTIONARY dict4"
" ("
" key_column1 String,"
" key_column2 UInt64,"
" third_column UInt8,"
" fourth_column UInt8"
" )"
" PRIMARY KEY key_column1, key_column2"
" SOURCE(MYSQL())"
" LAYOUT(COMPLEX_KEY_CACHE(size_in_cells 50))"
" LIFETIME(MIN 1 MAX 10)"
;
ParserCreateDictionaryQuery
parser
;
ASTPtr
ast
=
parseQuery
(
parser
,
input
.
data
(),
input
.
data
()
+
input
.
size
(),
""
,
0
);
ASTCreateQuery
*
create
=
ast
->
as
<
ASTCreateQuery
>
();
DictionaryConfigurationPtr
config
=
getDictionaryConfigurationFromAST
(
*
create
);
Poco
::
Util
::
AbstractConfiguration
::
Keys
keys
;
config
->
keys
(
"dictionary.structure.key"
,
keys
);
EXPECT_EQ
(
keys
.
size
(),
2
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure.key."
+
keys
[
0
]
+
".name"
),
"key_column1"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure.key."
+
keys
[
0
]
+
".type"
),
"String"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure.key."
+
keys
[
1
]
+
".name"
),
"key_column2"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure.key."
+
keys
[
1
]
+
".type"
),
"UInt64"
);
Poco
::
Util
::
AbstractConfiguration
::
Keys
attrs
;
config
->
keys
(
"dictionary.structure"
,
attrs
);
EXPECT_EQ
(
attrs
.
size
(),
3
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
attrs
[
0
]
+
".name"
),
"third_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
attrs
[
0
]
+
".type"
),
"UInt8"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
attrs
[
1
]
+
".name"
),
"fourth_column"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.structure."
+
attrs
[
1
]
+
".type"
),
"UInt8"
);
EXPECT_EQ
(
attrs
[
2
],
"key"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.layout.complex_key_cache.size_in_cells"
),
50
);
}
TEST
(
ConvertDictionaryAST
,
ComplexSource
)
{
String
input
=
" CREATE DICTIONARY dict4"
" ("
" key_column UInt64,"
" second_column UInt8,"
" third_column UInt8"
" )"
" PRIMARY KEY key_column"
" SOURCE(MYSQL(HOST 'localhost' PORT 9000 USER 'default' REPLICA(HOST '127.0.0.1' PRIORITY 1) PASSWORD ''))"
" LAYOUT(CACHE(size_in_cells 50))"
" LIFETIME(MIN 1 MAX 10)"
" RANGE(MIN second_column MAX third_column)"
;
ParserCreateDictionaryQuery
parser
;
ASTPtr
ast
=
parseQuery
(
parser
,
input
.
data
(),
input
.
data
()
+
input
.
size
(),
""
,
0
);
ASTCreateQuery
*
create
=
ast
->
as
<
ASTCreateQuery
>
();
DictionaryConfigurationPtr
config
=
getDictionaryConfigurationFromAST
(
*
create
);
/// source
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.mysql.host"
),
"localhost"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.source.mysql.port"
),
9000
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.mysql.user"
),
"default"
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.mysql.password"
),
""
);
EXPECT_EQ
(
config
->
getString
(
"dictionary.source.mysql.replica.host"
),
"127.0.0.1"
);
EXPECT_EQ
(
config
->
getInt
(
"dictionary.source.mysql.replica.priority"
),
1
);
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录