[KEP-8] Jinja2 templating in SQL-string datasets via load_args.template_params
#5571
Replies: 2 comments 1 reply
-
|
Have you explored using |
Beta Was this translation helpful? Give feedback.
-
|
First of all, thank you for the very well explained KEP. On the functional need of such a mechanismLike @deepyaman ,I tend to nudge users into using more pythonic ways of transforming data with libraries like Ibis. That said, there are no really ibis equivalent to "write in python and delegate the compute to SQL" that I am aware of, and despite it beeing reasonably popular, we cannot expect most of our users to switch their codebase, especially since it has less private fundings that it used to have. I sometimes suggest to "load from SQL with minimal filters so the table is easy to handle and then worj in python", but for big tables that don't fit in memory, I don't have better suggestion than "use sql files" for most users. Given this, I've seen a lot of users doing some SQL transformations with kedro, and I've seen a fair share of them implementing exactly this feature, most of the times with suspicious design to say the least (including a data analysis team that used to create their own parser with reserved character and regex 😨). I've also seen a lot of reimplementation with subtle variations of this so I feel we need to support it in a way; without encouraging it too much. On the technical suggestions
ConclusionI can commit to shepherd this KEP if other validate traction, especially my presonal observations that some kedro users ARE doing this currently |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Q1. What are you trying to do?
Make it possible to write one SQL file and reuse it across multiple Kedro datasets that differ only in filter values — for example, the same feature-engineering query with small changes, e.g. extra filters, used in both training and inference, or the same query run over different date windows. Today this is impossible without either copy-pasting the SQL file. The proposal is to let users add a small block of variables under
load_args.template_paramsin the catalog, and have the dataset substitute those variables into the SQL before sending it to the database.Q2. What problem is this proposal NOT designed to solve?
load_argskey.Q3. How is it done today, and what are the limits of current practice?
pandas.SQLQueryDatasetandpolars.PolarsDatabaseDatasetaccept either an inlinesqlstring or afilepathto a.sqlfile and pass it to the driver verbatim.The practical workarounds in production Kedro projects today are:
events_train.sql,events_inference.sql, etc. Duplicated CTEs drift out of sync; bug fixes in one variant silently miss the others; new contributors cannot tell which file is canonical.Q4. What is new in your approach and why do you think it will be successful?
Three properties combined make this approach distinct from past attempts to template SQL:
.sql.j2template parameterised viatemplate_paramscollapses each family of duplicated files into one canonical source of truth, so a fix made once propagates everywhere.template_paramsis present inload_args. Absent that key, behaviour is byte-for-byte identical to today. There is no global switch, no file-extension magic, no environment variable.kedro_datasets._utils. Bothpandas.SQLQueryDatasetandpolars.PolarsDatabaseDatasetconsume the helper in two lines of theirload()method. Future SQL-string datasets (Snowflake, Redshift, ibis-based, custom contrib datasets) can opt in identically — avoiding the same divergence at the dataset-implementation layer that the feature itself eliminates at the SQL layer.Q5. Who cares? If you are successful, what difference will it make?
Q6. What are the risks?
{{in their SQL breaks rendering.template_paramsis set. A SQL string with notemplate_paramsis never sent through Jinja.template_paramsvalues.template_paramscontrols structure, not user-supplied values. Values that originate from end users must still go through SQLAlchemy bind parameters (load_args.params). Mirrors dbt and Airflow guidance.kedro-airflow. Lazy-imported inside the helper, so other datasets pay zero cost.Q7. How long will it take?
pandas.SQLQueryDatasetintegration: ~0.5 day (two-line call site + tests + docstring update).polars.PolarsDatabaseDatasetintegration: ~0.5 day (mirrors pandas).Estimated total: under two weeks of part-time effort.
Q8. What are the mid-term and final "exams" to check for success?
Mid-term:
render_sqlhelper has dedicated unit tests covering: no-op whentemplate_paramsis absent, inline string rendering, file-based rendering with sibling macro import,StrictUndefinedraising on a missing variable, and composability withload_args.paramsbind parameters.SQLQueryDatasettest suite passes unchanged (backwards compatibility).kedro#1385.Final:
Appendix A. Proposed API Changes
New
load_argskey onpandas.SQLQueryDatasetandpolars.PolarsDatabaseDataset:Semantics:
template_paramsis a mapping of variable name → value. Values may be any JSON-serialisable scalar or list (whatever Jinja2 will accept).template_paramsis present, the SQL (inline or file-loaded) is rendered through Jinja2 withStrictUndefinedbefore being passed to the driver.None, or{}), behaviour is unchanged.template_paramsis not forwarded to the underlying driver. It is consumed and removed duringload().load_args(params,parse_dates,index_col,chunksize, etc.) are unaffected.Backwards compatibility: fully additive. No existing catalog, dataset, or pipeline behaves differently. The only observable change for users not opting in is a new entry in the dataset's documented
load_argsreference.Forwards compatibility: the
template_paramskey is namespaced insideload_argsand so cannot collide with future positional/keyword arguments ofpd.read_sql_queryorpl.read_database.Appendix B. Design Sketch
Reusable helper
A new module
kedro_datasets/_utils/sql_templating.py:Re-exported from
kedro_datasets/_utils/__init__.py:This mirrors the existing
_utilsconventions (ConnectionMixin,validate_sub_path,spark_utils.get_spark,databricks_utils.split_filepath) and is trivially adoptable by any future SQL-string dataset.Integration in
pandas.SQLQueryDatasetTwo-line change inside
load()(current code atkedro-datasets/kedro_datasets/pandas/sql_dataset.py:553-563):Integration in
polars.PolarsDatabaseDatasetSymmetric change at
kedro-datasets/kedro_datasets_experimental/polars/polars_database_dataset.py:322-338:Macro example
Dependency declaration
In
kedro-datasets/pyproject.toml:And a corresponding new extra for the experimental Polars dataset (or whichever extra currently gates
PolarsDatabaseDataset).Testing strategy
A new file
kedro-datasets/tests/_utils/test_sql_templating.pycovers the helper in isolation:template_paramsisNone.template_paramsis{}.{{ var }}from an inline string.jinja2.UndefinedErrorwhen a referenced variable is missing.search_pathis provided.search_pathisNone.The existing
test_sql_dataset.pyandtest_polars_database_dataset.pysuites gain a small set of integration tests that confirm:template_paramsset renders the SQL before the driver sees it (verifiable via a stub engine that captures the executed string).template_paramsworks exactly as before (byte-for-byte SQL).template_paramsandparams(bind parameters) coexist.Appendix C. Rejected Designs
I didn't explore possibility of using OmegaConf instead of Jinja2
Beta Was this translation helpful? Give feedback.
All reactions