.. _jinja_templater: Jinja templater ^^^^^^^^^^^^^^^ The Jinja templater uses Jinja2_ to render templates. .. _Jinja2: https://jinja.palletsprojects.com/ There are multiple, complementary ways of configuring the Jinja templater. - Reading variables and Jinja macros directly from the SQLFLuff config file - Loading macros from a path - Using a library .. list-table:: Overview of Jinja templater's configuration options :header-rows: 1 * - Configuration - Variables - Macros - Filters - Documentation * - Config file - ✅ - ✅ - ❌ - `Complex Jinja Variable Templating`_ and `Jinja Macro Templating (from config)`_ * - Macro Path - ❌ - ✅ - ❌ - `Jinja Macro Templating (from file)`_ * - Library - ✅ - ✅ - ✅ - `Library Templating`_ For example, a snippet from a :code:`.sqlfluff` file that uses all config options: .. code-block:: cfg [sqlfluff] templater = jinja [sqlfluff:templater:jinja] apply_dbt_builtins = True load_macros_from_path = my_macros loader_search_path = included_templates library_path = sqlfluff_libs exclude_macros_from_path = my_macros_exclude [sqlfluff:templater:jinja:context] my_list = ['a', 'b', 'c'] MY_LIST = ("d", "e", "f") my_where_dict = {"field_1": 1, "field_2": 2} [sqlfluff:templater:jinja:macros] a_macro_def = {% macro my_macro(n) %}{{ n }} + {{ n * 2 }}{% endmacro %} Complex Jinja Variable Templating """"""""""""""""""""""""""""""""" Apart from the Generic variable templating that is supported for all templaters, two more advanced features of variable templating are available for Jinja. *case sensitivity* and *native python types*. Both are illustrated in the following example: .. code-block:: cfg [sqlfluff:templater:jinja:context] my_list = ['a', 'b', 'c'] MY_LIST = ("d", "e", "f") my_where_dict = {"field_1": 1, "field_2": 2} .. code-block:: SQL+Jinja SELECT {% for elem in MY_LIST %} '{{elem}}' {% if not loop.last %}||{% endif %} {% endfor %} as concatenated_list FROM tbl WHERE {% for field, value in my_where_dict.items() %} {{field}} = {{value}} {% if not loop.last %}and{% endif %} {% endfor %} ...will render as... .. code-block:: sql SELECT 'd' || 'e' || 'f' as concatenated_list FROM tbl WHERE field_1 = 1 and field_2 = 2 Note that the variable was replaced in a case sensitive way and that the settings in the config file were interpreted as native python types. Jinja Macro Templating (from config) """""""""""""""""""""""""""""""""""" Macros (which also look and feel like *functions* are available only in the *jinja* templater. Similar to :ref:`generic_variable_templating`, these are specified in config files, what's different in this case is how they are named. Similar to the *context* section above, macros are configured separately in the *macros* section of the config. Consider the following example. If passed the following *.sql* file: .. code-block:: SQL+Jinja SELECT {{ my_macro(6) }} FROM some_table ...and the following configuration in *.sqlfluff* in the same directory (note the tight control of whitespace): .. code-block:: cfg [sqlfluff:templater:jinja:macros] a_macro_def = {% macro my_macro(n) %}{{ n }} + {{ n * 2 }}{% endmacro %} ...then before parsing, the sql will be transformed to: .. code-block:: sql SELECT 6 + 12 FROM some_table Note that in the code block above, the variable name in the config is *a_macro_def*, and this isn't apparently otherwise used anywhere else. Broadly this is accurate, however within the configuration loader this will still be used to overwrite previous *values* in other config files. As such this introduces the idea of config *blocks* which could be selectively overwritten by other configuration files downstream as required. Jinja Macro Templating (from file) """""""""""""""""""""""""""""""""" In addition to macros specified in the config file, macros can also be loaded from files or folders. This is specified in the config file: .. code-block:: cfg [sqlfluff:templater:jinja] load_macros_from_path = my_macros,other_macros ``load_macros_from_path`` is a comma-separated list of :code:`.sql` files or folders. Locations are *relative to the config file*. For example, if the config file above was found at :code:`/home/my_project/.sqlfluff`, then SQLFluff will look for macros in the folders :code:`/home/my_project/my_macros/` and :code:`/home/my_project/other_macros/`, including any of their subfolders. Any macros defined in the config will always take precedence over a macro defined in the path. ``exclude_macros_from_path`` works in the same manner as ``load_macros_from_path`` but allows you to have sqlfluff ignore certain macros. This can be useful if you have custom jinja tags. Macros loaded from these files are available in every :code:`.sql` file without requiring a Jinja :code:`include` or :code:`import`. They are loaded into the `Jinja Global Namespace `_. **Note:** The :code:`load_macros_from_path` setting also defines the search path for Jinja `include `_ or `import `_. As with loaded macros, subdirectories are also supported. For example, if :code:`load_macros_from_path` is set to :code:`my_macros`, and there is a file :code:`my_macros/subdir/my_file.sql`, you can do: .. code-block:: jinja {% include 'subdir/my_file.sql' %} If you would like to define the Jinja search path without also loading the macros into the global namespace, use the :code:`loader_search_path` setting instead. .. note:: Throughout the templating process **whitespace** will still be treated rigorously, and this includes **newlines**. In particular you may choose to provide *dummy* macros in your configuration different from the actual macros used in production. **REMEMBER:** The reason SQLFluff supports macros is to *enable* it to parse templated sql without it being a blocker. It shouldn't be a requirement that the *templating* is accurate - it only needs to work well enough that *parsing* and *linting* are helpful. .. _builtin_jinja_blocks: Builtin Jinja Macro Blocks """""""""""""""""""""""""" One of the main use cases which inspired *SQLFluff* as a project was `dbt`_. It uses jinja templating extensively and leads to some users maintaining large repositories of sql files which could potentially benefit from some linting. .. note:: *SQLFluff* has now a tighter integration with dbt through the "dbt" templater. It is the recommended templater for dbt projects. If used, it eliminates the need for the overrides described in this section. To use the dbt templater, go to :ref:`dbt_templater`. *SQLFluff* anticipates this use case and provides some built in macro blocks in the :ref:`defaultconfig` which assist in getting started with `dbt`_ projects. In particular it provides mock objects for: * *ref*: The mock version of this provided simply returns the model reference as the name of the table. In most cases this is sufficient. * *config*: A regularly used macro in `dbt`_ to set configuration values. For linting purposes, this makes no difference and so the provided macro simply returns nothing. .. note:: If there are other builtin macros which would make your life easier, consider submitting the idea (or even better a pull request) on `github`_. .. _`dbt`: https://www.getdbt.com/ .. _`github`: https://www.github.com/sqlfluff/sqlfluff .. _jinja_library_templating: Library Templating """""""""""""""""" If using *SQLFluff* with jinja as your templater, you may have library function calls within your sql files that can not be templated via the normal macro templating mechanisms: .. code-block:: SQL+Jinja SELECT foo, bar FROM baz {{ dbt_utils.group_by(2) }} To template these libraries, you can use the `sqlfluff:jinja:library_path` config option: .. code-block:: cfg [sqlfluff:templater:jinja] library_path = sqlfluff_libs This will pull in any python modules from that directory and allow sqlfluff to use them in templates. In the above example, you might define a file at `sqlfluff_libs/dbt_utils.py` as: .. code-block:: python def group_by(n): return "GROUP BY 1,2" If an `__init__.py` is detected, it will be loaded alongside any modules and submodules found within the library path. .. code-block:: SQL+Jinja SELECT {{ custom_sum('foo', 'bar') }}, {{ foo.bar.another_sum('foo', 'bar') }} FROM baz `sqlfluff_libs/__init__.py`: .. code-block:: python def custom_sum(a: str, b: str) -> str: return a + b `sqlfluff_libs/foo/__init__.py`: .. code-block:: python # empty file `sqlfluff_libs/foo/bar.py`: .. code-block:: python def another_sum(a: str, b: str) -> str: return a + b Additionally, the library can be used to expose `Jinja Filters `_ to the Jinja environment used by SQLFluff. This is achieve by setting a global variable named ``SQLFLUFF_JINJA_FILTERS``. ``SQLFLUFF_JINJA_FILTERS`` is a dictionary where * dictionary keys map to the Jinja filter name * dictionary values map to the Python callable For example, to make the Airflow filter ``ds`` available to SQLFLuff, add the following to the `__init__.py` of the library: .. code-block:: python # https://github.com/apache/airflow/blob/main/airflow/templates.py#L53 def ds_filter(value: datetime.date | datetime.time | None) -> str | None: """Date filter.""" if value is None: return None return value.strftime("%Y-%m-%d") SQLFLUFF_JINJA_FILTERS = {"ds": ds_filter} Now, ``ds`` can be used in SQL .. code-block:: SQL+Jinja SELECT "{{ "2000-01-01" | ds }}"; Jinja loader search path """""""""""""""""""""""" The Jinja environment can be configured to search for files included with `include `_ or `import `_ in a list of folders. This is specified in the config file: .. code-block:: cfg [sqlfluff:templater:jinja] loader_search_path = included_templates,other_templates ``loader_search_path`` is a comma-separated list of folders. Locations are *relative to the config file*. For example, if the config file above was found at :code:`/home/my_project/.sqlfluff`, then SQLFluff will look for included files in the folders :code:`/home/my_project/included_templates/` and :code:`/home/my_project/other_templates/`, including any of their subfolders. For example, this will read from :code:`/home/my_project/included_templates/my_template.sql`: .. code-block:: jinja {% include 'included_templates/my_template.sql' %} Any folders specified in the :code:`load_macros_from_path` setting are automatically appended to the ``loader_search_path``. It is not necessary to specify a given directory in both settings. Unlike the :code:`load_macros_from_path` setting, any macros within these folders are *not* automatically loaded into the global namespace. They must be explicitly imported using the `import `_ Jinja directive. If you would like macros to be automatically included in the global Jinja namespace, use the :code:`load_macros_from_path` setting instead. Interaction with ``--ignore=templating`` """""""""""""""""""""""""""""""""""""""" Ignoring Jinja templating errors provides a way for users to use SQLFluff while reducing or avoiding the need to spend a lot of time adding variables to ``[sqlfluff:templater:jinja:context]``. When ``--ignore=templating`` is enabled, the Jinja templater behaves a bit differently. This additional behavior is *usually* but not *always* helpful for making the file at least partially parsable and fixable. It definitely doesn’t **guarantee** that every file can be fixed, but it’s proven useful for some users. Here's how it works: * Within the expanded SQL, undefined variables are automatically *replaced* with the corresponding string value. * If you do: ``{% include query %}``, and the variable ``query`` is not defined, it returns a “file” containing the string ``query``. * If you do: ``{% include "query_file.sql" %}``, and that file does not exist or you haven’t configured a setting for ``load_macros_from_path`` or ``loader_search_path``, it returns a “file” containing the text ``query_file``. For example: .. code-block:: SQL+Jinja select {{ my_variable }} from {% include "my_table.sql" %} is interpreted as: .. code-block:: sql select my_variable from my_table The values provided by the Jinja templater act *a bit* (not exactly) like a mixture of several types: * ``str`` * ``int`` * ``list`` * Jinja's ``Undefined`` `class `_ Because the values behave like ``Undefined``, it's possible to replace them using Jinja's ``default()`` `filter `_. For example: .. code-block:: SQL+Jinja select {{ my_variable | default("col_a") }} from my_table is interpreted as: .. code-block:: sql select col_a from my_table