FTL Template Reference
Load steps support customizable FTL (FreeMarker Template Language) templates through the following approaches:
Product-defined transformations – Built-in logic provided by Agile Data Engine to handle common transformation patterns.
Product-defined templates – Preconfigured FTL templates included with the product for reuse across typical use cases.
User-defined templates – Custom templates created by users to support platform-specific logic and dynamic SQL generation.
Product-defined transformations
Product-defined transformations can be invoked using the standard <variable>
syntax.
target_entity_attribute_list_with_transform_cast_and_positions
target_entity_attribute_list_with_transform_cast_exttable_parquet
Product-defined FTL templates
Predefined templates can be directly used without modification, see Product-defined FTL templates for a list of available templates and instructions.
User-defined FTL templates
Any FTL template defined in the CONFIG_LOAD_TEMPLATES package can be called as a user-defined FTL template in an entity load step using the following format:
<ftltemplate:MY_TEMPLATE:param1=value:param2=value>
Escape control characters with a backslash (\
) when needed.
Multi-line parameter values can be given with the triple-quote ("""
) syntax:
<ftltemplate:MY_TEMPLATE:param1=value:param2=
"""
SELECT
*
FROM
\<target_schema\>.\<target_entity_name\>
"""
>
The transformation engine uses the Freemarker Template Engine. The version currently in use is FreeMarker 2.3.33
.
Helper functions and macros
There are multiple built-in functions and macros that can be used directly within FTL templates. See Supported FTL functions and macros.
Template model
The template model includes various predefined objects that can be referenced within FTL templates. Refer to the diagram below for an overview of the available objects and their structure.

Below are examples of how to reference commonly used objects within the FTL template model:
Target database name:
model.targetDbName
Target load name:
model.load.name
Target entity:
model.load.target
Target entity attributes:
model.load.target.attributes
All source entities:
model.load.sources
These objects can be used within FTL templates to dynamically generate logic based on the load context.
Examples
Syntax examples
Basic usage
Refer to the above diagram for details about the template model structure. For a complete list of available predefined functions and macros, see Supported FTL functions and macros.
Note that model, function and macro references can be directly used in FTL templates. This example covers some basic use cases and syntax:
<#-- Assigning a variable -->
<#assign targetEntity = model.load.target>
<#-- Calling a function -->
<#assign targetRunId = loads.getAttributeForType(model.load.target.attributes, "RUN_ID")>
<#-- Printing a variable value -->
${targetEntity.name}
<#-- Calling a macro -->
<@sql.generateEntityAttributeList
attributes=model.load.target.attributes
prefix="src"
indentation=2
/>
<#-- Conditional logic -->
<#if model.load.target.type == "HUB">
-- Do something with HUB
<#elseif model.load.target.type == "SAT">
-- Do something with SAT
<#else>
-- Do something with ${model.load.target.type}
</#if>
<#-- Iterating over a list -->
<#list attributesFiltered as attr>
- ${attr.name}
</#list>
<#-- Defining a reusable sequence -->
<#assign excludeTypes = ["DV_HASHKEY", "DV_LOAD_TIME", "RUN_ID"]>
<#-- List filtering with the defined sequence -->
<#assign attributesFiltered = model.load.target.attributes?filter(
attr -> !(excludeTypes?seq_contains(attr.attributeType))
)>
Testing variable output
The ${var}
syntax is useful for printing variable values during development or debugging.
Defining and calling a function
This example defines a FreeMarker function to return a fully qualified entity name. This function builds the name in the format [database.]schema.table, depending on whether a database name is provided.
Function definition:
<#--
Function: entityFullyQualifiedName
Description: Constructs a fully qualified entity name in the format [database.]schema.table.
Parameters:
- entityObject (REQUIRED): an object with `schemaName` and `name` fields.
- databaseName (OPTIONAL): a string representing the database name. If provided, it will be prepended followed by a dot.
Returns:
- A string like "database.schema.table" or "schema.table" if no database name is given.
-->
<#function entityFullyQualifiedName entityObject databaseName="">
<#-- Construct the database prefix if a database name is provided -->
<#local targetDbUsed>
<#compress>
<#if databaseName?has_content>
${databaseName}.
</#if>
</#compress>
</#local>
<#-- Return the fully qualified name -->
<#return "${targetDbUsed}${entityObject.schemaName}.${entityObject.name}">
</#function>
Function call:
<#-- Context: Target entity is staging.STG_CUSTOMER -->
<#assign targetEntity = model.load.target>
<#assign fullNameWithDb = entityFullyQualifiedName(sampleEntity, "dw_dev")>
<#assign fullNameWithoutDb = entityFullyQualifiedName(sampleEntity)>
${fullNameWithDb}
${fullNameWithoutDb}
Output:
dw_dev.staging.STG_CUSTOMER
staging.STG_CUSTOMER
Importing from another FTL template
You can import and use content from other FTL templates to promote code reuse and maintainability.
In this example:
The function from the previous example is saved into a separate FTL template called
generic_functions
.It is then imported into another FTL template, and the function is called from there.
<#import "generic_functions" as utils>
<#assign targetEntity = model.load.target>
<#assign fullNameWithDb = utils.entityFullyQualifiedName(targetEntity, "dw_dev")>
Generate SELECT statement for mapped source entities
This FTL template can be used to accelerate e.g. publish fact or dimension load logic development, where multiple entities from the data warehouse need to be joined together. The goal is not to produce complete load logic, but rather to provide a helpful starting point for writing custom, manually-maintained logic tailored to the requirements.
FTL template definition:
<#--
This template dynamically generates a SELECT statement that:
- Lists attributes from all mapped source entities.
- Adds comments for entity names and attribute descriptions.
- Joins the source entities based on their mappings.
- Assigns dynamic aliases (t1, t2, ...) to each source entity.
-->
<#-- Assign the list of source entities -->
<#assign sources = model.load.sources>
<#-- Initialize a list to hold aliases -->
<#assign aliases = []>
<#-- Generate aliases for each source entity -->
<#list sources as source>
<#assign alias = "t${source_index + 1}">
<#assign aliases += [alias]>
</#list>
-- SELECT clause with attributes from all source entities
SELECT
<#list sources as source>
<#assign alias = aliases[source_index]>
-- Attributes from entity: ${source.schemaName}.${source.name}
<#list source.attributes as attr>
<#assign isLast = source?is_last && attr?is_last>
${alias}.${attr.name}<#if !isLast>,</#if><#if attr.description??> -- ${attr.description}</#if>
</#list>
</#list>
-- FROM clause with JOINs between source entities
-- Replace the below ON conditions with actual join conditions based on your schema
FROM ${sources[0].schemaName}.${sources[0].name} ${aliases[0]}
<#list sources[1..] as source>
<#assign alias = aliases[source_index + 1]>
JOIN ${source.schemaName}.${source.name} ${alias}
ON ${alias}.<join_column> = ${aliases[0]}.<join_column>
</#list>
Template used on an example load which has multiple mapped source entities: