pub struct Ast { /* private fields */ }Expand description
The arena every node of one parse lives in.
Implementations§
Source§impl Ast
impl Ast
Sourcepub fn clear(&mut self)
pub fn clear(&mut self)
Empties the arena, keeping the memory it has already taken.
So that a second statement costs no allocations. Every one of these
vectors is empty at Ast::new and grows on its first push, so parsing
SELECT 1 takes half a dozen trips to the allocator - about 270 ns of a
1,337 ns prepare on this platform’s CRT heap. A parser handed a cleared
arena pushes into capacity that is already there.
It is a clear rather than a new for exactly that reason, and the
names are cleared with everything else: intern returns an existing id
for equal text, so a name left behind from the previous statement would
be a live id in the next one’s arena.
The names keep their byte buffers even though the names go
(task-2039). Clearing names drops every Name, and a Name owns
two Vec<u8> - so the vector’s capacity survived a clear and the two
allocations behind each entry in it did not, and a connection
re-compiling one statement went back to the allocator twice per
distinct name for ever. The buffers go on spare instead and intern
fills them again. [SPARE_NAME_BUFFERS] is what bounds the list.
Sourcepub fn charged_bytes(&self) -> usize
pub fn charged_bytes(&self) -> usize
Returns the number of arena bytes charged so far.
This is what the max_ast_bytes limit is charged against. It counts the
node structures rather than the source, because the source is borrowed.
Sourcepub fn intern(&mut self, text: Vec<u8>, quote: QuoteForm, span: Span) -> NameId
pub fn intern(&mut self, text: Vec<u8>, quote: QuoteForm, span: Span) -> NameId
Interns an identifier, returning the id of an equal existing entry when there is one.
A map rather than a scan (task-1932, H8). This walked every name
interned so far and compared three fields against each, so a statement
naming N distinct identifiers cost N-squared comparisons - and the
SqlLength default is 1 GiB, which leaves room for hundreds of
thousands of them. The key is exactly what the scan compared, so the
answer is the same one and only the cost changed.
The count is charged against Limit::Column for the same reason the
depth is charged below: a bound that exists in compat/limits.toml and
is enforced nowhere is not a bound. It is generous - a name is a column,
a table, an alias, a function or a collation, so one statement
legitimately interns more names than any one table has columns - and it
is a ceiling on an arena that has to fit in memory rather than a
statement about the schema.
Sourcepub fn intern_bytes(
&mut self,
text: &[u8],
quote: QuoteForm,
span: Span,
) -> NameId
pub fn intern_bytes( &mut self, text: &[u8], quote: QuoteForm, span: Span, ) -> NameId
Interns an identifier the caller does not own, returning the id of an equal existing entry when there is one.
The entry point that allocates nothing on a hit (task-2039). The
owned form above had to exist before the lookup could happen, so the
parser called identifier_text(..).into_owned() on every identifier
token whether or not the name was already interned - and intern then
folded a copy and cloned the key, four allocations for a name the arena
already held. This hashes the bytes where the source already has them.
A miss allocates what it stores and nothing else: the spelling and the
folded key, each taken from spare when a previous parse left one
there.
@param text - the identifier as written, with quoting already undone @param quote - how it was quoted, which decides whether it may become a string @param span - where this occurrence came from
Sourcepub fn name_count(&self) -> usize
pub fn name_count(&self) -> usize
Returns how many distinct identifiers have been interned.
Sourcepub fn max_expr_depth(&self) -> u32
pub fn max_expr_depth(&self) -> u32
Returns the depth of the deepest expression tree in the arena.
What Limit::ExprDepth is charged against. See expr_depths.
Sourcepub fn expr_depth(&self, id: ExprId) -> u32
pub fn expr_depth(&self, id: ExprId) -> u32
Returns how deep one expression’s own subtree is.
@param id - the node
Sourcepub fn folded(&self, id: NameId) -> &[u8] ⓘ
pub fn folded(&self, id: NameId) -> &[u8] ⓘ
Returns the folded key of an interned name, or an empty slice.
Sourcepub fn text(&self, id: NameId) -> &[u8] ⓘ
pub fn text(&self, id: NameId) -> &[u8] ⓘ
Returns the written spelling of an interned name, or an empty slice.
Sourcepub fn expr_count(&self) -> usize
pub fn expr_count(&self) -> usize
Returns the number of expression nodes in the arena.
Sourcepub fn add_select(&mut self, select: Select) -> SelectId
pub fn add_select(&mut self, select: Select) -> SelectId
Adds a compound SELECT.
Sourcepub fn add_core(&mut self, core: SelectCore) -> SelectCoreId
pub fn add_core(&mut self, core: SelectCore) -> SelectCoreId
Adds one arm of a compound SELECT.
Sourcepub fn core(&self, id: SelectCoreId) -> Option<&SelectCore>
pub fn core(&self, id: SelectCoreId) -> Option<&SelectCore>
Returns one arm of a compound SELECT.
Sourcepub fn add_from_term(&mut self, term: FromTerm) -> FromTermId
pub fn add_from_term(&mut self, term: FromTerm) -> FromTermId
Adds a FROM term.
Sourcepub fn from_term(&self, id: FromTermId) -> Option<&FromTerm>
pub fn from_term(&self, id: FromTermId) -> Option<&FromTerm>
Returns a FROM term.
Sourcepub fn from_term_mut(&mut self, id: FromTermId) -> Option<&mut FromTerm>
pub fn from_term_mut(&mut self, id: FromTermId) -> Option<&mut FromTerm>
Returns a FROM term for modification.
A join’s ON or USING clause follows the table it constrains, so the
term is stored first and its constraint attached once the parser has
read it. Building the term out of order instead would mean holding a
half-built node across a recursive parse.
Sourcepub fn add_window(&mut self, window: Window) -> WindowId
pub fn add_window(&mut self, window: Window) -> WindowId
Adds a window definition.
Trait Implementations§
impl Eq for Ast
Source§impl PartialEq for Ast
Two arenas are equal when they hold the same nodes.
impl PartialEq for Ast
Two arenas are equal when they hold the same nodes.
Hand-written rather than derived, because interned and spare are not
content (task-2039). interned is an index over names keyed by a hash
the arena seeds for itself, so two arenas parsed from the same text hold
the same names under different keys; spare is buffers the allocator has
not been given back yet, which the next parse may or may not use. Comparing
either would report two identical parses as different. The fields are
destructured by name and none is skipped with .., so a field added later
fails to compile here rather than being silently left out of equality.