(usage/script)= # Script
snowmobile.core.script ```{div} sn-dedent-v-b-h {class}`snowmobile.Script` parses a raw sql file into a composition of objects that can be leveraged for: ``` ```{div} sn-indent-h-cell-left-m, sn-block-list >Documentation and standardization of sql > >Access to individual statements within a script > >Lightweight control flow and QA > >Code generation and warehouse cleanup ```


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ## Overview ```{div} sn-dedent-list - [](script/model) - [](script/model/crash-course) - [](script/model/core-objects) - [Sections & Markup](script/model/sections-markup) - [](#statements) - [Quick Intro](script/statements/quick-intro) - [Statement Names](script/statements/statement-names) - [](#markup) - [](#tags) - [Single-Line](#single-line-tags) - [Multi-Line](#multi-line-tags) - [](#markers) - [](#patterns) ```
````{admonition} Note: If you're just wanting to run some sql :class: note, toggle, sn-code-pad, sn-clear-title, sn-subtle-background-blue ```{div} sn-unset-code-margins *The most straight-forward way to execute a local sql file is through the {xref}`SnowflakeConnection.execute_stream()` method*, the API for which can be accessed from an instance of with: ``` ```{code-block} python import snowmobile from codecs import open sn = snowmobile.connect() with open(sqlfile, 'r', encoding='utf-8') as f: for cur in sn.con.execute_stream(f): for ret in cur: print(ret) ``` ````
(script/model)= ### Model Intro ---
 

