limitsdb 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
@@ -0,0 +1,71 @@
1
+ # For example: source_owner, history_owner, table_name, source_orphan_purge
2
+ # All database identifiers in keys values must follow database identifier rules:
3
+ # unquoted → normalized by engine; quoted → case-sensitive and may include special characters.
4
+ # Example: "@inv_dsp_date" → normalized name, '@"inv_dsp_date"' → exact name
5
+ # For examples:
6
+ # source_owner: GL # unquoted, normalized by engine
7
+ # source_owner: '"Gl" # quoted, exact name: Gl
8
+ # source_owner: "\"gL*\"" # quoted, exact name with special characters: gL*
9
+ # source_additional_filter_expr: "@\"INV_STATUS\"='D'" # quoted column name, normalized by engine
10
+ #
11
+ # Example ILM configuration file
12
+ #
13
+ # tables: # List of tables to apply ILM
14
+ # - source_owner: GL # Source owner of table (must match DD case)
15
+ # history_owner: GLHST # History owner of table (must match DD case)
16
+ # table_name: INV_HEAD # Table name. In this example this is a parent table (must match DD case)
17
+ # frecuency: D # Frecuency of ILM application. Expected values (D: dayly, M: monthly)
18
+ # hint_expr: full(A) # Hint to apply in selection from source table. Use table alias A for source table and B, C, etc for referenced tables
19
+ # conds: # List of conditions for selection
20
+ # - is_active: true # if condition is active
21
+ # retain_months_source: 3 # Months to retain in source
22
+ # retain_months_history: 11 # Months to retain in history after transfered to history env. If 0 or undefined there will be no history, only source deletion
23
+ # purge_date_expr: "@INV_DSP_DATE" # Date expression for ILM to compare with. Columns must be marked @ preffix. (must follow DB identifier rules)
24
+ # source_additional_filter_expr: "@INV_STATUS='D'" # Optional additional expression to apply in ILM selection. (must follow DB identifier rules)
25
+ # - is_active: true # Another condition. Can differ in any definition of other conditions of same table
26
+ # retain_months_source: 1
27
+ # retain_months_history: 0
28
+ # purge_date_expr: "@INVD_DATE"
29
+ # source_additional_filter_expr: "@INV_STATUS='A'"
30
+ # history_addtl_filter_expr: "@INV_STATUS='H'" # Optional history-only expression. Falls back to additional_filter_expr when omitted. (must follow DB identifier rules)
31
+ # - source_owner: GL # Child table definition. Inherit conditions from parent tables. Can have multiple parent tables
32
+ # history_owner: GL
33
+ # table_name: INV_DET
34
+ # frecuency: D
35
+ # hint_expr: full(A) full(B) use_hash(A, B)
36
+ # history_hint_expr: full(A)
37
+ # referencing_tables: INV_HEAD B # List of parent tables
38
+ # join_expr: "@ JOIN GL.INV_HEAD B ON B.INV_ID=A.INV_ID" # Join expression to link to parent table (must follow DB identifier rules)
39
+ # source_orphan_purge: true # Archive source rows whose referenced parent is absent; requires use_added_columns: true
40
+ # orphan_check_column: B.INV_ID # Parent column checked with IS NULL after the left outer join
41
+ # # conds:
42
+ # # - is_active: true # If a table with no conds but an explicit inactive one is considered inactive
43
+ # - source_owner: GL # Parent table
44
+ # history_owner: GL
45
+ # table_name: DSP_HEAD
46
+ # frecuency: D
47
+ # hint_expr: full(A)
48
+ # conds:
49
+ # - is_active: true
50
+ # retain_months_source: 3
51
+ # retain_months_history: 0
52
+ # purge_date_expr: "@DSP_DATE"
53
+ # additional_filter_expr: "@DSP_STATUS='D'"
54
+ # - is_active: true
55
+ # retain_months_source: 1
56
+ # retain_months_history: 0
57
+ # purge_date_expr: "@DSP_DATE"
58
+ # additional_filter_expr: "@DSP_STATUS='A'"
59
+ # - source_owner: GL # Child table with three parent tables. Inherite conditions of all parent tables
60
+ # history_owner: GL
61
+ # table_name: DSP_DET
62
+ # frecuency: D
63
+ # hint_expr: full(A) full(B) full(C) full(D) use_hash(A, B, C, D)
64
+ # history_hint_expr: full(A)
65
+ # referencing_tables: GL.DSP_HEAD B, GL.INV_DET C, GL.INV_HEAD D
66
+ # join_expr: "@ JOIN GL.DSP_HEAD B ON B.DSP_ID=A.DSP_ID @ JOIN GL.INV_DET C ON C.IND_ID = A.IND_ID @ JOIN GL.INV_HEAD ON D.INV_ID= C.INV_ID"
67
+ # source_orphan_purge: true
68
+ # orphan_check_column: B.DSP_ID, C.IND_ID, D.INV_ID # Any NULL check marks the source row as orphaned
69
+ # has_lob_columns: true
70
+ # conds:
71
+ # # - is_active: true
@@ -0,0 +1,586 @@
1
+ Metadata-Version: 2.4
2
+ Name: limitsdb
3
+ Version: 0.5.0
4
+ Summary: Configuration-driven ILM (Information Lifecycle Management) for production and history databases
5
+ License: Apache License
6
+ Version 2.0, January 2004
7
+ http://www.apache.org/licenses/
8
+
9
+ Copyright © 2025 Inversiones Etimo SpA and contributors
10
+
11
+ Licensed under the Apache License, Version 2.0 (the "License");
12
+ you may not use this file except in compliance with the License.
13
+ You may obtain a copy of the License at
14
+
15
+ http://www.apache.org/licenses/LICENSE-2.0
16
+
17
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
18
+
19
+ 1. Definitions.
20
+
21
+ "License" shall mean the terms and conditions for use, reproduction,
22
+ and distribution as defined by Sections 1 through 9 of this document.
23
+
24
+ "Licensor" shall mean the copyright owner or entity authorized by
25
+ the copyright owner that is granting the License.
26
+
27
+ "Legal Entity" shall mean the union of the acting entity and all
28
+ other entities that control, are controlled by, or are under common
29
+ control with that entity.
30
+
31
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
32
+ permissions granted by this License.
33
+
34
+ "Source" form shall mean the preferred form for making modifications,
35
+ including but not limited to software source code, documentation
36
+ source, and configuration files.
37
+
38
+ "Object" form shall mean any form resulting from mechanical
39
+ transformation or translation of a Source form.
40
+
41
+ "Work" shall mean the work of authorship made available under the
42
+ License, as indicated by a copyright notice that is included in or
43
+ attached to the work.
44
+
45
+ "Derivative Works" shall mean any work that is based on (or derived
46
+ from) the Work and for which the editorial revisions, annotations,
47
+ elaborations, or other modifications represent, as a whole, an
48
+ original work of authorship.
49
+
50
+ "Contribution" shall mean any work of authorship submitted to the
51
+ Licensor for inclusion in the Work.
52
+
53
+ "Contributor" shall mean Licensor and any individual or Legal Entity
54
+ on behalf of whom a Contribution has been received by Licensor and
55
+ subsequently incorporated within the Work.
56
+
57
+ 2. Grant of Copyright License.
58
+
59
+ Subject to the terms and conditions of this License, each Contributor
60
+ hereby grants to You a perpetual, worldwide, non-exclusive,
61
+ no-charge, royalty-free, irrevocable copyright license to reproduce,
62
+ prepare Derivative Works of, publicly display, publicly perform,
63
+ sublicense, and distribute the Work and such Derivative Works in
64
+ Source or Object form.
65
+
66
+ 3. Grant of Patent License.
67
+
68
+ Subject to the terms and conditions of this License, each Contributor
69
+ hereby grants to You a perpetual, worldwide, non-exclusive,
70
+ no-charge, royalty-free, irrevocable patent license to make, have
71
+ made, use, offer to sell, sell, import, and otherwise transfer the
72
+ Work.
73
+
74
+ 4. Redistribution.
75
+
76
+ You may reproduce and distribute copies of the Work or Derivative
77
+ Works thereof in any medium, with or without modifications, and in
78
+ Source or Object form, provided that You meet the following
79
+ conditions:
80
+
81
+ (a) You must give any other recipients of the Work or Derivative
82
+ Works a copy of this License; and
83
+
84
+ (b) You must cause any modified files to carry prominent notices
85
+ stating that You changed the files; and
86
+
87
+ (c) You must retain, in the Source form of any Derivative Works that
88
+ You distribute, all copyright, patent, trademark, and attribution
89
+ notices from the Source form of the Work; and
90
+
91
+ (d) If the Work includes a "NOTICE" text file as part of its
92
+ distribution, then any Derivative Works that You distribute must
93
+ include a readable copy of the attribution notices contained within
94
+ such NOTICE file.
95
+
96
+ 5. Submission of Contributions.
97
+
98
+ Unless You explicitly state otherwise, any Contribution intentionally
99
+ submitted for inclusion in the Work by You to the Licensor shall be
100
+ under the terms and conditions of this License.
101
+
102
+ 6. Trademarks.
103
+
104
+ This License does not grant permission to use the trade names,
105
+ trademarks, service marks, or product names of the Licensor.
106
+
107
+ 7. Disclaimer of Warranty.
108
+
109
+ Unless required by applicable law or agreed to in writing, Licensor
110
+ provides the Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR
111
+ CONDITIONS OF ANY KIND.
112
+
113
+ 8. Limitation of Liability.
114
+
115
+ In no event shall any Contributor be liable to You for damages arising
116
+ from the use of the Work.
117
+
118
+ 9. Accepting Warranty or Additional Liability.
119
+
120
+ While redistributing the Work or Derivative Works thereof, You may
121
+ choose to offer support, warranty, indemnity, or other liability
122
+ obligations and/or rights consistent with this License.
123
+ License-File: LICENSE
124
+ License-File: LICENSE-DUAL.md
125
+ License-File: NOTICE
126
+ Keywords: database,oracle,ilm,data lifecycle,archiving
127
+ Author: Gustavo Cantizano
128
+ Author-email: gustavo.cantizano@etimo.cl
129
+ Maintainer: Inversiones Etimo SpA
130
+ Maintainer-email: contacto@etimo.cl
131
+ Requires-Python: >=3.12,<4.0
132
+ Classifier: Programming Language :: Python :: 3
133
+ Classifier: Programming Language :: Python :: 3.12
134
+ Classifier: License :: OSI Approved :: Apache Software License
135
+ Classifier: Operating System :: OS Independent
136
+ Classifier: Topic :: Database
137
+ Classifier: Topic :: System :: Systems Administration
138
+ Requires-Dist: cryptography (>=46.0.2,<47.0.0)
139
+ Requires-Dist: oracledb (>=2.5.1,<3.0.0)
140
+ Requires-Dist: platformdirs (>=4.5.0,<5.0.0)
141
+ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
142
+ Project-URL: Homepage, https://github.com/etimochile/limitsdb
143
+ Project-URL: Issues, https://github.com/etimochile/limitsdb/issues
144
+ Project-URL: Repository, https://github.com/etimochile/limitsdb
145
+ Description-Content-Type: text/markdown
146
+
147
+ # LimitsDb
148
+
149
+ Configuration-driven ILM (Information Lifecycle Management) runner for operational databases. It applies retention policies to your application tables—archiving, purging, and keeping datasets lean—so source stays fast while history remains auditable. LimitsDb is designed to support multiple database engines; **Oracle** is supported today, with an engine-agnostic ILM model that can extend to others.
150
+
151
+ ## Overview (What it does)
152
+
153
+ - **Executes ILM policies on real schemas.** You describe which tables are in scope, how they relate (parents/children), and the rules that decide when rows move to history or are purged.
154
+ - **Targets source and history environments.** Choose whether to act on **source** (move/purge) or **history** (downstream cleanup) for the same ILM model.
155
+ - **Retains exactly what you need.** Per-table, per-condition retention windows (e.g., “keep 3 months in source, 11 months in history”), with the ability to define multiple conditions per table.
156
+ - **Understands relationships.** Reference parent tables, define join expressions, and optionally purge **orphans** when child rows lose their parent.
157
+ - **Handles heavy tables safely.** Chunked processing and parallelism keep large operations predictable and observable.
158
+ - **Works with hints and special cases.** Add optimizer hints, mark LOB-heavy tables, and tune behavior per table.
159
+ - **Dry-run anytime.** Generate the exact operations without executing them, to review and approve planned changes.
160
+ - **Keeps secrets safe.** Database credentials are stored encrypted and transparently decrypted at runtime.
161
+ - **Automates control DB objects creation.** Creates missing control schemas and DB objects.
162
+ - **Automates history objects creation and updates.** Creates missing history tables and correct columns, contraints and indexes for ILM executions.
163
+
164
+ ### Typical outcomes
165
+
166
+ - **Slimmer source**: old/inactive rows are moved or purged on a schedule.
167
+ - **Auditable history**: history schemas keep what you configure, for the period you define.
168
+ - **Predictable cost/perf**: smaller hot datasets, fewer full-table scans, faster maintenance windows.
169
+
170
+ ### How you use it (at a glance)
171
+
172
+ 1. **Model your ILM**: list the tables, joins to parents, and one or more conditions with retention windows and optional filters.
173
+ 2. **Choose the target**: run against **SOURCE_ILM** (move/purge in source) or **HISTORY_ILM** (history-side cleanup).
174
+ 3. **Run safely**: start with **Query-only / Dry-run** to review the actions; then run for real.
175
+ 4. **Repeat** on your preferred cadence (via scheduler/cron or your release pipeline).
176
+
177
+ ---
178
+
179
+ ## Requirements
180
+
181
+ - **Python**: 3.12+ — https://www.python.org/downloads/
182
+ - **Oracle**: `oracledb` runs in thin mode (pure Python, no Oracle Instant Client required).
183
+ - **PostgreSQL**: planned for a future adapter; not currently accepted in configuration.
184
+
185
+ ## Installation
186
+
187
+ ### Recommended — pipx (no virtual environment management needed)
188
+
189
+ ```bash
190
+ pip install pipx
191
+ pipx install limitsdb
192
+ ```
193
+
194
+ `pipx` creates and manages the virtual environment automatically. After installation,
195
+ `ldb-run`, `ldb-impl`, `ldb-crypt`, and `ldb-init` are available in your terminal
196
+ on any platform without activation.
197
+
198
+ ### Alternative — pip with a virtual environment
199
+
200
+ ```bash
201
+ python -m venv .venv
202
+ # Windows:
203
+ .venv\Scripts\activate
204
+ # macOS / Linux:
205
+ source .venv/bin/activate
206
+
207
+ pip install limitsdb
208
+ ```
209
+
210
+ For development setup (cloning the repo, running tests, CI) see [CONTRIBUTING.md](CONTRIBUTING.md).
211
+
212
+ ## Quickstart
213
+
214
+ Spin up a minimal end-to-end run with the built-in scaffolding tools:
215
+
216
+ 1. **Create config files** for a schema/profile pair:
217
+
218
+ ```bash
219
+ ldb-init --schema billing --profile dev
220
+ ```
221
+
222
+ 2. **Fill in connection details** in `~/.config/LimitsDb/schemas/billing/config.dev.yml` and secrets in `secrets.dev.json`.
223
+
224
+ 3. **Encrypt secrets** in place:
225
+
226
+ ```bash
227
+ ldb-crypt --schema billing --profile dev
228
+ ```
229
+
230
+ 4. **Dry-run the ILM plan** (no data changes):
231
+
232
+ ```bash
233
+ ldb-run --schema billing --profile dev --action SOURCE_ILM --mode PREVIEW
234
+ ```
235
+
236
+ 5. **Execute for real** once you are satisfied with the plan:
237
+
238
+ ```bash
239
+ ldb-run --schema billing --profile dev --action SOURCE_ILM --mode EXECUTE
240
+ ```
241
+
242
+ ## CLI Commands
243
+
244
+ After installation, the following commands are available directly in your terminal:
245
+
246
+ - `ldb-init` — scaffold configuration files for a named configuration set
247
+ - `ldb-crypt` — encrypt cleartext secrets in place
248
+ - `ldb-impl` — verify and create DB control schemas and objects
249
+ - `ldb-run` — main ILM runner
250
+
251
+ ```bash
252
+ ldb-init --help
253
+ ldb-crypt --help
254
+ ldb-impl --help
255
+ ldb-run --help
256
+ ```
257
+
258
+ ## Initialize Configuration
259
+
260
+ Scaffold the configuration files for a named configuration set and optional environment:
261
+
262
+ ```bash
263
+ ldb-init --schema <SCHEMA> [--profile <PROFILE>] [--config-dir <DIR>] [--overwrite] [--no-examples]
264
+ # examples:
265
+ # ldb-init --schema billing
266
+ # ldb-init --schema billing --profile prod
267
+ # ldb-init --schema billing --profile dev --config-dir /etc/limitsdb
268
+ # ldb-init --schema billing --profile dev --no-examples
269
+ ```
270
+
271
+ `--schema` is a name you choose to group all configuration files for one
272
+ system or application (e.g., `billing`, `crm`). It is not a database schema;
273
+ it is simply the folder name under which the files live.
274
+
275
+ `--profile` is optional. Use it when the same configuration set targets
276
+ multiple environments (e.g., `dev`, `prod`). When supplied, the profile name
277
+ becomes part of every file name as `.<PROFILE>`. When omitted, file names carry
278
+ no extra suffix.
279
+
280
+ `--overwrite` replaces existing files. Without it, any file that already exists
281
+ is left unchanged; only missing files are created.
282
+
283
+ `--no-examples` skips copying the ILM example file (`ilm[.<PROFILE>].example.yml`).
284
+
285
+ `--config-dir` scaffolds under a custom root instead of `~/.config/LimitsDb`.
286
+
287
+ Generated files (default root is `~/.config/LimitsDb`):
288
+
289
+ ```
290
+ ~/.config/LimitsDb/
291
+ └─ schemas/
292
+ └─ <SCHEMA>/
293
+ ├─ config[.<PROFILE>].yml # commented defaults + help (from the Config model)
294
+ ├─ ilm[.<PROFILE>].yml # empty list (you fill it)
295
+ ├─ ilm[.<PROFILE>].example.yml # example copied verbatim from the package
296
+ └─ secrets[.<PROFILE>].json # secret fields with empty values
297
+ ```
298
+
299
+ ## Secrets & Encryption
300
+
301
+ ```bash
302
+ ldb-crypt --schema <SCHEMA> [--profile <PROFILE>] [--config-dir <DIR>]
303
+ ```
304
+
305
+ 1. Edit `secrets[.<PROFILE>].json` and fill in the credentials:
306
+
307
+ ```json
308
+ {
309
+ "source_password": "",
310
+ "history_password": "",
311
+ "admin_source_password": "",
312
+ "admin_history_password": ""
313
+ }
314
+ ```
315
+
316
+ 2. Encrypt in place:
317
+
318
+ ```bash
319
+ ldb-crypt --schema billing --profile prod
320
+ ```
321
+
322
+ `ldb-crypt` creates or reuses a local encryption key and replaces every
323
+ non-empty plaintext value in `secrets[.<PROFILE>].json` with an
324
+ `enc:v1:aes256gcm:…` token. Already-encrypted values are left unchanged.
325
+ If this step is skipped, `ldb-run` and `ldb-impl` will encrypt any plaintext
326
+ secrets automatically before the first run, provided the secrets file is in the
327
+ default user configuration directory or an explicit `--config-dir` is supplied.
328
+ `ldb-crypt` is a convenience to encrypt them without executing ILM.
329
+
330
+ The encryption key is stored in your user context and protected by OS
331
+ permissions. Never version or copy the key file.
332
+
333
+ ## Bootstrap Database Objects
334
+
335
+ ```bash
336
+ ldb-impl --schema <SCHEMA> [--profile <PROFILE>] [--config-dir <DIR>] [--config-file <YAML>] [--set key=value] [--log-level LEVEL]
337
+ ```
338
+
339
+ `ldb-impl` creates the database objects that LimitsDb needs to operate. Run it
340
+ once before the first `ldb-run`, and again whenever a new environment is
341
+ provisioned. All operations are idempotent: existing objects are left unchanged
342
+ and only missing ones are created.
343
+
344
+ **`ldb-impl` is optional** if a DBA has already created all required objects
345
+ manually. LimitsDb does not require objects to have been created through
346
+ `ldb-impl`; it only requires that the objects exist with the expected structure
347
+ before `ldb-run` is executed.
348
+
349
+ What it creates in both source and history environments:
350
+
351
+ - **Role** — grants the application user the privileges required by LimitsDb.
352
+ - **User** — the application user with its default tablespace and role.
353
+ - **Control tables** — `LDB_CTL` (execution state per table) and `LDB_LOG`
354
+ (audit log of each run).
355
+ - **Sequence** — `LDB_LOG_ID` for audit log identifiers.
356
+ - **Database link** — bidirectional links between source and history so the ILM
357
+ engine can reach both environments from either side.
358
+
359
+ `ldb-impl` requires all four credential pairs to be present and encrypted in
360
+ `secrets[.<PROFILE>].json`: `source_password`, `history_password`,
361
+ `admin_source_password`, and `admin_history_password`. Run `ldb-crypt` first.
362
+
363
+ ## Run ILM
364
+
365
+ ```bash
366
+ ldb-run --schema <SCHEMA> [--profile <PROFILE>] --action SOURCE_ILM|HISTORY_ILM [options]
367
+ # examples:
368
+ # ldb-run --schema billing --profile prod --action SOURCE_ILM
369
+ # ldb-run --schema billing --action HISTORY_ILM
370
+ ```
371
+
372
+ Common overrides:
373
+
374
+ ```
375
+ --mode VALIDATE|PLAN|PREVIEW|SCRIPT|EXECUTE # VALIDATE setup, PLAN DAG, PREVIEW simulate, SCRIPT emit SQL, EXECUTE run
376
+ --chunk-size <int> # Rows per chunk when processing large tables
377
+ --parallel-max <int> # Maximum number of tables processed in parallel
378
+ --log-level DEBUG|INFO|WARNING|ERROR|CRITICAL # Adjust logger verbosity
379
+ --ilm-config-file <path> # Point to a specific ILM YAML file
380
+ --config-dir <root> # Override config root discovery
381
+ --config-file <yaml> # Extra overlay (highest priority)
382
+ --set key=value # Generic override (supports dotted keys)
383
+ ```
384
+
385
+ Examples:
386
+
387
+ ```bash
388
+ # Dry-run with verbose logging
389
+ ldb-run --schema billing --profile prod --action SOURCE_ILM --mode PREVIEW --log-level DEBUG
390
+
391
+ # Apply quick overrides without editing files
392
+ ldb-run --schema billing --profile prod --action SOURCE_ILM --mode EXECUTE --set chunk_size=200000 --set parallel_max=8
393
+ ```
394
+
395
+ ### Admin credentials in ldb-run
396
+
397
+ `ldb-run` uses admin credentials (`admin_source_password`,
398
+ `admin_history_password`) for two operations: granting table privileges to the
399
+ application role, and creating or altering history tables before an `EXECUTE`
400
+ run. Both operations are idempotent.
401
+
402
+ If admin credentials are absent from `secrets[.<PROFILE>].json`, the privilege
403
+ grants are skipped silently — the assumption is that grants are already in
404
+ place. History table management in `EXECUTE` mode still requires admin
405
+ credentials in the current version.
406
+
407
+ ### Configuration parameters reference
408
+
409
+ | Parameter | Purpose | Default / values |
410
+ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
411
+ | `action` | ILM target (`SOURCE_ILM` moves/purges source, `HISTORY_ILM` cleans downstream history). | Default `SOURCE_ILM`; choices `SOURCE_ILM`, `HISTORY_ILM`. |
412
+ | `mode` | Execution mode: `VALIDATE` config/credentials, `PLAN` dependency order, `PREVIEW` simulate (a.k.a. legacy `DRY_RUN`), `SCRIPT` emit SQL, `EXECUTE` run changes. | Default `PREVIEW`; choices `VALIDATE`, `PLAN`, `PREVIEW`, `SCRIPT`, `EXECUTE`. |
413
+ | `chunk_size` | Rows per chunk when processing large tables. | Default `100000`. |
414
+ | `use_added_columns` | Populate derived columns in history tables. | Default `True` (boolean toggle). |
415
+ | `add_ldb_columns` | Add LimitsDb execution-date columns in history tables. | Default `True` (boolean toggle). |
416
+ | `parallel_max` | Maximum number of parallel processes. | Default `10`. |
417
+ | `db_engine` | Database engine. | Default and only current choice: `"oracle"`. |
418
+ | `log_level` | Logging level. | Default `"INFO"`; choices `"DEBUG"`, `"INFO"`, `"WARNING"`, `"ERROR"`, `"CRITICAL"`. |
419
+ | `schema` | Configuration set name: identifies a system or application (e.g., `billing`). Not a database schema — it is the folder that groups all configuration files for one target. | Required on CLI; no persisted default. |
420
+ | `profile` | Environment name (e.g., `dev`, `prod`). Differentiates configuration files for different environments within the same configuration set. | Optional; default `null`. |
421
+ | `ilm_config_file` | YAML file with tables (bypass DB discovery). | Optional; default `null`. |
422
+ | `source_dsn` | Source DSN / connection descriptor. | Default empty string. |
423
+ | `source_username` | Source username. | Default empty string. |
424
+ | `source_password` | Source password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
425
+ | `history_dsn` | History DSN / connection descriptor. | Default empty string. |
426
+ | `history_username` | History username. | Default empty string. |
427
+ | `history_password` | History password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
428
+ | `admin_source_username` | Admin source username. | Default empty string. |
429
+ | `admin_source_password` | Admin source password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
430
+ | `admin_history_username` | Admin history username. | Default empty string. |
431
+ | `admin_history_password` | Admin history password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
432
+
433
+ #### Overrides matrix (CLI, env, YAML)
434
+
435
+ | Parameter | CLI flag | Environment variable | YAML key |
436
+ | ------------------------ | -------------------------- | ---------------------------- | ------------------------------------------------ |
437
+ | `action` | `--action` | `LDB_ACTION` | `config[.<PROFILE>].yml: action` |
438
+ | `mode` | `--mode` | `LDB_MODE` | `config[.<PROFILE>].yml: mode` |
439
+ | `chunk_size` | `--chunk-size` | `LDB_CHUNK_SIZE` | `config[.<PROFILE>].yml: chunk_size` |
440
+ | `use_added_columns` | — | — | `config[.<PROFILE>].yml: use_added_columns` |
441
+ | `add_ldb_columns` | — | — | `config[.<PROFILE>].yml: add_ldb_columns` |
442
+ | `parallel_max` | `--parallel-max` | `LDB_PARALLEL_MAX` | `config[.<PROFILE>].yml: parallel_max` |
443
+ | `db_engine` | `--db-engine` | `LDB_DB_ENGINE` | `config[.<PROFILE>].yml: db_engine` |
444
+ | `log_level` | `--log-level` | `LDB_LOG_LEVEL` | `config[.<PROFILE>].yml: log_level` |
445
+ | `schema` | `--schema` | `LDB_SCHEMA` | CLI only (not stored). |
446
+ | `profile` | `--profile` | `LDB_PROFILE` | CLI only (not stored). |
447
+ | `ilm_config_file` | `--ilm-config-file` | `LDB_ILM_CONFIG_FILE` | CLI only (not stored). |
448
+ | `source_dsn` | `--source-dsn` | `LDB_SOURCE_DSN` | `config[.<PROFILE>].yml: source_dsn` |
449
+ | `source_username` | `--source-username` | `LDB_SOURCE_USERNAME` | `config[.<PROFILE>].yml: source_username` |
450
+ | `source_password` | — | — | `secrets[.<PROFILE>].json: source_password` |
451
+ | `history_dsn` | `--history-dsn` | `LDB_HISTORY_DSN` | `config[.<PROFILE>].yml: history_dsn` |
452
+ | `history_username` | `--history-username` | `LDB_HISTORY_USERNAME` | `config[.<PROFILE>].yml: history_username` |
453
+ | `history_password` | — | — | `secrets[.<PROFILE>].json: history_password` |
454
+ | `admin_source_username` | `--admin-source-username` | `LDB_ADMIN_SOURCE_USERNAME` | `config[.<PROFILE>].yml: admin_source_username` |
455
+ | `admin_source_password` | — | — | `secrets[.<PROFILE>].json: admin_source_password` |
456
+ | `admin_history_username` | `--admin-history-username` | `LDB_ADMIN_HISTORY_USERNAME` | `config[.<PROFILE>].yml: admin_history_username` |
457
+ | `admin_history_password` | — | — | `secrets[.<PROFILE>].json: admin_history_password` |
458
+
459
+ Builder-only flags (`--config-dir`, `--config-file`, `--set`) control how overlays are discovered and have no equivalent entry in the configuration files.
460
+
461
+ ## ILM Rule Semantics
462
+
463
+ Each entry in `ilm[.<PROFILE>].yml` merges table-level attributes with one or more rule conditions defined under `conds`. When `conds` is omitted, LimitsDb assumes a single active rule so the table remains eligible for processing.
464
+
465
+ - **Activation:** A condition runs only when `is_active: true`. Deactivating the sole condition for a table effectively removes that table from the run.
466
+ - **Retention windows:** `retain_months_source` is required whenever `purge_date_expr` is present. `retain_months_history` extends the history window but also depends on defining the source retention. During `HISTORY_ILM` runs, the engine sums source and history months to determine the cut-off date.
467
+ - **Date expressions:** `purge_date_expr` identifies the date column (or expression) that anchors retention. Prefix column references with `@` (for example, `"@DSP_DATE"`); the runner swaps the prefix for the proper table alias at execution time.
468
+ - **Additional filters:** Add optional filters through `additional_filter_expr` (source runs) and `history_addtl_filter_expr` (history runs). Both accept the same `@column` syntax, and the history expression falls back to the source expression when omitted.
469
+ - **Referencing tables:** Use `referencing_tables` to pull parent conditions into child tables. List entries as `<TABLE> <ALIAS>` (optionally `<OWNER>.<TABLE> <ALIAS>`) and supply matching `join_expr` fragments. The runner inherits active conditions from each referenced table and rewrites the join fragments—`@` becomes `inner` joins by default or `left outer` joins when `source_orphan_purge: true` to find orphans.
470
+ - **Historical predicate snapshot:** With `use_added_columns` enabled, every archived row preserves the values needed to reevaluate all its predicates in history, including values sourced from related tables. Distinct cut-off dates are stored in helper columns such as `ldb_date_<suffix>`. Predicates that share a date reuse the same helper column while retaining their own filters and retention windows. Other values sourced from related tables are materialized as `<column>_<table_alias>`.
471
+ - **Orphan handling:** Enable `source_orphan_purge` with `orphan_check_column` to archive source rows whose referenced parent is absent. The source query uses `left outer` joins and treats the row as an orphan when any comma-separated check column is `NULL`. `referencing_tables`, `join_expr`, `orphan_check_column`, `use_added_columns: true`, and a positive historical retention are required. Orphans follow the table's normal source-to-history flow rather than being deleted directly. LimitsDb snapshots this state in `LDB_IS_ORPHAN` and stores a fallback date so `HISTORY_ILM` can apply the configured retention without accessing the missing parent.
472
+
473
+ ## Configuration Resolution (Overlay Order)
474
+
475
+ Lowest → highest priority:
476
+
477
+ 1. System root: `/etc/limitsdb`
478
+ 2. User root: `~/.config/LimitsDb`
479
+ 3. `--config-file` (extra overlay)
480
+ 4. Registered environment variables (`Env("LDB_*")` metadata)
481
+ 5. `--<param> value` or `--set key=value`
482
+
483
+ After overlays, secrets from `secrets[.<PROFILE>].json` are loaded and decrypted. If any secret is plaintext and enforcement is enabled (default), the loader raises an error.
484
+
485
+ Tip: you can redirect both “system” and “user” roots to the same place with `--config-dir`.
486
+
487
+ ## Configuration Files
488
+
489
+ ### `config[.<PROFILE>].yml` (commented defaults + help)
490
+
491
+ Every key is commented; the default value and short help appear inline. Example:
492
+
493
+ ```yaml
494
+ # action: "SOURCE_ILM" # ILM target: months_keep_history_max (SOURCE_ILM) or history (HISTORY_ILM)
495
+ # mode: "PREVIEW" # VALIDATE | PLAN | PREVIEW | SCRIPT | EXECUTE (PREVIEW mirrors legacy DRY_RUN)
496
+ # chunk_size: 100000 # Rows per chunk when processing large tables
497
+ # use_added_columns: True # Populate derived columns in history tables
498
+ # add_ldb_columns: True # Add LimitsDb execution-date columns in history tables
499
+ # parallel_max: 10 # Maximum number of parallel processes
500
+ # db_engine: "oracle" # Database engine
501
+ # log_level: "INFO" # Logging level
502
+ ```
503
+
504
+ `oracle` is the only supported `db_engine`. PostgreSQL remains a future implementation; any other value is rejected.
505
+
506
+ Uncomment and set values as needed. Secrets are not listed here (they live in `secrets[.<PROFILE>].json`).
507
+
508
+ ### `ilm[.<PROFILE>].yml`
509
+
510
+ Empty list scaffolded by `ldb-init`. You define your ILM tables here.
511
+
512
+ ### `ilm[.<PROFILE>].example.yml`
513
+
514
+ Comprehensive, commented example shipped in the package and copied verbatim by `ldb-init`.
515
+
516
+ ### `secrets[.<PROFILE>].json`
517
+
518
+ Holds only secret fields (empty by default). Must be encrypted (run `ldb-crypt`) before the main runner will accept them.
519
+
520
+ ## Environment Variables
521
+
522
+ Only the variables listed in the table above work as overrides; any other `LDB_*` variable is ignored. Examples:
523
+
524
+ - `LDB_PARALLEL_MAX=8` → `parallel_max: 8`
525
+ - `LDB_LOG_LEVEL=DEBUG` → `log_level: "DEBUG"`
526
+
527
+ Booleans accept `true/false`, `1/0`, `on/off` (case-insensitive).
528
+
529
+ `use_added_columns` and `add_ldb_columns` are intentionally file-only because changing the historical table shape
530
+ between runs can destabilize history processing. They cannot be overridden through environment variables or `--set`.
531
+
532
+ ## Troubleshooting
533
+
534
+ - Plaintext secret detected
535
+ Run `ldb-crypt --schema <SCHEMA> [--profile <PROFILE>]`. Or set the value to `""` until you’re ready.
536
+
537
+ - Oracle thick mode required
538
+ Install Oracle Instant Client and configure library paths (`PATH` on Windows, `LD_LIBRARY_PATH` on Linux/macOS).
539
+
540
+ - Configuration not found
541
+ Use `ldb-init` to scaffold files or pass `--config-dir` to point at the correct root.
542
+
543
+ - Unexpected key in config
544
+ The loader is strict—unknown keys raise an error. Fix typos or remove unused entries.
545
+
546
+ - Invalid or unreadable secrets file
547
+ LimitsDb stops before database work and leaves the file unchanged. Repair the JSON or key, then rerun `ldb-crypt`.
548
+
549
+ Low-level configuration, secret, connection, validation, and execution failures are exposed as domain errors while
550
+ preserving their original cause. See [exception handling](docs/exception-handling.md) for the boundary contract and
551
+ broad-catch inventory.
552
+
553
+ ## License
554
+
555
+ LimitsDb is released under the **Apache License 2.0**.
556
+ You are free to use, modify, and redistribute the software — including for commercial purposes — provided that you comply with the terms of that license.
557
+
558
+ All open-source source code of LimitsDb remains free and community-driven.
559
+ However, **Inversiones Etimo SpA** retains the exclusive right to produce, brand, and distribute proprietary or commercial editions of LimitsDb that may include additional features, services, or licensing terms.
560
+
561
+ 📄 See [LICENSE](LICENSE), [LICENSE-DUAL.md](LICENSE-DUAL.md), and [NOTICE](NOTICE) for details.
562
+
563
+ ---
564
+
565
+ ## Governance and Ownership
566
+
567
+ LimitsDb is maintained by **Inversiones Etimo SpA**
568
+ 📧 contacto@etimo.cl
569
+ 🌐 [https://www.etimo.cl](https://www.etimo.cl)
570
+
571
+ ### Project Governance
572
+
573
+ - **Maintainer:** Inversiones Etimo SpA (core architecture, releases, roadmap).
574
+ - **Community contributions:** welcomed via pull requests under the [CONTRIBUTING.md](CONTRIBUTING.md) guidelines.
575
+ - **Issue tracking & discussions:** handled publicly through GitHub Issues and Discussions.
576
+ - **Release model:**
577
+ - `development` → integration branch for new work.
578
+ - `main` → stable, production-ready releases.
579
+ - Periodic tags and changelogs define official versions.
580
+
581
+ ### Ownership
582
+
583
+ All intellectual property and trademarks for LimitsDb are owned by **Inversiones Etimo SpA**.
584
+ Open-source distribution under the Apache 2.0 license does **not** transfer ownership of the software or brand.
585
+ Any proprietary extensions, hosted services, or commercial editions may be offered solely by Inversiones Etimo SpA.
586
+