2012-07-16 03:42:36 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <DB/DataTypes/DataTypeString.h>
|
|
|
|
|
#include <DB/Columns/ColumnString.h>
|
|
|
|
|
#include <DB/Columns/ColumnConst.h>
|
|
|
|
|
#include <DB/Functions/FunctionsString.h>
|
2013-03-05 13:30:23 +00:00
|
|
|
|
#include <DB/Functions/FunctionsStringSearch.h>
|
|
|
|
|
#include <DB/Functions/FunctionsStringArray.h>
|
2012-07-16 03:42:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/** Функции работы с URL.
|
|
|
|
|
* Все функции работают не по RFC - то есть, максимально упрощены ради производительности.
|
|
|
|
|
*
|
|
|
|
|
* Функции, извлекающие часть URL-а.
|
|
|
|
|
* Если в URL-е нет ничего похожего, то возвращается пустая строка.
|
|
|
|
|
*
|
|
|
|
|
* domain
|
|
|
|
|
* domainWithoutWWW
|
|
|
|
|
* topLevelDomain
|
|
|
|
|
* protocol
|
|
|
|
|
* path
|
|
|
|
|
* queryString
|
|
|
|
|
* fragment
|
|
|
|
|
* queryStringAndFragment
|
|
|
|
|
*
|
|
|
|
|
* Функции, удаляющие часть из URL-а.
|
|
|
|
|
* Если в URL-е нет ничего похожего, то URL остаётся без изменений.
|
|
|
|
|
*
|
|
|
|
|
* cutWWW
|
|
|
|
|
* cutFragment
|
|
|
|
|
* cutQueryString
|
|
|
|
|
* cutQueryStringAndFragment
|
2012-07-21 03:45:48 +00:00
|
|
|
|
*
|
|
|
|
|
* Извлечь значение параметра в URL, если он есть. Вернуть пустую строку, если его нет.
|
|
|
|
|
* Если таких параметров много - вернуть значение первого. Значение не разэскейпливается.
|
|
|
|
|
*
|
2013-03-18 10:27:45 +00:00
|
|
|
|
* extractURLParameter(URL, name)
|
|
|
|
|
*
|
|
|
|
|
* Извлечь все параметры из URL в виде массива строк вида name=value.
|
|
|
|
|
* extractURLParameters(URL)
|
2013-08-02 13:55:43 +00:00
|
|
|
|
*
|
|
|
|
|
* Извлечь все имена параметров из URL в виде массива строк
|
2013-08-05 08:40:56 +00:00
|
|
|
|
* extractURLParameterNames(URL)
|
2013-03-18 10:27:45 +00:00
|
|
|
|
*
|
|
|
|
|
* Убрать указанный параметр из URL.
|
|
|
|
|
* cutURLParameter(URL, name)
|
|
|
|
|
*
|
|
|
|
|
* Получить массив иерархии URL. См. функцию nextURLInHierarchy в URLParser.
|
|
|
|
|
* URLHierarchy(URL)
|
2012-07-16 03:42:36 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef const char * Pos;
|
|
|
|
|
|
|
|
|
|
struct ExtractProtocol
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return strlen("https") + 1; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
|
|
|
|
|
while ((*pos >= 'a' && *pos <= 'z') || (*pos >= 'A' && *pos <= 'Z') || (*pos >= '0' && *pos <= '9'))
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
if (pos == data || pos + 3 >= data + size)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (pos[0] == ':')
|
|
|
|
|
res_size = pos - data;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <bool without_www>
|
|
|
|
|
struct ExtractDomain
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return 15; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
Pos tmp;
|
|
|
|
|
size_t protocol_length;
|
|
|
|
|
ExtractProtocol::execute(data, size, tmp, protocol_length);
|
|
|
|
|
pos += protocol_length + 3;
|
|
|
|
|
|
|
|
|
|
if (pos[-1] != '/' || pos[-2] != '/')
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (without_www && pos + 4 < end && !strncmp(pos, "www.", 4))
|
|
|
|
|
pos += 4;
|
|
|
|
|
|
|
|
|
|
Pos domain_begin = pos;
|
|
|
|
|
|
|
|
|
|
while (pos < end && *pos != '/' && *pos != ':' && *pos != '?' && *pos != '#')
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
if (pos == domain_begin)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
res_data = domain_begin;
|
|
|
|
|
res_size = pos - domain_begin;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ExtractTopLevelDomain
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return 5; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
Pos tmp;
|
|
|
|
|
size_t protocol_length;
|
|
|
|
|
ExtractProtocol::execute(data, size, tmp, protocol_length);
|
|
|
|
|
pos += protocol_length + 3;
|
|
|
|
|
|
|
|
|
|
if (pos[-1] != '/' || pos[-2] != '/')
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Pos domain_begin = pos;
|
|
|
|
|
|
|
|
|
|
while (pos < end && *pos != '/' && *pos != ':' && *pos != '?' && *pos != '#')
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
if (pos == domain_begin)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Pos last_dot = reinterpret_cast<Pos>(memrchr(domain_begin, '.', pos - domain_begin));
|
|
|
|
|
|
|
|
|
|
if (!last_dot)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/// Для IPv4-адресов не выделяем ничего.
|
|
|
|
|
if (last_dot[1] <= '9')
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
res_data = last_dot + 1;
|
|
|
|
|
res_size = pos - res_data;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct ExtractPath
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return 25; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
if (NULL != (pos = strchr(data, '/')) && pos[1] == '/' && NULL != (pos = strchr(pos + 2, '/')))
|
|
|
|
|
{
|
|
|
|
|
Pos query_string_or_fragment = strpbrk(pos, "?#");
|
|
|
|
|
|
|
|
|
|
res_data = pos;
|
|
|
|
|
res_size = (query_string_or_fragment ? query_string_or_fragment : end) - res_data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <bool without_leading_char>
|
|
|
|
|
struct ExtractQueryString
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return 10; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
if (NULL != (pos = strchr(data, '?')))
|
|
|
|
|
{
|
|
|
|
|
Pos fragment = strchr(pos, '#');
|
|
|
|
|
|
|
|
|
|
res_data = pos + (without_leading_char ? 1 : 0);
|
|
|
|
|
res_size = (fragment ? fragment : end) - res_data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <bool without_leading_char>
|
|
|
|
|
struct ExtractFragment
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return 10; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
if (NULL != (pos = strchr(data, '#')))
|
|
|
|
|
{
|
|
|
|
|
res_data = pos + (without_leading_char ? 1 : 0);
|
|
|
|
|
res_size = end - res_data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <bool without_leading_char>
|
|
|
|
|
struct ExtractQueryStringAndFragment
|
|
|
|
|
{
|
|
|
|
|
static size_t getReserveLengthForElement() { return 20; }
|
|
|
|
|
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
if (NULL != (pos = strchr(data, '?')))
|
|
|
|
|
{
|
|
|
|
|
res_data = pos + (without_leading_char ? 1 : 0);
|
|
|
|
|
res_size = end - res_data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// С точкой на конце.
|
|
|
|
|
struct ExtractWWW
|
|
|
|
|
{
|
|
|
|
|
static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size)
|
|
|
|
|
{
|
|
|
|
|
res_data = data;
|
|
|
|
|
res_size = 0;
|
|
|
|
|
|
|
|
|
|
Pos pos = data;
|
|
|
|
|
Pos end = pos + size;
|
|
|
|
|
|
|
|
|
|
Pos tmp;
|
|
|
|
|
size_t protocol_length;
|
|
|
|
|
ExtractProtocol::execute(data, size, tmp, protocol_length);
|
|
|
|
|
pos += protocol_length + 3;
|
|
|
|
|
|
|
|
|
|
if (pos[-1] != '/' || pos[-2] != '/')
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (pos + 4 < end && !strncmp(pos, "www.", 4))
|
|
|
|
|
{
|
|
|
|
|
res_data = pos;
|
|
|
|
|
res_size = 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2013-03-05 12:12:47 +00:00
|
|
|
|
struct ExtractURLParameterImpl
|
|
|
|
|
{
|
2013-09-15 05:51:43 +00:00
|
|
|
|
static void vector(const ColumnString::Chars_t & data,
|
2013-05-05 15:25:25 +00:00
|
|
|
|
const ColumnString::Offsets_t & offsets,
|
2013-03-05 12:12:47 +00:00
|
|
|
|
std::string pattern,
|
2013-09-15 05:51:43 +00:00
|
|
|
|
ColumnString::Chars_t & res_data, ColumnString::Offsets_t & res_offsets)
|
2013-03-05 12:12:47 +00:00
|
|
|
|
{
|
|
|
|
|
res_data.reserve(data.size() / 5);
|
|
|
|
|
res_offsets.resize(offsets.size());
|
|
|
|
|
|
|
|
|
|
pattern += '=';
|
|
|
|
|
const char * param_str = pattern.c_str();
|
|
|
|
|
size_t param_len = pattern.size();
|
|
|
|
|
|
|
|
|
|
size_t prev_offset = 0;
|
|
|
|
|
size_t res_offset = 0;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < offsets.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
size_t cur_offset = offsets[i];
|
|
|
|
|
|
|
|
|
|
const char * pos = NULL;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
const char * str = reinterpret_cast<const char *>(&data[prev_offset]);
|
|
|
|
|
|
|
|
|
|
const char * begin = strchr(str, '?');
|
|
|
|
|
if (begin == NULL)
|
|
|
|
|
break;
|
|
|
|
|
|
2013-03-05 13:20:01 +00:00
|
|
|
|
pos = strstr(begin + 1, param_str);
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
break;
|
|
|
|
|
if (pos != begin + 1 && *(pos - 1) != ';' && *(pos - 1) != '&')
|
2013-03-05 12:12:47 +00:00
|
|
|
|
{
|
2013-03-05 13:20:01 +00:00
|
|
|
|
pos = NULL;
|
2013-03-05 12:12:47 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-05 13:20:01 +00:00
|
|
|
|
pos += param_len;
|
2013-03-05 12:12:47 +00:00
|
|
|
|
} while (false);
|
|
|
|
|
|
|
|
|
|
if (pos != NULL)
|
|
|
|
|
{
|
2013-03-05 13:20:01 +00:00
|
|
|
|
const char * end = strpbrk(pos, "&;#");
|
2013-03-05 12:12:47 +00:00
|
|
|
|
if (end == NULL)
|
|
|
|
|
end = pos + strlen(pos);
|
|
|
|
|
|
|
|
|
|
res_data.resize(res_offset + (end - pos) + 1);
|
|
|
|
|
memcpy(&res_data[res_offset], pos, end - pos);
|
|
|
|
|
res_offset += end - pos;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
res_data.resize(res_offset + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res_data[res_offset] = 0;
|
|
|
|
|
++res_offset;
|
|
|
|
|
res_offsets[i] = res_offset;
|
|
|
|
|
|
|
|
|
|
prev_offset = cur_offset;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2013-03-18 10:27:45 +00:00
|
|
|
|
struct CutURLParameterImpl
|
|
|
|
|
{
|
2013-09-15 05:51:43 +00:00
|
|
|
|
static void vector(const ColumnString::Chars_t & data,
|
2013-05-05 15:25:25 +00:00
|
|
|
|
const ColumnString::Offsets_t & offsets,
|
2013-03-18 10:27:45 +00:00
|
|
|
|
std::string pattern,
|
2013-09-15 05:51:43 +00:00
|
|
|
|
ColumnString::Chars_t & res_data, ColumnString::Offsets_t & res_offsets)
|
2013-03-18 10:27:45 +00:00
|
|
|
|
{
|
|
|
|
|
res_data.reserve(data.size());
|
|
|
|
|
res_offsets.resize(offsets.size());
|
|
|
|
|
|
|
|
|
|
pattern += '=';
|
|
|
|
|
const char * param_str = pattern.c_str();
|
|
|
|
|
size_t param_len = pattern.size();
|
|
|
|
|
|
|
|
|
|
size_t prev_offset = 0;
|
|
|
|
|
size_t res_offset = 0;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < offsets.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
size_t cur_offset = offsets[i];
|
|
|
|
|
|
|
|
|
|
const char * url_begin = reinterpret_cast<const char *>(&data[prev_offset]);
|
|
|
|
|
const char * url_end = reinterpret_cast<const char *>(&data[cur_offset]) - 1;
|
|
|
|
|
const char * begin_pos = url_begin;
|
|
|
|
|
const char * end_pos = begin_pos;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
const char * begin = strchr(url_begin, '?');
|
|
|
|
|
if (begin == NULL)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
const char * pos = strstr(begin + 1, param_str);
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
break;
|
|
|
|
|
if (pos != begin + 1 && *(pos - 1) != ';' && *(pos - 1) != '&')
|
|
|
|
|
{
|
|
|
|
|
pos = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
begin_pos = pos;
|
|
|
|
|
end_pos = begin_pos + param_len;
|
|
|
|
|
|
|
|
|
|
/// Пропустим значение.
|
|
|
|
|
while (*end_pos && *end_pos != ';' && *end_pos != '&' && *end_pos != '#')
|
|
|
|
|
++end_pos;
|
|
|
|
|
|
|
|
|
|
/// Захватим ';' или '&' до или после параметра.
|
|
|
|
|
if (*end_pos == ';' || *end_pos == '&')
|
|
|
|
|
++end_pos;
|
2013-03-18 10:39:47 +00:00
|
|
|
|
else if (*(begin_pos - 1) == ';' || *(begin_pos - 1) == '&')
|
2013-03-18 10:27:45 +00:00
|
|
|
|
--begin_pos;
|
|
|
|
|
} while (false);
|
|
|
|
|
|
2013-03-18 10:39:47 +00:00
|
|
|
|
size_t cut_length = (url_end - url_begin) - (end_pos - begin_pos);
|
|
|
|
|
res_data.resize(res_offset + cut_length + 1);
|
2013-03-18 10:27:45 +00:00
|
|
|
|
memcpy(&res_data[res_offset], url_begin, begin_pos - url_begin);
|
|
|
|
|
memcpy(&res_data[res_offset] + (begin_pos - url_begin), end_pos, url_end - end_pos);
|
2013-03-18 10:39:47 +00:00
|
|
|
|
res_offset += cut_length + 1;
|
2013-03-18 10:27:45 +00:00
|
|
|
|
res_data[res_offset - 1] = 0;
|
|
|
|
|
res_offsets[i] = res_offset;
|
|
|
|
|
|
|
|
|
|
prev_offset = cur_offset;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2013-03-05 13:30:23 +00:00
|
|
|
|
class ExtractURLParametersImpl
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
Pos pos;
|
|
|
|
|
Pos end;
|
|
|
|
|
bool first;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static String getName() { return "extractURLParameters"; }
|
|
|
|
|
|
|
|
|
|
static void checkArguments(const DataTypes & arguments)
|
|
|
|
|
{
|
|
|
|
|
if (arguments.size() != 1)
|
|
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
2013-06-21 20:34:19 +00:00
|
|
|
|
+ toString(arguments.size()) + ", should be 1.",
|
2013-03-05 13:30:23 +00:00
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
2013-03-06 11:22:17 +00:00
|
|
|
|
|
|
|
|
|
if (!dynamic_cast<const DataTypeString *>(&*arguments[0]))
|
|
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName() + ". Must be String.",
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
2013-03-05 13:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void init(Block & block, const ColumnNumbers & arguments) {}
|
2013-08-07 11:25:02 +00:00
|
|
|
|
|
|
|
|
|
/// Возвращает позицию аргумента, являющегося столбцом строк
|
|
|
|
|
size_t getStringsArgumentPosition()
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-03-05 13:30:23 +00:00
|
|
|
|
|
|
|
|
|
/// Вызывается для каждой следующей строки.
|
|
|
|
|
void set(Pos pos_, Pos end_)
|
|
|
|
|
{
|
|
|
|
|
pos = pos_;
|
|
|
|
|
end = end_;
|
|
|
|
|
first = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Получить следующий токен, если есть, или вернуть false.
|
|
|
|
|
bool get(Pos & token_begin, Pos & token_end)
|
2013-03-06 09:00:58 +00:00
|
|
|
|
{
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
|
{
|
|
|
|
|
first = false;
|
|
|
|
|
pos = strchr(pos, '?');
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
++pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token_begin = pos;
|
|
|
|
|
pos = strchr(pos, '=');
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
++pos;
|
|
|
|
|
pos = strpbrk(pos, "&;#");
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
token_end = end;
|
|
|
|
|
else
|
2013-03-06 11:22:17 +00:00
|
|
|
|
token_end = pos++;
|
2013-03-06 09:00:58 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-08-02 13:55:43 +00:00
|
|
|
|
class ExtractURLParameterNamesImpl
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
Pos pos;
|
|
|
|
|
Pos end;
|
|
|
|
|
bool first;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static String getName() { return "extractURLParameterNames"; }
|
|
|
|
|
|
|
|
|
|
static void checkArguments(const DataTypes & arguments)
|
|
|
|
|
{
|
|
|
|
|
if (arguments.size() != 1)
|
|
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
|
|
|
|
+ toString(arguments.size()) + ", should be 1.",
|
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
|
|
|
|
|
|
|
|
|
if (!dynamic_cast<const DataTypeString *>(&*arguments[0]))
|
|
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName() + ". Must be String.",
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-07 11:25:02 +00:00
|
|
|
|
/// Возвращает позицию аргумента, являющегося столбцом строк
|
|
|
|
|
size_t getStringsArgumentPosition()
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-02 13:55:43 +00:00
|
|
|
|
void init(Block & block, const ColumnNumbers & arguments) {}
|
|
|
|
|
|
|
|
|
|
/// Вызывается для каждой следующей строки.
|
|
|
|
|
void set(Pos pos_, Pos end_)
|
|
|
|
|
{
|
|
|
|
|
pos = pos_;
|
|
|
|
|
end = end_;
|
|
|
|
|
first = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Получить следующий токен, если есть, или вернуть false.
|
|
|
|
|
bool get(Pos & token_begin, Pos & token_end)
|
|
|
|
|
{
|
|
|
|
|
if (pos == NULL)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
|
{
|
|
|
|
|
first = false;
|
|
|
|
|
pos = strchr(pos, '?');
|
|
|
|
|
}
|
2013-08-05 08:40:56 +00:00
|
|
|
|
else
|
|
|
|
|
pos = strchr(pos, '&');
|
|
|
|
|
|
2013-08-02 13:55:43 +00:00
|
|
|
|
if (pos == NULL)
|
2013-08-05 08:40:56 +00:00
|
|
|
|
return false;
|
2013-08-02 13:55:43 +00:00
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
token_begin = pos;
|
|
|
|
|
|
2013-08-05 08:40:56 +00:00
|
|
|
|
pos = strchr(pos, '=');
|
2013-08-02 13:55:43 +00:00
|
|
|
|
if (pos == NULL)
|
2013-08-05 08:40:56 +00:00
|
|
|
|
return false;
|
2013-08-02 13:55:43 +00:00
|
|
|
|
else
|
|
|
|
|
token_end = pos++;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
2013-03-06 09:00:58 +00:00
|
|
|
|
|
|
|
|
|
class URLHierarchyImpl
|
|
|
|
|
{
|
|
|
|
|
private:
|
2013-03-06 11:22:17 +00:00
|
|
|
|
Pos begin;
|
2013-03-06 09:00:58 +00:00
|
|
|
|
Pos pos;
|
|
|
|
|
Pos end;
|
|
|
|
|
|
|
|
|
|
public:
|
2013-03-06 11:22:17 +00:00
|
|
|
|
static String getName() { return "URLHierarchy"; }
|
2013-03-06 09:00:58 +00:00
|
|
|
|
|
|
|
|
|
static void checkArguments(const DataTypes & arguments)
|
|
|
|
|
{
|
|
|
|
|
if (arguments.size() != 1)
|
|
|
|
|
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
|
2013-06-21 20:34:19 +00:00
|
|
|
|
+ toString(arguments.size()) + ", should be 1.",
|
2013-03-06 09:00:58 +00:00
|
|
|
|
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
|
|
|
|
|
|
|
|
|
if (!dynamic_cast<const DataTypeString *>(&*arguments[0]))
|
|
|
|
|
throw Exception("Illegal type " + arguments[0]->getName() + " of first argument of function " + getName() + ". Must be String.",
|
|
|
|
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void init(Block & block, const ColumnNumbers & arguments) {}
|
2013-08-07 11:25:02 +00:00
|
|
|
|
|
|
|
|
|
/// Возвращает позицию аргумента, являющегося столбцом строк
|
|
|
|
|
size_t getStringsArgumentPosition()
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-03-06 09:00:58 +00:00
|
|
|
|
|
|
|
|
|
/// Вызывается для каждой следующей строки.
|
|
|
|
|
void set(Pos pos_, Pos end_)
|
|
|
|
|
{
|
2013-03-06 11:22:17 +00:00
|
|
|
|
begin = pos = pos_;
|
2013-03-06 09:00:58 +00:00
|
|
|
|
end = end_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Получить следующий токен, если есть, или вернуть false.
|
|
|
|
|
bool get(Pos & token_begin, Pos & token_end)
|
2013-03-05 13:30:23 +00:00
|
|
|
|
{
|
2013-03-06 11:22:17 +00:00
|
|
|
|
/// Код из URLParser.
|
|
|
|
|
|
|
|
|
|
if (pos == end)
|
2013-03-05 13:30:23 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
2013-03-06 11:22:17 +00:00
|
|
|
|
if (pos == begin)
|
2013-03-05 13:30:23 +00:00
|
|
|
|
{
|
2013-03-06 11:22:17 +00:00
|
|
|
|
/// Распарсим всё, что идёт до пути
|
|
|
|
|
|
|
|
|
|
/// Предположим, что протокол уже переведён в нижний регистр.
|
|
|
|
|
while (pos < end && ((*pos > 'a' && *pos < 'z') || (*pos > '0' && *pos < '9')))
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
/** Будем вычислять иерархию только для URL-ов, в которых есть протокол, и после него идут два слеша.
|
|
|
|
|
* (http, file - подходят, mailto, magnet - не подходят), и после двух слешей ещё хоть что-нибудь есть
|
|
|
|
|
* Для остальных просто вернём полный URL как единственный элемент иерархии.
|
|
|
|
|
*/
|
|
|
|
|
if (pos == begin || pos == end || !(*pos++ == ':' && pos < end && *pos++ == '/' && pos < end && *pos++ == '/' && pos < end))
|
|
|
|
|
{
|
|
|
|
|
pos = end;
|
|
|
|
|
token_begin = begin;
|
|
|
|
|
token_end = end;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Доменом для простоты будем считать всё, что после протокола и двух слешей, до следующего слеша или до ? или до #
|
|
|
|
|
while (pos < end && !(*pos == '/' || *pos == '?' || *pos == '#'))
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
if (pos != end)
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
token_begin = begin;
|
|
|
|
|
token_end = pos;
|
|
|
|
|
|
|
|
|
|
return true;
|
2013-03-05 13:30:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-06 11:22:17 +00:00
|
|
|
|
/// Идём до следующего / или ? или #, пропуская все те, что вначале.
|
|
|
|
|
while (pos < end && (*pos == '/' || *pos == '?' || *pos == '#'))
|
|
|
|
|
++pos;
|
|
|
|
|
if (pos == end)
|
2013-03-05 13:30:23 +00:00
|
|
|
|
return false;
|
2013-03-06 11:22:17 +00:00
|
|
|
|
while (pos < end && !(*pos == '/' || *pos == '?' || *pos == '#'))
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
if (pos != end)
|
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
|
|
token_begin = begin;
|
|
|
|
|
token_end = pos;
|
2013-03-05 13:30:23 +00:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2012-07-16 03:42:36 +00:00
|
|
|
|
/** Выделить кусок строки, используя Extractor.
|
|
|
|
|
*/
|
|
|
|
|
template <typename Extractor>
|
|
|
|
|
struct ExtractSubstringImpl
|
|
|
|
|
{
|
2013-09-15 05:51:43 +00:00
|
|
|
|
static void vector(const ColumnString::Chars_t & data, const ColumnString::Offsets_t & offsets,
|
|
|
|
|
ColumnString::Chars_t & res_data, ColumnString::Offsets_t & res_offsets)
|
2012-07-16 03:42:36 +00:00
|
|
|
|
{
|
|
|
|
|
size_t size = offsets.size();
|
|
|
|
|
res_offsets.resize(size);
|
2013-04-17 12:52:55 +00:00
|
|
|
|
res_data.reserve(size * Extractor::getReserveLengthForElement());
|
|
|
|
|
|
2012-07-16 03:42:36 +00:00
|
|
|
|
size_t prev_offset = 0;
|
|
|
|
|
size_t res_offset = 0;
|
|
|
|
|
|
|
|
|
|
/// Выделенный кусок.
|
|
|
|
|
Pos start;
|
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
|
{
|
|
|
|
|
Extractor::execute(reinterpret_cast<const char *>(&data[prev_offset]), offsets[i] - prev_offset - 1, start, length);
|
|
|
|
|
|
|
|
|
|
res_data.resize(res_data.size() + length + 1);
|
|
|
|
|
memcpy(&res_data[res_offset], start, length);
|
|
|
|
|
res_offset += length + 1;
|
|
|
|
|
res_data[res_offset - 1] = 0;
|
|
|
|
|
|
|
|
|
|
res_offsets[i] = res_offset;
|
|
|
|
|
prev_offset = offsets[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void constant(const std::string & data,
|
|
|
|
|
std::string & res_data)
|
|
|
|
|
{
|
|
|
|
|
Pos start;
|
|
|
|
|
size_t length;
|
|
|
|
|
Extractor::execute(data.data(), data.size(), start, length);
|
|
|
|
|
res_data.assign(start, length);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-15 05:51:43 +00:00
|
|
|
|
static void vector_fixed(const ColumnString::Chars_t & data, size_t n,
|
|
|
|
|
ColumnString::Chars_t & res_data)
|
2012-07-16 03:42:36 +00:00
|
|
|
|
{
|
|
|
|
|
throw Exception("Column of type FixedString is not supported by URL functions", ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Удалить кусок строки, используя Extractor.
|
|
|
|
|
*/
|
|
|
|
|
template <typename Extractor>
|
|
|
|
|
struct CutSubstringImpl
|
|
|
|
|
{
|
2013-09-15 05:51:43 +00:00
|
|
|
|
static void vector(const ColumnString::Chars_t & data, const ColumnString::Offsets_t & offsets,
|
|
|
|
|
ColumnString::Chars_t & res_data, ColumnString::Offsets_t & res_offsets)
|
2012-07-16 03:42:36 +00:00
|
|
|
|
{
|
|
|
|
|
res_data.reserve(data.size());
|
|
|
|
|
size_t size = offsets.size();
|
|
|
|
|
res_offsets.resize(size);
|
|
|
|
|
|
|
|
|
|
size_t prev_offset = 0;
|
|
|
|
|
size_t res_offset = 0;
|
|
|
|
|
|
|
|
|
|
/// Выделенный кусок.
|
|
|
|
|
Pos start;
|
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
|
{
|
|
|
|
|
const char * current = reinterpret_cast<const char *>(&data[prev_offset]);
|
|
|
|
|
Extractor::execute(current, offsets[i] - prev_offset - 1, start, length);
|
|
|
|
|
size_t start_index = start - reinterpret_cast<const char *>(&data[0]);
|
|
|
|
|
|
|
|
|
|
res_data.resize(res_data.size() + offsets[i] - prev_offset - length);
|
|
|
|
|
memcpy(&res_data[res_offset], current, start - current);
|
|
|
|
|
memcpy(&res_data[res_offset + start - current], start + length, offsets[i] - start_index - length);
|
|
|
|
|
res_offset += offsets[i] - prev_offset - length;
|
|
|
|
|
|
|
|
|
|
res_offsets[i] = res_offset;
|
|
|
|
|
prev_offset = offsets[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void constant(const std::string & data,
|
|
|
|
|
std::string & res_data)
|
|
|
|
|
{
|
|
|
|
|
Pos start;
|
|
|
|
|
size_t length;
|
|
|
|
|
Extractor::execute(data.data(), data.size(), start, length);
|
|
|
|
|
res_data.erase(start - data.data(), length);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-15 05:51:43 +00:00
|
|
|
|
static void vector_fixed(const ColumnString::Chars_t & data, size_t n,
|
|
|
|
|
ColumnString::Chars_t & res_data)
|
2012-07-16 03:42:36 +00:00
|
|
|
|
{
|
|
|
|
|
throw Exception("Column of type FixedString is not supported by URL functions", ErrorCodes::ILLEGAL_COLUMN);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct NameProtocol { static const char * get() { return "protocol"; } };
|
|
|
|
|
struct NameDomain { static const char * get() { return "domain"; } };
|
|
|
|
|
struct NameDomainWithoutWWW { static const char * get() { return "domainWithoutWWW"; } };
|
|
|
|
|
struct NameTopLevelDomain { static const char * get() { return "topLevelDomain"; } };
|
|
|
|
|
struct NamePath { static const char * get() { return "path"; } };
|
|
|
|
|
struct NameQueryString { static const char * get() { return "queryString"; } };
|
|
|
|
|
struct NameFragment { static const char * get() { return "fragment"; } };
|
|
|
|
|
struct NameQueryStringAndFragment { static const char * get() { return "queryStringAndFragment"; } };
|
|
|
|
|
|
|
|
|
|
struct NameCutWWW { static const char * get() { return "cutWWW"; } };
|
|
|
|
|
struct NameCutQueryString { static const char * get() { return "cutQueryString"; } };
|
|
|
|
|
struct NameCutFragment { static const char * get() { return "cutFragment"; } };
|
|
|
|
|
struct NameCutQueryStringAndFragment { static const char * get() { return "cutQueryStringAndFragment"; } };
|
|
|
|
|
|
2013-03-05 12:12:47 +00:00
|
|
|
|
struct NameExtractURLParameter { static const char * get() { return "extractURLParameter"; } };
|
2013-03-18 10:27:45 +00:00
|
|
|
|
struct NameCutURLParameter { static const char * get() { return "cutURLParameter"; } };
|
2013-03-05 12:12:47 +00:00
|
|
|
|
|
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractProtocol>, NameProtocol> FunctionProtocol;
|
2012-07-16 03:42:36 +00:00
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractDomain<false> >, NameDomain> FunctionDomain;
|
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractDomain<true> >, NameDomainWithoutWWW> FunctionDomainWithoutWWW;
|
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractTopLevelDomain>, NameTopLevelDomain> FunctionTopLevelDomain;
|
2013-03-05 12:12:47 +00:00
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractPath>, NamePath> FunctionPath;
|
2012-07-16 03:42:36 +00:00
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractQueryString<true> >, NameQueryString> FunctionQueryString;
|
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractFragment<true> >, NameFragment> FunctionFragment;
|
|
|
|
|
typedef FunctionStringToString<ExtractSubstringImpl<ExtractQueryStringAndFragment<true> >, NameQueryStringAndFragment> FunctionQueryStringAndFragment;
|
|
|
|
|
|
|
|
|
|
typedef FunctionStringToString<CutSubstringImpl<ExtractWWW>, NameCutWWW> FunctionCutWWW;
|
|
|
|
|
typedef FunctionStringToString<CutSubstringImpl<ExtractQueryString<false> >, NameCutQueryString> FunctionCutQueryString;
|
2013-03-05 12:12:47 +00:00
|
|
|
|
typedef FunctionStringToString<CutSubstringImpl<ExtractFragment<false> >, NameCutFragment> FunctionCutFragment;
|
2012-07-16 03:42:36 +00:00
|
|
|
|
typedef FunctionStringToString<CutSubstringImpl<ExtractQueryStringAndFragment<false> >, NameCutQueryStringAndFragment> FunctionCutQueryStringAndFragment;
|
|
|
|
|
|
2013-03-05 12:12:47 +00:00
|
|
|
|
typedef FunctionsStringSearchToString<ExtractURLParameterImpl, NameExtractURLParameter> FunctionExtractURLParameter;
|
2013-03-18 10:27:45 +00:00
|
|
|
|
typedef FunctionsStringSearchToString<CutURLParameterImpl, NameCutURLParameter> FunctionCutURLParameter;
|
2013-03-05 13:30:23 +00:00
|
|
|
|
typedef FunctionTokens<ExtractURLParametersImpl> FunctionExtractURLParameters;
|
2013-03-06 11:22:17 +00:00
|
|
|
|
typedef FunctionTokens<ExtractURLParametersImpl> FunctionExtractURLParameters;
|
|
|
|
|
typedef FunctionTokens<URLHierarchyImpl> FunctionURLHierarchy;
|
2013-08-02 13:55:43 +00:00
|
|
|
|
typedef FunctionTokens<ExtractURLParameterNamesImpl> FunctionExtractURLParameterNames;
|
2012-07-16 03:42:36 +00:00
|
|
|
|
|
|
|
|
|
}
|