`````{admonition} **intro1.sql** :class: toggle, sn-fixture, sn-fixture-local, sn-unset-margins, sn-block, sn-code-pad, toggle-shown ````{div} sn-pre-code-s This section creates a [](#script) from the following file, *intro1.sql*, containing 3 bare sql statements: ```` ```{literalinclude} ../snippets/script/intro/intro1.sql :language: sql :lines: 1-10 :emphasize-lines: 3, 8, 10 ``` ```{div} sn-snippet [{fa}`file-code-o` intro1.sql](../snippets.md#intro1sql) ``` `````
(script/model/crash-course)= #### Crash Course ----


##### Creating a Script ``````{tabbed} snowmobile.Script(path=path) :class-content: sn-light-shadow :class-label: sn-tabbed-stack [snowmobile.Script](#script) identifies sql and metadata in a sql file; assuming *path* is a full path to intro1.sql , **script** can be created with: ```{literalinclude} ../snippets/script/intro/intro1.py :language: python :lines: 18-20 ``` Each command is instantiated as its own {class}`Statement` and stored according to its position in the original script; {meth}`script.dtl()` is used to send a summary of the contents parsed by **script** to the console: ```{literalinclude} ../snippets/script/intro/intro1.py :language: python :lines: 23-23 ``` ````{div} sn-output ```{literalinclude} ../snippets/script/intro/intro1.py :language: python :lines: 26-30 ``` ```` `````{admonition} FYI :class: note, sn-inline-block-container, sn-clear-title, sn-free-margin, sn-subtle-background-black {meth}`script.dtl()` is generating its \ output with something like:
```{literalinclude} ../snippets/script/intro/intro1.py :language: python :lines: 34-35 ``` ````{div} sn-output ```{literalinclude} ../snippets/script/intro/intro1.py :language: python :lines: 38-40 ``` ````
````` `````` ``````{tabbed} Instantiating from raw sql :class-content: sn-light-shadow :class-label: sn-tabbed-stack ```{admonition} Missing Content :class: error   ``` ``````
```{div} sn-pre-code-s Because these are bare sql statements.. ``` ```{literalinclude} ../snippets/script/intro/intro1.py :language: python :lines: 44-50 ```

(script/model/core-objects)= #### Core Objects ----

When [](#script) parses a string of sql, it identifies and stores [statements](#statements), [tags](#tags), and [markers](#markers): ````{div} sn-def, sn-dedent-v-t-container-neg, sn-linear-gradient-background, sn-thin-left-border-g
{class}`~snowmobile.core.statement.Statement`\ *A valid sql command, a standard set of attributes, and any information (optionally) provided in a [tag](#tags)* [**Tag**](#tags)\ *An arbitrary amount of information wrapped in a pre-defined, sql-compliant pattern that is parsable by {xref}`snowmobile`* [**Marker**](#markers)\ *A collection of information within a [tag](#tags) that is associated with the script (or a subset of it) as opposed to an individual statement*
```` ```{admonition} Note :class: note, sn-indent-h-cell-left-m, sn-indent-h-cell-right-m The simple zen is to enable the consistent, clear annotation of sql in a way that is: \ **(1)** easily human-readable / writable \ **(2)** syntactically (& idiomatically) compliant \ **(3)** identifiable and parsable by {xref}`snowmobile` **To that end, [snowmobile.Script](#script) intentionally ignores all comments that are not part of a [tag](#tags)**. ```

(script/model/sections-markup)= #### Sections & Markup ----

A {class}`~snowmobile.core.section.Section` can be instantiated from a {class}`~snowmobile.core.statement.Statement` or a [Marker](#markers), and the {class}`~snowmobile.core.markup.Markup` class combines multiple sections into a single document: ````{div} sn-def, sn-dedent-v-t-container-neg, sn-linear-gradient-background, sn-thin-left-border-g
{class}`~snowmobile.core.section.Section`\ *Performs additional operations on the attributes from a {class}`~snowmobile.core.statement.Statement` or a [Marker](#markers), typically to generate a 'headered' section in a markdown file or a sql statement stripped of surrounding comments* [**Markup**](#markup)\ *A context-specific collection of all sections within a script; capable of exporting markdown and tidied sql files*
```` Calling the {meth}`doc()` method on a [](#script) will return a {class}`~snowmobile.core.cfg.script.Markup` of its contents. The {meth}`Markup.save()` method will (by default) export a pair of files into a `.snowmobile` folder directly adjacent to the file with which the [](#script) was instantiated. A base case for this in practice is outlined in Example: **intro.sql** ```{admonition} Note :class: note, sn-indent-h-cell-left-m, sn-indent-h-cell-right-m (script-example-intro-sql)= The following options can be configured on an instance of {class}`~snowmobile.core.cfg.script.Markup` prior to calling {meth}`save()`: - Target location - File names - File types - File contents ```


`````{admonition} Example: **intro.sql** :class: sn-example, toggle, toggle-shown
```{literalinclude} ../snippets/script/intro.sql :language: sql :lines: 6-21 ``` ```{div} sn-snippet [{fa}`file-code-o` intro.sql](../snippets.md#introsql) ``` ````{div} sn-pre-code-s With a `path` to *intro.sql*, the following can be run: ```` ```{literalinclude} ../snippets/script/intro.py :language: python :lines: 13-21 :emphasize-lines: 9,9 ``` ```{div} sn-pre-code-s, sn-post-code Given *intro.sql* is here: ``` ````{div} sn-inline-block-container ```{code-block} bash sql/ └─ intro.sql ``` ```` ```{div} sn-pre-code-s {meth}`markup.save()` created the `.snowmobile` directory and exported the following files: ``` ````{div} sn-inline-block-container ```{code-block} bash :emphasize-lines: 4,5 sql/ ├─ intro.sql └─ .snowmobile/ ├─ intro.md └─ intro.sql ``` ````
````{tabbed} intro.md

intro.sql

* **Authored-By**: _Some Chap or Lass_ * **Authored-On**: _Some Day or Year_ **Impetus**: *SQL is older than time and isn't going anywhere; might we allow a simple markup syntax?*

(1) create table~sample_table; DDL

* **Description**: _This is an example statement description_ ```sql create or replace table sample_table ( col1 number(18,0), col2 number(18,0) ); ```
```` ````{tabbed} intro.sql ```{literalinclude} ../snippets/script/.snowmobile/intro/intro.sql :language: sql :lines: 1-17 ``` ```` `````

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
(script/statements/quick-intro)= ### Statements ---
 

`````{admonition} **script** :class: toggle, sn-fixture, sn-fixture-local, sn-unset-margins, sn-block, sn-code-pad, toggle-shown ```{div} sn-pre-code This section performs operations on the following {fa}`fixture script`: ``` ````{div} sn-indent-h-cell-even ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 17-17 ``` ```` ```{div} sn-indent-h-cell-text Where `path` ({class}`pathlib.Path` or {class}`str`) is a full path to [overview.sql](script/statements/quick-intro). ```

The 7 generic sql statements within [overview.sql](script/statements/quick-intro) are arbitrary and chosen based only on the loose criteria of: ```{div} sn-bold-list 1. Includes the minimum variety of [Statements](#st) and [](#markup) to demonstrate the fundamentals of how [](#script) parses sql 1. Is executable from top to bottom without requiring external setup ```

```{div} sn-snippet-h, sn-indent-h-cell [{fa}`file-code-o` overview.sql](../snippets.md#overviewsql) ``` ````{div} sn-indent-h-cell-even ```{literalinclude} ../snippets/script/overview.sql :language: sql :lines: 3-32 :emphasize-lines: 1, 6, 17, 20, 22, 24, 30 ``` ```` `````
#### Intro ----

```{div} sn-pre-code-s When a sql file is parsed by {class}`~snowmobile.Script`, each statement is identified and instantiated as its own {class}`~snowmobile.core.statement.Statement`. An overview of the statements within a script's context can be sent to the console with {meth}`script.dtl()`; in the case of [{fa}`fixture script`](script/statements/quick-intro), this looks like: ``` ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 18-18 ``` ````{div} sn-output ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 21-29 ``` ````
```{div} sn-pre-code-s Accessing the first and last statements of [{fa}`fixture script`](script/statements/quick-intro) and inspecting a few of their attributes can be done with: ``` ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 33-42 ``` ```{div} sn-pre-code-s, sn-post-code A {class}`~snowmobile.core.Statement` can be interacted with off the [](#script) or stored and used independently; for example, here are two ways that the first statement in [overview.sql](script/statements/quick-intro) can be executed: ``` ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 45-46 ```
Those above are several amongst a set of {class}`~snowmobile.core.Statement` attributes that can be used to alter the scope of a [](#script). For example, the following snippet filters out `drop` and `select` statements based on their {attr}`~snowmobile.core.name.Name.kw` attribute and returns a modified [{fa}`fixture script`](script/statements/quick-intro), `s`, that can be operated on within that context: ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 50-58 ``` ````{div} sn-output ```{literalinclude} ../snippets/script/overview-statement-intro.py :language: python :lines: 61-66 ``` ```` ```{div} sn-snippet, sn-indent-to-output [{fa}`file-code-o` overview-statement-intro.py](../snippets.md#overview-statement-intropy) ``` (script/statements/statement-names)= The following section outlines how these components are constructed.

#### Statement Names ----

```{div} sn-dedent-v-b-h-m *The intent of the following taxonomy is to define a standard such that the name for a given statement is:* ``` ```{div} sn-bold-list 1. Constructed from attributes that can be unambiguously parsed from a piece of raw sql 1. Structured such that user *provided* names can be easily implemented and loosely parsed into the same set of attributes as those *generated* from (**1**) ```
Every statement has a {class}`~snowmobile.core.name.Name` with a set of underlying properties that are used by the rest of the API; for each property, there is a *generated* (**_ge**) and *provided* (**_pr**) attribute from which its final value is sourced. (script/note1)= *Generated* attributes are populated for all st, whereas only those with a name specified in a [tag](#tags) have populated *provided* attributes; consequently, a *provided* value takes precedent over its *generated* counterpart. ````{admonition} Example: **nm** :class: sn-example ```{div} sn-pre-code-s The {class}`~snowmobile.core.name.Name.nm` value for a given statement will be equivalent to its {attr}`~snowmobile.core.name.Name.nm_pr` if present and its {attr}`~snowmobile.core.name.Name.nm_ge` otherwise. ``` ```` (script/statement/nm)= This resolution order is repeated across the underlying components of {class}`~snowmobile.core.name.Name.nm`, documented in the following sections.

`````{admonition} **s1** & **s4** :class: toggle, toggle-shown, sn-fixture, sn-fixture-local, sn-block, sn-increase-margin-v-container, sn-code-pad The below statements, `s1` and `s4`, from [{fa}`fixture script`](script/statements/quick-intro) are used throughout the remaining examples in [this section](#statement-names). ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 21-22 :emphasize-lines: 4-5 ``` `````
% (nm) ##### ***nm*** ````{div} sn-def, sn-dedent-v-t-container-neg, sn-linear-gradient-background, sn-def-container-side-border
```{div} sn-block-indented-h >{anchor}{delimiter}{desc} ``` {attr}`~snowmobile.core.name.Name.anchor`\ *what operation is a statement performing*

on what kind of object is it operating

{attr}`~snowmobile.core.cfg.script.Core.delimiter` ```{div} sn-multiline-def *a* configured value *with which to delimit the {attr}`~snowmobile.core.name.Name.anchor` and {attr}`~snowmobile.core.name.Name.desc`* ``` {attr}`~snowmobile.core.name.Name.desc`\ *A free-form piece of text associated with the statement*
```` % (-) ``````{tabbed} - :class-content: sn-light-shadow {attr}`~snowmobile.core.name.Name.nm` is the highest-level accessor for a {class}`~snowmobile.core.statement.Statement`. Its values for s1 & s4 (for example) can be inspected with: ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 24-25 ``` `````` ``````{tabbed} nm_pr :class-content: sn-light-shadow :class-label: sn-rm-background In determining the {attr}`~snowmobile.core.name.Name.nm` for s1 specifically, [{fa}`fixture script`](script/statements/quick-intro) is considering the following two lines of [overview.sql](script/statements/quick-intro): ```{literalinclude} ../snippets/script/overview.sql :language: sql :lines: 21-22 ``` ```{div} sn-post-code Each of these two lines above is the respective source for *provided* and *generated* information about the statement called out in Example: **nm** , the underlying values for which can be inspected in the same way: ``` ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 36-46 ``` ``````
% (anchor =======================================================) ##### ***anchor*** ````{div} sn-def, sn-dedent-v-t-container-neg, sn-linear-gradient-background, sn-def-container-side-border
```{div} sn-block-indented-h >{kw} {obj} ``` {attr}`~snowmobile.core.name.Name.kw`\ *the literal first sql keyword the statement contains* {attr}`~snowmobile.core.name.Name.obj`\ *the in-warehouse object found in the first line of the statement*
```` ``````{tabbed} - :class-content: sn-light-shadow {attr}`~snowmobile.core.name.Name.anchor` represents all text to the left of the first {attr}`~snowmobile.core.cfg.script.Core.delimiter` and when [*generated*](#statement-names) will fit the above structure to a varying degree depending on the sql being parsed and configurations in [](./snowmobile_toml.md). For s1 & s4 : ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 30-31 ``` ``````
% (kw ===========================================================) (script-kw)= ##### ***kw*** ``````{tabbed} - :class-content: sn-light-shadow {attr}`~snowmobile.core.name.Name.kw` is the literal first *keyword* within the [command](https://docs.snowflake.com/en/sql-reference/sql-all.html) being executed by a statement's sql. For s1 & s4: ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 59-60 ``` `````` ``````{tabbed} keyword-exceptions :class-content: sn-light-shadow The {ref}`keyword-exceptions ` section in the {ref}`[sql] ` block of {ref}`snowmobile-ext.toml ` enables specifying an alternate keyword for a literal keyword parsed from a statement's sql; alternate keywords will populate the statement's {attr}`~snowmobile.core.name.Name.kw_ge` as opposed to the literal keyword identified at the start of the statement: ```toml [sql.keyword-exceptions] "with" = "select" ``` The default included above is the reason that the {attr}`~snowmobile.core.name.Name.kw` for both the following statements is `select` as opposed to `select` and `with` respectively: ```{literalinclude} ../snippets/script/keyword_exceptions.sql :language: sql :lines: 3-10 ``` ``````
% (obj ==========================================================) (script-obj)= ##### ***obj*** ``````{tabbed} - :class-content: sn-light-shadow {attr}`~snowmobile.core.name.Name.obj` is determined by a case-insensitive, full ('word-boundaried') search through the **first** line of a statement's sql for a match within a pre-defined set of values. `````` ``````{tabbed} named-objects :class-content: sn-light-shadow The values for which a match is checked are configured in the {ref}`named-objects ` section within the {ref}`[sql] ` block of {ref}`snowmobile-ext.toml`, included below. Matching is peformed against values in the **literal** order as they are configured in {ref}`snowmobile-ext.toml` until a match is found or the list is exhausted; it is enforced that the object found cannot be equal to the {attr}`~snowmobile.core.name.Name.kw` for the statement. ```{code-block} toml named-objects = [ # 'grant' statements "select", "all", "drop", # base objects "temp table", "transient table", "table", "view", "schema", "warehouse", "file format", # plural bases "tables", "views", "schemas", ] ``` ````{admonition} Note :class: note, toggle, toggle-shown, sn-rm-t-m-code, sn-increase-margin-v-container The above order is as such so that table qualifiers for the following three (types of) statements are reflected in the {attr}`~snowmobile.core.name.Name.obj` for each.
```sql -- obj = 'table' create table any_table as select 1 as any_col; -- obj = 'transient table' create transient table any_table2 as select 1 as any_col; -- obj = 'temp table' create temp table any_table3 as select 1 as any_col; ``` ```` `````` ``````{tabbed} generic-anchors :class-content: sn-light-shadow A mapping of sql keywords to generic anchor names can be configured in the {ref}`generic-anchors` block within the {ref}`[sql] ` section of {ref}`snowmobile-ext.toml`, included below. ```{code-block} toml [sql.generic-anchors] "select" = "select data" "set" = "set param" "unset" = "unset param" "insert" = "insert into" "delete" = "delete from" ``` ``````
% (delimiter ====================================================) (script-delimiter)= ##### ***delimiter*** ``````{tabbed} - :class-content: sn-light-shadow {attr}`~snowmobile.core.cfg.script.Core.delimiter` is a literal constant specified in the `description-delimiter` field within the {ref}`[script.patterns.core]` section of [](./snowmobile_toml.md), the value for which can be accessed directly off with: ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 33-33 ``` ``````
% (desc =========================================================) (script-desc)= ##### ***desc*** `````{tabbed} - :class-content: sn-light-shadow
{attr}`~snowmobile.core.name.Name.desc` is a free-form text field loosely intended to be short-hand *description* for the statement.
The **generated** description for a statement, {attr}`~snowmobile.core.name.Name.desc_ge`, is a concatenation of a constant prefix and its index position within the script. The prefix used is configurable in the `description-index-prefix` field within the {ref}`[script.patterns.core]` section of [](./snowmobile_toml.md), the value for which can be accessed directly off with: ```python print(sn.cfg.script.patterns.core.prefix) #> s ```
The **provided** description for a statement, {attr}`~snowmobile.core.name.Name.desc_pr`, is all text to the right of the first *character* found matching the {ref}`script-delimiter` within a statement's {attr}`~snowmobile.core.name.Name.nm_pr`.
````` `````{tabbed} using *desc-is-simple* :class-content: sn-light-shadow
```{admonition} Warning :class: warning, sn-inherit-overflow The functionality outlined below is experimental and not under test. ``` Using parsed values for the {attr}`~snowmobile.core.name.Name.obj_ge` and {attr}`~snowmobile.core.name.Name.desc_ge` can be enabled by setting the {ref}`desc-is-simple` field to `true` in {ref}`snowmobile-ext.toml` or by modifying the attribute's value on an instance of . In the case of [{fa}`fixture script`](script/statements/quick-intro), this looks like: ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 64-68 ``` ````{div} sn-output2 ```{literalinclude} ../snippets/script/overview-statement-names.py :language: python :lines: 71-79 ``` ````
`````
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(tags)= ### Tags
``````{div} sn-tabbed-section `````{tabbed}   A tag contains an abitrary amount of information wrapped in a pre-defined opening/closing pattern. It can be associated with a {class}`~snowmobile.core.statement.Statement`, identified by its literal position relative to the statement's sql, or with a {class}`~snowmobile.core.cfg.script.Marker`, identified by its contents. The default pattern, highlighted in the below snippet from [snowmobile.toml](./snowmobile_toml.md), mirrors that of a standard sql block comment with an additional dash (`-`) on the inside of each component: ```{literalinclude} ../../snowmobile/core/pkg_data/snowmobile-template.toml :class: sn-dedent-v-b-container-neg :language: toml :lineno-start: 64 :lines: 64-66 :emphasize-lines: 2-3 ``` ````` ```````

### Markers
``````{div} sn-tabbed-section `````{tabbed} Overview ```{admonition} TODO :class: error Missing ``` ````` `````{tabbed} +- MORE CONTENT GOES HERE ````` ```````

### Markup
``````{div} sn-tabbed-section `````{tabbed}   ```{div} sn-hanging-p Using markup within a script enables: ``` - Defining accessors for individual statements - Adding descriptive information to individual statements or to the script itself - Finer-grained control of the script's execution - Generating documentation and cleansed sql files from the working version of a script ```{div} sn-dedent-v-b-h {xref}`snowmobile` introduces two sql-compliant forms of adding markup to a sql file: ``` 1. [Tags](#tags) enable constructing collections of attributes amidst sql st, including those directly associated with a particular statement 2. [Markers](#markers) are a collection of attributes that are **not** associated with a particular statement The following sections outline the different ways that [](#tags) and [](#markers) are implemented and utilized. ````` ``````` +++

##### Single-Line Tags
``````{div} sn-tabbed-section `````{tabbed} Overview Single-line tags are the simplest form of [markup](#markup) and can be used to succinctly denote a name for a given statement. When a single-line string directly precedes a statement and is wrapped in [a valid open/close pattern](#tags), it will be recognized as the *provided* name ({attr}`~snowmobile.core.name.Name.nm_pr`) and used as the statement's name ({attr}`~snowmobile.core.name.Name.nm`) as opposed to its *generated* name ({attr}`~snowmobile.core.name.Name.nm_ge`). ````` `````{tabbed} + Consider the sql file, *tags_single-line.sql*, containing two st, the first and second of which have valid and invalid single-line tags respectively: ````{div} sn-inline-flex-container ```{literalinclude} ../snippets/script/tags_single-line.sql :language: sql :lines: 1-8 ``` ```` Given a `path` to *tags_single-line.sql* and [{fa}`fixture sn`](../index.ipynb#fixture-sn), the following `script` can be created: ```python # Instantiate a Script from sql file script = snowmobile.Script(path=path, sn=sn) # Store individual statements for inspection s1, s2 = script(1), script(2) print(s1) #> Statement('I am a tag') print(s1.nm_ge) #> select data~s1 print(s1.nm_pr) #> I am a tag print(s1.nm) #> I am a tag print(s2) #> Statement('select data~s2') print(s2.nm_ge) #> select data~s2 print(s2.nm_pr) #> '' print(s2.nm) #> select data~s2 ``` ````{div} sn-indent-h-cell ```{admonition} Note :class: note, toggle, toggle-shown, sn-dedent-v-b-h-container The first statement has a valid tag directly preceding it, so its name ({attr}`~snowmobile.core.name.Name.nm`) is populated by the *provided* name within the tag ({attr}`~snowmobile.core.name.Name.nm_pr`) as opposed to the name that was *generated* for the statement ({attr}`~snowmobile.core.name.Name.nm_ge`). The second statement does **not** have a valid tag directly preceding it, so its generated name, `select data~s2`, is used and the line `/*-I am a tag that isn't positioned correctly-*/` is ignored. ``` ```` ````` ``````

##### Multi-Line Tags
```````{div} sn-tabbed-section `````{tabbed} Overview ```{div} sn-hanging-p Multi-line tags provide a method of associating multiple attributes with a {class}`~snowmobile.core.statement.Statement` according to the following syntax: - Attribute **names** must: 1. Start at the beginning of a new line 1. Have leading double underscores (`__`) 1. End with a single colon (`:`) - Attribute **values** have no restrictions except for several reserved attributes documented in the *reserved attributes* (LINK NEEDED) section below ``` ````` ``````{tabbed} + In practice, this looks something like the following: ```{literalinclude} ../snippets/script/tags_multi-line.sql :language: sql :lines: 1-13 ``` `````{div} sn-dedent-v-t-container ````{admonition} Tip :class: tip, toggle, toggle-shown Trailing wildcards can be appended to attribute **names** to denote how information will be rendered in generated documentation; this is covered in [Patterns - Wildcards](#wildcards) below. ```` ````` `````` ```````

#### Patterns
``````{div} sn-tabbed-section `````{tabbed}   ```{admonition} TODO :class: error Missing ``` ````` `````{tabbed} +- MORE CONTENT GOES HERE ````` ```````

##### Core
``````{div} sn-tabbed-section `````{tabbed} Overview ```{admonition} TODO :class: error Missing ``` ````` `````{tabbed} +- MORE CONTENT GOES HERE ````` ```````

##### Wildcards
``````{div} sn-tabbed-section `````{tabbed} Overview ```{admonition} TODO :class: error Missing ``` ````` `````{tabbed} +- MORE CONTENT GOES HERE ````` ```````