#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class SipHash;
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
using IdentifierNameSet = std::set;
class WriteBuffer;
using Strings = std::vector;
/** Element of the syntax tree (hereinafter - directed acyclic graph with elements of semantics)
*/
class IAST : public std::enable_shared_from_this, public TypePromotion
{
public:
ASTs children;
virtual ~IAST();
IAST() = default;
IAST(const IAST &) = default;
IAST & operator=(const IAST &) = default;
/** Get the canonical name of the column if the element is a column */
String getColumnName() const;
/** Same as the above but ensure no alias names are used. This is for index analysis */
String getColumnNameWithoutAlias() const;
virtual void appendColumnName(WriteBuffer &) const
{
throw Exception(ErrorCodes::LOGICAL_ERROR, "Trying to get name of not a column: {}", getID());
}
virtual void appendColumnNameWithoutAlias(WriteBuffer &) const
{
throw Exception(ErrorCodes::LOGICAL_ERROR, "Trying to get name of not a column: {}", getID());
}
/** Get the alias, if any, or the canonical name of the column, if it is not. */
virtual String getAliasOrColumnName() const { return getColumnName(); }
/** Get the alias, if any, or an empty string if it does not exist, or if the element does not support aliases. */
virtual String tryGetAlias() const { return String(); }
/** Set the alias. */
virtual void setAlias(const String & /*to*/)
{
throw Exception(ErrorCodes::LOGICAL_ERROR, "Can't set alias of {} of {}", getColumnName(), getID());
}
/** Get the text that identifies this element. */
virtual String getID(char delimiter = '_') const = 0; /// NOLINT
ASTPtr ptr() { return shared_from_this(); }
/** Get a deep copy of the tree. Cloned object must have the same range. */
virtual ASTPtr clone() const = 0;
/** Get hash code, identifying this element and its subtree.
* Hashing by default ignores aliases (e.g. identifier aliases, function aliases, literal aliases) which is
* useful for common subexpression elimination. Set 'ignore_aliases = false' if you don't want that behavior.
*/
using Hash = CityHash_v1_0_2::uint128;
Hash getTreeHash(bool ignore_aliases) const;
void updateTreeHash(SipHash & hash_state, bool ignore_aliases) const;
virtual void updateTreeHashImpl(SipHash & hash_state, bool ignore_aliases) const;
void dumpTree(WriteBuffer & ostr, size_t indent = 0) const;
std::string dumpTree(size_t indent = 0) const;
/** Check the depth of the tree.
* If max_depth is specified and the depth is greater - throw an exception.
* Returns the depth of the tree.
*/
size_t checkDepth(size_t max_depth) const
{
return checkDepthImpl(max_depth);
}
/** Get total number of tree elements
*/
size_t size() const;
/** Same for the total number of tree elements.
*/
size_t checkSize(size_t max_size) const;
/** Get `set` from the names of the identifiers
*/
virtual void collectIdentifierNames(IdentifierNameSet & set) const
{
for (const auto & child : children)
child->collectIdentifierNames(set);
}
template
void set(T * & field, const ASTPtr & child)
{
if (!child)
return;
T * casted = dynamic_cast(child.get());
if (!casted)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Could not cast AST subtree");
children.push_back(child);
field = casted;
}
template
void replace(T * & field, const ASTPtr & child)
{
if (!child)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Trying to replace AST subtree with nullptr");
T * casted = dynamic_cast(child.get());
if (!casted)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Could not cast AST subtree");
for (ASTPtr & current_child : children)
{
if (current_child.get() == field)
{
current_child = child;
field = casted;
return;
}
}
throw Exception(ErrorCodes::LOGICAL_ERROR, "AST subtree not found in children");
}
template
void setOrReplace(T * & field, const ASTPtr & child)
{
if (field)
replace(field, child);
else
set(field, child);
}
template
void reset(T * & field)
{
if (field == nullptr)
return;
const auto child = std::find_if(children.begin(), children.end(), [field](const auto & p)
{
return p.get() == field;
});
if (child == children.end())
throw Exception(ErrorCodes::LOGICAL_ERROR, "AST subtree not found in children");
children.erase(child);
field = nullptr;
}
/// After changing one of `children` elements, update the corresponding member pointer if needed.
void updatePointerToChild(void * old_ptr, void * new_ptr)
{
forEachPointerToChild([old_ptr, new_ptr](void ** ptr) mutable
{
if (*ptr == old_ptr)
*ptr = new_ptr;
});
}
/// Convert to a string.
/// Format settings.
struct FormatSettings
{
WriteBuffer & ostr;
bool one_line;
bool hilite;
IdentifierQuotingRule identifier_quoting_rule;
IdentifierQuotingStyle identifier_quoting_style;
bool show_secrets; /// Show secret parts of the AST (e.g. passwords, encryption keys).
char nl_or_ws; /// Newline or whitespace.
LiteralEscapingStyle literal_escaping_style;
bool print_pretty_type_names;
bool enforce_strict_identifier_format;
explicit FormatSettings(
WriteBuffer & ostr_,
bool one_line_,
bool hilite_ = false,
IdentifierQuotingRule identifier_quoting_rule_ = IdentifierQuotingRule::WhenNecessary,
IdentifierQuotingStyle identifier_quoting_style_ = IdentifierQuotingStyle::Backticks,
bool show_secrets_ = true,
LiteralEscapingStyle literal_escaping_style_ = LiteralEscapingStyle::Regular,
bool print_pretty_type_names_ = false,
bool enforce_strict_identifier_format_ = false)
: ostr(ostr_)
, one_line(one_line_)
, hilite(hilite_)
, identifier_quoting_rule(identifier_quoting_rule_)
, identifier_quoting_style(identifier_quoting_style_)
, show_secrets(show_secrets_)
, nl_or_ws(one_line ? ' ' : '\n')
, literal_escaping_style(literal_escaping_style_)
, print_pretty_type_names(print_pretty_type_names_)
, enforce_strict_identifier_format(enforce_strict_identifier_format_)
{
}
FormatSettings(WriteBuffer & ostr_, const FormatSettings & other)
: ostr(ostr_)
, one_line(other.one_line)
, hilite(other.hilite)
, identifier_quoting_rule(other.identifier_quoting_rule)
, identifier_quoting_style(other.identifier_quoting_style)
, show_secrets(other.show_secrets)
, nl_or_ws(other.nl_or_ws)
, literal_escaping_style(other.literal_escaping_style)
, print_pretty_type_names(other.print_pretty_type_names)
, enforce_strict_identifier_format(other.enforce_strict_identifier_format)
{
}
void writeIdentifier(const String & name, bool ambiguous) const;
void checkIdentifier(const String & name) const;
};
/// State. For example, a set of nodes can be remembered, which we already walk through.
struct FormatState
{
/** The SELECT query in which the alias was found; identifier of a node with such an alias.
* It is necessary that when the node has met again, output only the alias.
*/
std::set> printed_asts_with_alias;
};
/// The state that is copied when each node is formatted. For example, nesting level.
struct FormatStateStacked
{
UInt16 indent = 0;
bool need_parens = false;
bool expression_list_always_start_on_new_line = false; /// Line feed and indent before expression list even if it's of single element.
bool expression_list_prepend_whitespace = false; /// Prepend whitespace (if it is required)
bool surround_each_list_element_with_parens = false;
bool allow_operators = true; /// Format some functions, such as "plus", "in", etc. as operators.
size_t list_element_index = 0;
const IAST * current_select = nullptr;
};
void format(const FormatSettings & settings) const
{
FormatState state;
formatImpl(settings, state, FormatStateStacked());
}
virtual void formatImpl(const FormatSettings & /*settings*/, FormatState & /*state*/, FormatStateStacked /*frame*/) const
{
throw Exception(ErrorCodes::LOGICAL_ERROR, "Unknown element in AST: {}", getID());
}
/// Secrets are displayed regarding show_secrets, then SensitiveDataMasker is applied.
/// You can use Interpreters/formatWithPossiblyHidingSecrets.h for convenience.
String formatWithPossiblyHidingSensitiveData(
size_t max_length,
bool one_line,
bool show_secrets,
bool print_pretty_type_names,
IdentifierQuotingRule identifier_quoting_rule,
IdentifierQuotingStyle identifier_quoting_style) const;
/** formatForLogging and formatForErrorMessage always hide secrets. This inconsistent
* behaviour is due to the fact such functions are called from Client which knows nothing about
* access rights and settings. Moreover, the only use case for displaying secrets are backups,
* and backup tools use only direct input and ignore logs and error messages.
*/
String formatForLogging(size_t max_length = 0) const
{
return formatWithPossiblyHidingSensitiveData(
/*max_length=*/max_length,
/*one_line=*/true,
/*show_secrets=*/false,
/*print_pretty_type_names=*/false,
/*identifier_quoting_rule=*/IdentifierQuotingRule::WhenNecessary,
/*identifier_quoting_style=*/IdentifierQuotingStyle::Backticks);
}
String formatForErrorMessage() const
{
return formatWithPossiblyHidingSensitiveData(
/*max_length=*/0,
/*one_line=*/true,
/*show_secrets=*/false,
/*print_pretty_type_names=*/false,
/*identifier_quoting_rule=*/IdentifierQuotingRule::WhenNecessary,
/*identifier_quoting_style=*/IdentifierQuotingStyle::Backticks);
}
virtual bool hasSecretParts() const { return childrenHaveSecretParts(); }
void cloneChildren();
enum class QueryKind : uint8_t
{
None = 0,
Select,
Insert,
Delete,
Create,
Drop,
Undrop,
Rename,
Optimize,
Check,
Alter,
Grant,
Revoke,
Move,
System,
Set,
Use,
Show,
Exists,
Describe,
Explain,
Backup,
Restore,
KillQuery,
ExternalDDL,
Begin,
Commit,
Rollback,
SetTransactionSnapshot,
AsyncInsertFlush
};
/// Return QueryKind of this AST query.
virtual QueryKind getQueryKind() const { return QueryKind::None; }
/// For syntax highlighting.
static const char * hilite_keyword;
static const char * hilite_identifier;
static const char * hilite_function;
static const char * hilite_operator;
static const char * hilite_alias;
static const char * hilite_substitution;
static const char * hilite_none;
protected:
bool childrenHaveSecretParts() const;
/// Some AST classes have naked pointers to children elements as members.
/// This method allows to iterate over them.
virtual void forEachPointerToChild(std::function) {}
private:
size_t checkDepthImpl(size_t max_depth) const;
/** Forward linked list of ASTPtr to delete.
* Used in IAST destructor to avoid possible stack overflow.
*/
ASTPtr next_to_delete = nullptr;
ASTPtr * next_to_delete_list_head = nullptr;
};
}