limitsdb 0.6.0__tar.gz → 0.7.0__tar.gz
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.
- {limitsdb-0.6.0 → limitsdb-0.7.0}/PKG-INFO +86 -16
- {limitsdb-0.6.0 → limitsdb-0.7.0}/README.md +85 -15
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/cli/ldb_impl.py +78 -23
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_ddl.py +51 -28
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_errors.py +8 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_ilm_config.py +6 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_params_config.py +11 -8
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_runner.py +491 -120
- limitsdb-0.7.0/limitsdb/core/ldb_status.py +31 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/db/ldb_engines.py +164 -50
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/db/oracle/ldb_engine_impl.py +679 -336
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/resources/ilm.example.yml +4 -2
- {limitsdb-0.6.0 → limitsdb-0.7.0}/pyproject.toml +1 -1
- limitsdb-0.6.0/limitsdb/core/ldb_status.py +0 -8
- {limitsdb-0.6.0 → limitsdb-0.7.0}/LICENSE +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/LICENSE-DUAL.md +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/NOTICE +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/__init__.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/cli/__init__.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/cli/ldb_crypt.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/cli/ldb_init.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/cli/ldb_run.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/__init__.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_config_loader.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_crypto.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_logger.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_meta.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/core/ldb_utils.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/db/__init__.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/db/ldb_engine_loader.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/db/oracle/__init__.py +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/db/oracle/ldb_conf.sql +0 -0
- {limitsdb-0.6.0 → limitsdb-0.7.0}/limitsdb/resources/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limitsdb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Configuration-driven ILM (Information Lifecycle Management) for production and history databases
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -420,15 +420,23 @@ The script:
|
|
|
420
420
|
|
|
421
421
|
- stops at the first error (`whenever sqlerror exit failure`) and ends with a
|
|
422
422
|
`LimitsDb: OK` banner on success;
|
|
423
|
-
- contains no passwords: it prompts for them (hidden input)
|
|
423
|
+
- contains no passwords: it prompts for all of them (hidden input) at its start,
|
|
424
|
+
before it changes anything;
|
|
424
425
|
- can be rerun: each object is created in a PL/SQL block that ignores only its
|
|
425
426
|
own "already exists" error, and an existing database link is skipped;
|
|
426
|
-
- creates the database link after reconnecting as the runtime user, who owns it
|
|
427
|
-
|
|
427
|
+
- creates the database link after reconnecting as the runtime user, who owns it.
|
|
428
|
+
It asks for that user's password once and reuses it to reconnect. An existing user keeps its password, so the one entered must
|
|
429
|
+
match it; the script says so before it reconnects.
|
|
428
430
|
|
|
429
431
|
Run it in SQL*Plus connected as a user with administrative privileges:
|
|
430
432
|
`sqlplus admin@<dsn> @bootstrap_<schema>_<env>_<timestamp>.sql`.
|
|
431
433
|
|
|
434
|
+
When `ldb-impl` has admin credentials and the runtime user already existed with
|
|
435
|
+
a different password, the reconnection as that user is rejected (ORA-01017).
|
|
436
|
+
`ldb-impl` then logs that the user rejected the configured password, naming the
|
|
437
|
+
user and the database, and exits 1; it never changes the password of an existing
|
|
438
|
+
user.
|
|
439
|
+
|
|
432
440
|
## Run ILM
|
|
433
441
|
|
|
434
442
|
```bash
|
|
@@ -441,7 +449,7 @@ ldb-run --schema <SCHEMA> [--profile <PROFILE>] --action SOURCE_ILM|HISTORY_ILM
|
|
|
441
449
|
Common overrides:
|
|
442
450
|
|
|
443
451
|
```
|
|
444
|
-
--mode VALIDATE|PLAN|PREVIEW|SCRIPT|EXECUTE # VALIDATE setup, PLAN DAG, PREVIEW run without moving data, SCRIPT emit SQL, EXECUTE run
|
|
452
|
+
--mode VALIDATE|PREPARE|PLAN|PREVIEW|SCRIPT|EXECUTE # VALIDATE setup and privileges (read-only), PREPARE grants and objects, PLAN DAG, PREVIEW run without moving data, SCRIPT emit SQL, EXECUTE run
|
|
445
453
|
--chunk-size <int> # Rows per chunk when processing large tables
|
|
446
454
|
--parallel-max <int> # Maximum number of tables processed in parallel
|
|
447
455
|
--ddl-pending-dir <path> # Where failed history DDL is written for a DBA (default: ldb_ddl_pending)
|
|
@@ -471,9 +479,25 @@ application role, and creating or altering history tables before a `PREVIEW` or
|
|
|
471
479
|
`EXECUTE` run (`PREVIEW` needs the objects in place to validate and time the
|
|
472
480
|
queries; it skips only the data movement). Both operations are idempotent.
|
|
473
481
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
482
|
+
A `PREVIEW` writes its own statuses in `ldb_ctl`, the `EXECUTE` ones prefixed with `P` (`PTSTART`, `PCSTART`,
|
|
483
|
+
`PCEND`, `PLSTART`, `PLEND`, `PTEND`, `PERROR`), and finishes each table with `PTEND`, not `TEND`, so it never marks a
|
|
484
|
+
table as processed for the day: an `EXECUTE` run afterwards (same day) still moves the data. A `PREVIEW` is refused
|
|
485
|
+
(`ORA-20006`) over a table that has an `EXECUTE` status for the same process date. An `EXECUTE` continues an incomplete
|
|
486
|
+
`EXECUTE` of the same process date (finished tables stay finished and the rows already moved are kept); in any other case
|
|
487
|
+
the table starts over and the abandoned run is closed in `ldb_log` as "Incomplete process closed by a later process".
|
|
488
|
+
|
|
489
|
+
Only one `ldb-run` can work on a table at a time: the runner takes a lock per table in `LDB_RUN_LOCK` and a second runner
|
|
490
|
+
stops with exit code 1 before starting anything. After upgrading, rerun `ldb-impl` so the new table and the supporting
|
|
491
|
+
procedures (`check_save_status`, `check_referencing_tables`) are installed.
|
|
492
|
+
|
|
493
|
+
The generated `SCRIPT` takes the mode from SQL*Plus and runs without the runner lock:
|
|
494
|
+
`sqlplus user/password@dsn @script EXECUTE` (or `PREVIEW`). Its process date is the `ldb_process_date` variable.
|
|
495
|
+
|
|
496
|
+
If source admin credentials are absent from `secrets[.<PROFILE>].json`, the
|
|
497
|
+
source privilege grants are not applied. `ldb-run` checks with the runtime
|
|
498
|
+
connection which privileges are missing through the role and logs a warning
|
|
499
|
+
naming them; a table the runtime user cannot see is skipped with a warning.
|
|
500
|
+
Run `--mode PREPARE` to get the missing grants as a file for a DBA.
|
|
477
501
|
|
|
478
502
|
History table management follows the same principle:
|
|
479
503
|
|
|
@@ -486,7 +510,7 @@ History table management follows the same principle:
|
|
|
486
510
|
DBA pre-creates all history objects and admin credentials are not stored.
|
|
487
511
|
- **Admin credentials absent, history schema cannot be inspected** (for example
|
|
488
512
|
the runtime connection or dictionary query fails): `ldb-run` exits with code 1
|
|
489
|
-
and
|
|
513
|
+
and tells the operator to run `ldb-impl` so a DBA completes the configuration. It does not run ILM against a history schema whose state is
|
|
490
514
|
unknown.
|
|
491
515
|
- **Admin credentials absent, DDL changes are required**: `ldb-run` applies the
|
|
492
516
|
DDL directly over the runtime history connection, with no prior privilege
|
|
@@ -504,9 +528,46 @@ History table management follows the same principle:
|
|
|
504
528
|
If the file cannot be written (for example the directory is not writable),
|
|
505
529
|
`ldb-run` logs the error with the failed statements and exits with code 1.
|
|
506
530
|
|
|
531
|
+
### Preparing grants and objects with `--mode PREPARE`
|
|
532
|
+
|
|
533
|
+
`--mode PREPARE` brings permissions and objects into place without running any
|
|
534
|
+
ILM work, so the problems show up before `SCRIPT`, `PREVIEW` or `EXECUTE`. It
|
|
535
|
+
is safe to rerun and meant to run first (`VALIDATE`, `PREPARE`, then the rest):
|
|
536
|
+
|
|
537
|
+
- With source admin credentials (`SOURCE_ILM`) it grants the source tables to
|
|
538
|
+
the source role. Without them it writes `source_grants_<schema>_<ts>.sql` under
|
|
539
|
+
`ddl_pending_dir` for a DBA, with only the privileges the runtime user does not
|
|
540
|
+
receive yet through the role, and warns. Once the grants are in place no file
|
|
541
|
+
is written.
|
|
542
|
+
- It checks, with the runtime connections alone, that the source and history
|
|
543
|
+
roles are enabled for the runtime users and give them `SELECT`, `INSERT`,
|
|
544
|
+
`UPDATE` and `DELETE` on every configured table. A role that is granted but
|
|
545
|
+
not a default role gives nothing.
|
|
546
|
+
The role must stay a default role of the runtime user (`ldb-run` warns
|
|
547
|
+
when it is not); `GRANT role` makes it default unless the user was altered.
|
|
548
|
+
- The default role names are `LDB_SOURCE_ROLE` and `LDB_HISTORY_ROLE`. When
|
|
549
|
+
several schemas (configuration sets) share the same SOURCE or HISTORY
|
|
550
|
+
database, those names collide; set `source_role_name` / `history_role_name`
|
|
551
|
+
per schema to keep the roles apart. Reusing one role across schemas also
|
|
552
|
+
works and is rarely a problem.
|
|
553
|
+
- It creates or alters the history tables and grants them to the history role,
|
|
554
|
+
with the same admin or runtime-connection rules as `EXECUTE`: statements that
|
|
555
|
+
fail go to the pending file.
|
|
556
|
+
- It exits with code 1 if the runtime user still cannot see a configured table
|
|
557
|
+
(it does not exist or `SELECT` is not granted). Other modes only skip such a
|
|
558
|
+
table with a warning.
|
|
559
|
+
|
|
560
|
+
`VALIDATE` runs the same privilege check without changing anything: a gap on
|
|
561
|
+
the action's own environment fails it, a gap on the other environment is a
|
|
562
|
+
warning, since `PREPARE` creates the history objects. `SCRIPT`, `PREVIEW` and
|
|
563
|
+
`EXECUTE` warn about the gaps they find.
|
|
564
|
+
|
|
565
|
+
`PREVIEW` and `EXECUTE` keep applying DDL and grants themselves, so a change in
|
|
566
|
+
source reaches history without failing the run.
|
|
567
|
+
|
|
507
568
|
### History DDL in `--mode SCRIPT`
|
|
508
569
|
|
|
509
|
-
`--mode SCRIPT` applies
|
|
570
|
+
`--mode SCRIPT` applies no DDL and no history grants to the databases. The ILM SQL blocks, which
|
|
510
571
|
run many times, go to stdout. The one-time statements go to files under
|
|
511
572
|
`ddl_pending_dir`, so they stay out of the script that is rerun:
|
|
512
573
|
|
|
@@ -516,7 +577,14 @@ run many times, go to stdout. The one-time statements go to files under
|
|
|
516
577
|
- `source_grants_<schema>_<YYYYMMDD_HHMMSS>.sql` (`SOURCE_ILM` only): the grants
|
|
517
578
|
on the source tables for the source role. Run it against the source database.
|
|
518
579
|
|
|
519
|
-
|
|
580
|
+
The runtime user reads the column metadata of the source tables through their
|
|
581
|
+
grants, so when source admin credentials exist `SCRIPT` applies the source
|
|
582
|
+
grants itself and writes no `source_grants` file. Without them the file is
|
|
583
|
+
written and `ldb-run` logs a warning. In every mode, a table the runtime user
|
|
584
|
+
cannot see (it does not exist, or `SELECT` is not granted) is skipped with a
|
|
585
|
+
warning instead of failing the run, and so is a table that references it.
|
|
586
|
+
|
|
587
|
+
Both files are plain SQL for SQL*Plus that stop at the first failing statement; the
|
|
520
588
|
grants are unconditional and safe to run again. The history DDL is always
|
|
521
589
|
emitted, computed like this:
|
|
522
590
|
|
|
@@ -538,7 +606,7 @@ the same pending file as the failed DDL, so a DBA can run it.
|
|
|
538
606
|
| Parameter | Purpose | Default / values |
|
|
539
607
|
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
540
608
|
| `action` | ILM target (`SOURCE_ILM` moves/purges source, `HISTORY_ILM` cleans downstream history). | Default `SOURCE_ILM`; choices `SOURCE_ILM`, `HISTORY_ILM`. |
|
|
541
|
-
| `mode` | Execution mode: `VALIDATE` config
|
|
609
|
+
| `mode` | Execution mode: `VALIDATE` config, credentials and role privileges (read-only), `PREPARE` apply grants and history objects without running ILM, `PLAN` dependency order, `PREVIEW` run the real queries and apply DDL and grants but do not insert or delete data (a.k.a. legacy `DRY_RUN`), `SCRIPT` emit the history DDL and the ILM SQL without applying anything, `EXECUTE` run changes. | Default `PREVIEW`; choices `VALIDATE`, `PLAN`, `PREVIEW`, `SCRIPT`, `EXECUTE`. |
|
|
542
610
|
| `chunk_size` | Rows per chunk when processing large tables. | Default `100000`. |
|
|
543
611
|
| `use_added_columns` | Populate derived columns in history tables. | Default `True` (boolean toggle). |
|
|
544
612
|
| `add_ldb_columns` | Add LimitsDb execution-date columns in history tables. | Default `True` (boolean toggle). |
|
|
@@ -551,10 +619,10 @@ the same pending file as the failed DDL, so a DBA can run it.
|
|
|
551
619
|
| `profile` | Environment name (e.g., `dev`, `prod`). Differentiates configuration files for different environments within the same configuration set. | Optional; default `null`. |
|
|
552
620
|
| `ilm_config_file` | YAML file with tables (bypass DB discovery). | Optional; default `null`. |
|
|
553
621
|
| `source_dsn` | Source DSN / connection descriptor. | Default empty string. |
|
|
554
|
-
| `source_username` | Source username.
|
|
622
|
+
| `source_username` | Source username, as named in the data dictionary: lowercase (`ldb`) is a quoted user created as `"ldb"`, uppercase (`LDB`) a regular one. | | Default empty string. |
|
|
555
623
|
| `source_password` | Source password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
|
|
556
624
|
| `history_dsn` | History DSN / connection descriptor. | Default empty string. |
|
|
557
|
-
| `history_username` | History username
|
|
625
|
+
| `history_username` | History username, with the same case rules as `source_username`. || Default empty string. |
|
|
558
626
|
| `history_password` | History password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
|
|
559
627
|
| `admin_source_username` | Admin source username. | Default empty string. |
|
|
560
628
|
| `admin_source_password` | Admin source password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
|
|
@@ -599,7 +667,9 @@ Each entry in `ilm[.<PROFILE>].yml` merges table-level attributes with one or mo
|
|
|
599
667
|
- **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.
|
|
600
668
|
- **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.
|
|
601
669
|
- **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.
|
|
602
|
-
- **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
|
|
670
|
+
- **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 the active conditions of each referenced table: the conditions of one table are alternatives (`or`), and a child row is purged only when its own conditions and those of every referenced table are met (`and` between tables). It rewrites the join fragments—`@` becomes `inner` joins by default or `left outer` joins when `source_orphan_purge: true` to find orphans.
|
|
671
|
+
- **Join owners in history:** `join_expr` names each referenced table by its source owner. In `HISTORY_ILM` runs with `use_added_columns: false` the runner joins the history copy instead, replacing `<source_owner>.<table>` with `<history_owner>.<table>` for every table listed in `referencing_tables`. With `use_added_columns: true` history runs do not join.
|
|
672
|
+
- **Extra tables:** Filters may contain subqueries over tables that ILM does not manage. List them in `extra_select_tables` (read by `additional_filter_expr`, so source runs) and `history_extra_select_tables` (read by `history_addtl_filter_expr`) as comma-separated `[OWNER.]TABLE` entries; an entry without owner belongs to the environment's own owner. The environment's role gets `SELECT` on them wherever it gets the other table grants (`ldb-run`, `--mode PREPARE`, `--mode SCRIPT`) and the privilege checks include them. In history, `history_extra_select_tables` falls back to `extra_select_tables`, just as `history_addtl_filter_expr` falls back to `additional_filter_expr`. A table also managed by ILM keeps its full privileges. For tables that are not in the history database write the access in `history_addtl_filter_expr` yourself (for example `TABLE@SRC`, or `TABLE UNION ALL TABLE@SRC` in a subquery); LimitsDb does not rewrite subqueries. `SRC` is only the default name of the history-to-source database link; it can be overridden with `history_to_source_dblink_name` (and `source_to_history_dblink_name`, default `HIST`, for the other direction). The link connects as the source runtime user and uses the grants of its source role, so a table read as `TABLE@SRC` must also be listed in `extra_select_tables`.
|
|
603
673
|
- **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>`.
|
|
604
674
|
- **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.
|
|
605
675
|
|
|
@@ -625,7 +695,7 @@ Every key is commented; the default value and short help appear inline. Example:
|
|
|
625
695
|
|
|
626
696
|
```yaml
|
|
627
697
|
# action: "SOURCE_ILM" # ILM target: months_keep_history_max (SOURCE_ILM) or history (HISTORY_ILM)
|
|
628
|
-
# mode: "PREVIEW" # VALIDATE | PLAN | PREVIEW | SCRIPT | EXECUTE
|
|
698
|
+
# mode: "PREVIEW" # VALIDATE | PREPARE | PLAN | PREVIEW | SCRIPT | EXECUTE
|
|
629
699
|
# chunk_size: 100000 # Rows per chunk when processing large tables
|
|
630
700
|
# use_added_columns: True # Populate derived columns in history tables
|
|
631
701
|
# add_ldb_columns: True # Add LimitsDb execution-date columns in history tables
|
|
@@ -274,15 +274,23 @@ The script:
|
|
|
274
274
|
|
|
275
275
|
- stops at the first error (`whenever sqlerror exit failure`) and ends with a
|
|
276
276
|
`LimitsDb: OK` banner on success;
|
|
277
|
-
- contains no passwords: it prompts for them (hidden input)
|
|
277
|
+
- contains no passwords: it prompts for all of them (hidden input) at its start,
|
|
278
|
+
before it changes anything;
|
|
278
279
|
- can be rerun: each object is created in a PL/SQL block that ignores only its
|
|
279
280
|
own "already exists" error, and an existing database link is skipped;
|
|
280
|
-
- creates the database link after reconnecting as the runtime user, who owns it
|
|
281
|
-
|
|
281
|
+
- creates the database link after reconnecting as the runtime user, who owns it.
|
|
282
|
+
It asks for that user's password once and reuses it to reconnect. An existing user keeps its password, so the one entered must
|
|
283
|
+
match it; the script says so before it reconnects.
|
|
282
284
|
|
|
283
285
|
Run it in SQL*Plus connected as a user with administrative privileges:
|
|
284
286
|
`sqlplus admin@<dsn> @bootstrap_<schema>_<env>_<timestamp>.sql`.
|
|
285
287
|
|
|
288
|
+
When `ldb-impl` has admin credentials and the runtime user already existed with
|
|
289
|
+
a different password, the reconnection as that user is rejected (ORA-01017).
|
|
290
|
+
`ldb-impl` then logs that the user rejected the configured password, naming the
|
|
291
|
+
user and the database, and exits 1; it never changes the password of an existing
|
|
292
|
+
user.
|
|
293
|
+
|
|
286
294
|
## Run ILM
|
|
287
295
|
|
|
288
296
|
```bash
|
|
@@ -295,7 +303,7 @@ ldb-run --schema <SCHEMA> [--profile <PROFILE>] --action SOURCE_ILM|HISTORY_ILM
|
|
|
295
303
|
Common overrides:
|
|
296
304
|
|
|
297
305
|
```
|
|
298
|
-
--mode VALIDATE|PLAN|PREVIEW|SCRIPT|EXECUTE # VALIDATE setup, PLAN DAG, PREVIEW run without moving data, SCRIPT emit SQL, EXECUTE run
|
|
306
|
+
--mode VALIDATE|PREPARE|PLAN|PREVIEW|SCRIPT|EXECUTE # VALIDATE setup and privileges (read-only), PREPARE grants and objects, PLAN DAG, PREVIEW run without moving data, SCRIPT emit SQL, EXECUTE run
|
|
299
307
|
--chunk-size <int> # Rows per chunk when processing large tables
|
|
300
308
|
--parallel-max <int> # Maximum number of tables processed in parallel
|
|
301
309
|
--ddl-pending-dir <path> # Where failed history DDL is written for a DBA (default: ldb_ddl_pending)
|
|
@@ -325,9 +333,25 @@ application role, and creating or altering history tables before a `PREVIEW` or
|
|
|
325
333
|
`EXECUTE` run (`PREVIEW` needs the objects in place to validate and time the
|
|
326
334
|
queries; it skips only the data movement). Both operations are idempotent.
|
|
327
335
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
336
|
+
A `PREVIEW` writes its own statuses in `ldb_ctl`, the `EXECUTE` ones prefixed with `P` (`PTSTART`, `PCSTART`,
|
|
337
|
+
`PCEND`, `PLSTART`, `PLEND`, `PTEND`, `PERROR`), and finishes each table with `PTEND`, not `TEND`, so it never marks a
|
|
338
|
+
table as processed for the day: an `EXECUTE` run afterwards (same day) still moves the data. A `PREVIEW` is refused
|
|
339
|
+
(`ORA-20006`) over a table that has an `EXECUTE` status for the same process date. An `EXECUTE` continues an incomplete
|
|
340
|
+
`EXECUTE` of the same process date (finished tables stay finished and the rows already moved are kept); in any other case
|
|
341
|
+
the table starts over and the abandoned run is closed in `ldb_log` as "Incomplete process closed by a later process".
|
|
342
|
+
|
|
343
|
+
Only one `ldb-run` can work on a table at a time: the runner takes a lock per table in `LDB_RUN_LOCK` and a second runner
|
|
344
|
+
stops with exit code 1 before starting anything. After upgrading, rerun `ldb-impl` so the new table and the supporting
|
|
345
|
+
procedures (`check_save_status`, `check_referencing_tables`) are installed.
|
|
346
|
+
|
|
347
|
+
The generated `SCRIPT` takes the mode from SQL*Plus and runs without the runner lock:
|
|
348
|
+
`sqlplus user/password@dsn @script EXECUTE` (or `PREVIEW`). Its process date is the `ldb_process_date` variable.
|
|
349
|
+
|
|
350
|
+
If source admin credentials are absent from `secrets[.<PROFILE>].json`, the
|
|
351
|
+
source privilege grants are not applied. `ldb-run` checks with the runtime
|
|
352
|
+
connection which privileges are missing through the role and logs a warning
|
|
353
|
+
naming them; a table the runtime user cannot see is skipped with a warning.
|
|
354
|
+
Run `--mode PREPARE` to get the missing grants as a file for a DBA.
|
|
331
355
|
|
|
332
356
|
History table management follows the same principle:
|
|
333
357
|
|
|
@@ -340,7 +364,7 @@ History table management follows the same principle:
|
|
|
340
364
|
DBA pre-creates all history objects and admin credentials are not stored.
|
|
341
365
|
- **Admin credentials absent, history schema cannot be inspected** (for example
|
|
342
366
|
the runtime connection or dictionary query fails): `ldb-run` exits with code 1
|
|
343
|
-
and
|
|
367
|
+
and tells the operator to run `ldb-impl` so a DBA completes the configuration. It does not run ILM against a history schema whose state is
|
|
344
368
|
unknown.
|
|
345
369
|
- **Admin credentials absent, DDL changes are required**: `ldb-run` applies the
|
|
346
370
|
DDL directly over the runtime history connection, with no prior privilege
|
|
@@ -358,9 +382,46 @@ History table management follows the same principle:
|
|
|
358
382
|
If the file cannot be written (for example the directory is not writable),
|
|
359
383
|
`ldb-run` logs the error with the failed statements and exits with code 1.
|
|
360
384
|
|
|
385
|
+
### Preparing grants and objects with `--mode PREPARE`
|
|
386
|
+
|
|
387
|
+
`--mode PREPARE` brings permissions and objects into place without running any
|
|
388
|
+
ILM work, so the problems show up before `SCRIPT`, `PREVIEW` or `EXECUTE`. It
|
|
389
|
+
is safe to rerun and meant to run first (`VALIDATE`, `PREPARE`, then the rest):
|
|
390
|
+
|
|
391
|
+
- With source admin credentials (`SOURCE_ILM`) it grants the source tables to
|
|
392
|
+
the source role. Without them it writes `source_grants_<schema>_<ts>.sql` under
|
|
393
|
+
`ddl_pending_dir` for a DBA, with only the privileges the runtime user does not
|
|
394
|
+
receive yet through the role, and warns. Once the grants are in place no file
|
|
395
|
+
is written.
|
|
396
|
+
- It checks, with the runtime connections alone, that the source and history
|
|
397
|
+
roles are enabled for the runtime users and give them `SELECT`, `INSERT`,
|
|
398
|
+
`UPDATE` and `DELETE` on every configured table. A role that is granted but
|
|
399
|
+
not a default role gives nothing.
|
|
400
|
+
The role must stay a default role of the runtime user (`ldb-run` warns
|
|
401
|
+
when it is not); `GRANT role` makes it default unless the user was altered.
|
|
402
|
+
- The default role names are `LDB_SOURCE_ROLE` and `LDB_HISTORY_ROLE`. When
|
|
403
|
+
several schemas (configuration sets) share the same SOURCE or HISTORY
|
|
404
|
+
database, those names collide; set `source_role_name` / `history_role_name`
|
|
405
|
+
per schema to keep the roles apart. Reusing one role across schemas also
|
|
406
|
+
works and is rarely a problem.
|
|
407
|
+
- It creates or alters the history tables and grants them to the history role,
|
|
408
|
+
with the same admin or runtime-connection rules as `EXECUTE`: statements that
|
|
409
|
+
fail go to the pending file.
|
|
410
|
+
- It exits with code 1 if the runtime user still cannot see a configured table
|
|
411
|
+
(it does not exist or `SELECT` is not granted). Other modes only skip such a
|
|
412
|
+
table with a warning.
|
|
413
|
+
|
|
414
|
+
`VALIDATE` runs the same privilege check without changing anything: a gap on
|
|
415
|
+
the action's own environment fails it, a gap on the other environment is a
|
|
416
|
+
warning, since `PREPARE` creates the history objects. `SCRIPT`, `PREVIEW` and
|
|
417
|
+
`EXECUTE` warn about the gaps they find.
|
|
418
|
+
|
|
419
|
+
`PREVIEW` and `EXECUTE` keep applying DDL and grants themselves, so a change in
|
|
420
|
+
source reaches history without failing the run.
|
|
421
|
+
|
|
361
422
|
### History DDL in `--mode SCRIPT`
|
|
362
423
|
|
|
363
|
-
`--mode SCRIPT` applies
|
|
424
|
+
`--mode SCRIPT` applies no DDL and no history grants to the databases. The ILM SQL blocks, which
|
|
364
425
|
run many times, go to stdout. The one-time statements go to files under
|
|
365
426
|
`ddl_pending_dir`, so they stay out of the script that is rerun:
|
|
366
427
|
|
|
@@ -370,7 +431,14 @@ run many times, go to stdout. The one-time statements go to files under
|
|
|
370
431
|
- `source_grants_<schema>_<YYYYMMDD_HHMMSS>.sql` (`SOURCE_ILM` only): the grants
|
|
371
432
|
on the source tables for the source role. Run it against the source database.
|
|
372
433
|
|
|
373
|
-
|
|
434
|
+
The runtime user reads the column metadata of the source tables through their
|
|
435
|
+
grants, so when source admin credentials exist `SCRIPT` applies the source
|
|
436
|
+
grants itself and writes no `source_grants` file. Without them the file is
|
|
437
|
+
written and `ldb-run` logs a warning. In every mode, a table the runtime user
|
|
438
|
+
cannot see (it does not exist, or `SELECT` is not granted) is skipped with a
|
|
439
|
+
warning instead of failing the run, and so is a table that references it.
|
|
440
|
+
|
|
441
|
+
Both files are plain SQL for SQL*Plus that stop at the first failing statement; the
|
|
374
442
|
grants are unconditional and safe to run again. The history DDL is always
|
|
375
443
|
emitted, computed like this:
|
|
376
444
|
|
|
@@ -392,7 +460,7 @@ the same pending file as the failed DDL, so a DBA can run it.
|
|
|
392
460
|
| Parameter | Purpose | Default / values |
|
|
393
461
|
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
394
462
|
| `action` | ILM target (`SOURCE_ILM` moves/purges source, `HISTORY_ILM` cleans downstream history). | Default `SOURCE_ILM`; choices `SOURCE_ILM`, `HISTORY_ILM`. |
|
|
395
|
-
| `mode` | Execution mode: `VALIDATE` config
|
|
463
|
+
| `mode` | Execution mode: `VALIDATE` config, credentials and role privileges (read-only), `PREPARE` apply grants and history objects without running ILM, `PLAN` dependency order, `PREVIEW` run the real queries and apply DDL and grants but do not insert or delete data (a.k.a. legacy `DRY_RUN`), `SCRIPT` emit the history DDL and the ILM SQL without applying anything, `EXECUTE` run changes. | Default `PREVIEW`; choices `VALIDATE`, `PLAN`, `PREVIEW`, `SCRIPT`, `EXECUTE`. |
|
|
396
464
|
| `chunk_size` | Rows per chunk when processing large tables. | Default `100000`. |
|
|
397
465
|
| `use_added_columns` | Populate derived columns in history tables. | Default `True` (boolean toggle). |
|
|
398
466
|
| `add_ldb_columns` | Add LimitsDb execution-date columns in history tables. | Default `True` (boolean toggle). |
|
|
@@ -405,10 +473,10 @@ the same pending file as the failed DDL, so a DBA can run it.
|
|
|
405
473
|
| `profile` | Environment name (e.g., `dev`, `prod`). Differentiates configuration files for different environments within the same configuration set. | Optional; default `null`. |
|
|
406
474
|
| `ilm_config_file` | YAML file with tables (bypass DB discovery). | Optional; default `null`. |
|
|
407
475
|
| `source_dsn` | Source DSN / connection descriptor. | Default empty string. |
|
|
408
|
-
| `source_username` | Source username.
|
|
476
|
+
| `source_username` | Source username, as named in the data dictionary: lowercase (`ldb`) is a quoted user created as `"ldb"`, uppercase (`LDB`) a regular one. | | Default empty string. |
|
|
409
477
|
| `source_password` | Source password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
|
|
410
478
|
| `history_dsn` | History DSN / connection descriptor. | Default empty string. |
|
|
411
|
-
| `history_username` | History username
|
|
479
|
+
| `history_username` | History username, with the same case rules as `source_username`. || Default empty string. |
|
|
412
480
|
| `history_password` | History password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
|
|
413
481
|
| `admin_source_username` | Admin source username. | Default empty string. |
|
|
414
482
|
| `admin_source_password` | Admin source password stored in secrets. | Default empty string; encrypted in `secrets.<PROFILE>.json`. |
|
|
@@ -453,7 +521,9 @@ Each entry in `ilm[.<PROFILE>].yml` merges table-level attributes with one or mo
|
|
|
453
521
|
- **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.
|
|
454
522
|
- **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.
|
|
455
523
|
- **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.
|
|
456
|
-
- **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
|
|
524
|
+
- **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 the active conditions of each referenced table: the conditions of one table are alternatives (`or`), and a child row is purged only when its own conditions and those of every referenced table are met (`and` between tables). It rewrites the join fragments—`@` becomes `inner` joins by default or `left outer` joins when `source_orphan_purge: true` to find orphans.
|
|
525
|
+
- **Join owners in history:** `join_expr` names each referenced table by its source owner. In `HISTORY_ILM` runs with `use_added_columns: false` the runner joins the history copy instead, replacing `<source_owner>.<table>` with `<history_owner>.<table>` for every table listed in `referencing_tables`. With `use_added_columns: true` history runs do not join.
|
|
526
|
+
- **Extra tables:** Filters may contain subqueries over tables that ILM does not manage. List them in `extra_select_tables` (read by `additional_filter_expr`, so source runs) and `history_extra_select_tables` (read by `history_addtl_filter_expr`) as comma-separated `[OWNER.]TABLE` entries; an entry without owner belongs to the environment's own owner. The environment's role gets `SELECT` on them wherever it gets the other table grants (`ldb-run`, `--mode PREPARE`, `--mode SCRIPT`) and the privilege checks include them. In history, `history_extra_select_tables` falls back to `extra_select_tables`, just as `history_addtl_filter_expr` falls back to `additional_filter_expr`. A table also managed by ILM keeps its full privileges. For tables that are not in the history database write the access in `history_addtl_filter_expr` yourself (for example `TABLE@SRC`, or `TABLE UNION ALL TABLE@SRC` in a subquery); LimitsDb does not rewrite subqueries. `SRC` is only the default name of the history-to-source database link; it can be overridden with `history_to_source_dblink_name` (and `source_to_history_dblink_name`, default `HIST`, for the other direction). The link connects as the source runtime user and uses the grants of its source role, so a table read as `TABLE@SRC` must also be listed in `extra_select_tables`.
|
|
457
527
|
- **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>`.
|
|
458
528
|
- **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.
|
|
459
529
|
|
|
@@ -479,7 +549,7 @@ Every key is commented; the default value and short help appear inline. Example:
|
|
|
479
549
|
|
|
480
550
|
```yaml
|
|
481
551
|
# action: "SOURCE_ILM" # ILM target: months_keep_history_max (SOURCE_ILM) or history (HISTORY_ILM)
|
|
482
|
-
# mode: "PREVIEW" # VALIDATE | PLAN | PREVIEW | SCRIPT | EXECUTE
|
|
552
|
+
# mode: "PREVIEW" # VALIDATE | PREPARE | PLAN | PREVIEW | SCRIPT | EXECUTE
|
|
483
553
|
# chunk_size: 100000 # Rows per chunk when processing large tables
|
|
484
554
|
# use_added_columns: True # Populate derived columns in history tables
|
|
485
555
|
# add_ldb_columns: True # Add LimitsDb execution-date columns in history tables
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
from collections.abc import Callable
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
9
|
+
from typing import Any, Literal
|
|
9
10
|
|
|
10
11
|
from limitsdb.core.ldb_crypto import load_or_create_key
|
|
11
|
-
from limitsdb.core.
|
|
12
|
+
from limitsdb.core.ldb_ddl import render_script
|
|
13
|
+
from limitsdb.core.ldb_errors import InvalidCredentialsError, LimitsDbError
|
|
12
14
|
from limitsdb.core.ldb_logger import configure_logger, get_logger, reconfigure_logger
|
|
13
15
|
from limitsdb.core.ldb_params_config import Config, build_config
|
|
14
16
|
from limitsdb.core.ldb_utils import encrypt_secrets_in_place
|
|
@@ -16,6 +18,7 @@ from limitsdb.db.ldb_engine_loader import get_db_engine
|
|
|
16
18
|
from limitsdb.db.ldb_engines import (
|
|
17
19
|
ColumnDefinition,
|
|
18
20
|
DatabaseLinkDefinition,
|
|
21
|
+
DdlSink,
|
|
19
22
|
RoleDefinition,
|
|
20
23
|
SequenceDefinition,
|
|
21
24
|
TableDefinition,
|
|
@@ -60,6 +63,7 @@ def _build_control_tables(owner: str) -> list[TableDefinition]:
|
|
|
60
63
|
ColumnDefinition("CTL_PROCESS_END", "date"),
|
|
61
64
|
ColumnDefinition("CTL_ROWS_PROCESSED", "number", precision=18, scale=0),
|
|
62
65
|
ColumnDefinition("CTL_PLSQL", "clob"),
|
|
66
|
+
ColumnDefinition("CTL_RUN_ID", "string", length=36),
|
|
63
67
|
),
|
|
64
68
|
primary_key=("CTL_OWNER", "CTL_TABLE_NAME"),
|
|
65
69
|
),
|
|
@@ -81,6 +85,22 @@ def _build_control_tables(owner: str) -> list[TableDefinition]:
|
|
|
81
85
|
),
|
|
82
86
|
primary_key=("LOG_ID",),
|
|
83
87
|
),
|
|
88
|
+
# Runner mutex: the 'L' row is locked by the live runner; the 'K' row publishes its run_id
|
|
89
|
+
TableDefinition(
|
|
90
|
+
owner=owner,
|
|
91
|
+
name="LDB_RUN_LOCK",
|
|
92
|
+
columns=(
|
|
93
|
+
ColumnDefinition("RL_OWNER", "string", length=50, nullable=False),
|
|
94
|
+
ColumnDefinition("RL_TABLE_NAME", "string", length=50, nullable=False),
|
|
95
|
+
ColumnDefinition("RL_KIND", "string", length=1, nullable=False),
|
|
96
|
+
ColumnDefinition("RL_RUN_ID", "string", length=36),
|
|
97
|
+
ColumnDefinition("RL_MODE", "string", length=10),
|
|
98
|
+
ColumnDefinition("RL_HOST", "string", length=255),
|
|
99
|
+
ColumnDefinition("RL_PID", "number", precision=18, scale=0),
|
|
100
|
+
ColumnDefinition("RL_START", "date"),
|
|
101
|
+
),
|
|
102
|
+
primary_key=("RL_OWNER", "RL_TABLE_NAME", "RL_KIND"),
|
|
103
|
+
),
|
|
84
104
|
]
|
|
85
105
|
|
|
86
106
|
|
|
@@ -164,13 +184,15 @@ def run_cli() -> None:
|
|
|
164
184
|
source_has_admin = bool(administration.source.username and administration.source.password)
|
|
165
185
|
history_has_admin = bool(administration.history.username and administration.history.password)
|
|
166
186
|
|
|
167
|
-
def write_script(env: str,
|
|
168
|
-
|
|
187
|
+
def write_script(env: str, steps: Callable[[DdlSink], None]) -> None:
|
|
188
|
+
fragments: list[str] = []
|
|
189
|
+
steps(fragments.append)
|
|
190
|
+
title = f"{env} database of schema {config.schema}"
|
|
169
191
|
stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
170
192
|
path = Path(config.execution.ddl_pending_dir) / f"bootstrap_{config.schema}_{env.lower()}_{stamp}.sql"
|
|
171
193
|
try:
|
|
172
194
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
173
|
-
path.write_text(
|
|
195
|
+
path.write_text(render_script(title, fragments), encoding="utf-8")
|
|
174
196
|
except OSError as exc:
|
|
175
197
|
logger.error(
|
|
176
198
|
"Admin credentials for %s are absent and the script could not be written to %s: %s", env, path, exc
|
|
@@ -178,29 +200,48 @@ def run_cli() -> None:
|
|
|
178
200
|
raise SystemExit(1) from exc
|
|
179
201
|
script_files.append(path)
|
|
180
202
|
|
|
203
|
+
def bootstrap_steps(
|
|
204
|
+
roles: list[RoleDefinition],
|
|
205
|
+
user: UserDefinition,
|
|
206
|
+
tables: list[TableDefinition],
|
|
207
|
+
sequences: list[SequenceDefinition],
|
|
208
|
+
link: DatabaseLinkDefinition,
|
|
209
|
+
runtime: tuple[str, str],
|
|
210
|
+
) -> Callable[[DdlSink], None]:
|
|
211
|
+
def steps(sink: DdlSink) -> None:
|
|
212
|
+
engine.script_password_prompts([user], [link], ddl_sink=sink)
|
|
213
|
+
engine.ensure_roles(None, roles, ddl_sink=sink)
|
|
214
|
+
engine.ensure_users(None, [user], ddl_sink=sink)
|
|
215
|
+
engine.ensure_tables(None, tables, ddl_sink=sink)
|
|
216
|
+
engine.ensure_sequences(None, sequences, ddl_sink=sink)
|
|
217
|
+
engine.ensure_supporting_objects(None, runtime[0], ddl_sink=sink)
|
|
218
|
+
engine.ensure_database_links(None, [link], ddl_sink=sink, connect_as=runtime)
|
|
219
|
+
|
|
220
|
+
return steps
|
|
221
|
+
|
|
181
222
|
if not source_has_admin:
|
|
182
223
|
write_script(
|
|
183
224
|
"SOURCE",
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
225
|
+
bootstrap_steps(
|
|
226
|
+
source_roles,
|
|
227
|
+
source_user,
|
|
228
|
+
source_tables,
|
|
229
|
+
source_sequences,
|
|
230
|
+
source_db_link,
|
|
231
|
+
(connections.source.username, connections.source.dsn),
|
|
232
|
+
),
|
|
192
233
|
)
|
|
193
234
|
if not history_has_admin:
|
|
194
235
|
write_script(
|
|
195
236
|
"HISTORY",
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
237
|
+
bootstrap_steps(
|
|
238
|
+
history_roles,
|
|
239
|
+
history_user,
|
|
240
|
+
history_tables,
|
|
241
|
+
history_sequences,
|
|
242
|
+
history_db_link,
|
|
243
|
+
(connections.history.username, connections.history.dsn),
|
|
244
|
+
),
|
|
204
245
|
)
|
|
205
246
|
|
|
206
247
|
if source_has_admin:
|
|
@@ -229,15 +270,29 @@ def run_cli() -> None:
|
|
|
229
270
|
finally:
|
|
230
271
|
engine.close_connection(history_admin_conn)
|
|
231
272
|
|
|
273
|
+
def connect_as_runtime(env: Literal["SOURCE", "HISTORY"], username: str) -> Any:
|
|
274
|
+
# The user exists here (ensure_users ran and never changes an existing password), so a rejected
|
|
275
|
+
# login can only be a configured password that differs from the one the user already has.
|
|
276
|
+
try:
|
|
277
|
+
return engine.get_connection(config, admin=False, env=env)
|
|
278
|
+
except InvalidCredentialsError as exc:
|
|
279
|
+
logger.error(
|
|
280
|
+
"The runtime user %s exists in %s but rejected the configured password; ldb-impl does not "
|
|
281
|
+
"change the password of an existing user. Set the configured password to the user's current one.",
|
|
282
|
+
username,
|
|
283
|
+
env,
|
|
284
|
+
)
|
|
285
|
+
raise SystemExit(1) from exc
|
|
286
|
+
|
|
232
287
|
if source_has_admin:
|
|
233
|
-
source_user_conn =
|
|
288
|
+
source_user_conn = connect_as_runtime("SOURCE", connections.source.username)
|
|
234
289
|
try:
|
|
235
290
|
summary["source_database_links"] = engine.ensure_database_links(source_user_conn, [source_db_link])
|
|
236
291
|
finally:
|
|
237
292
|
engine.close_connection(source_user_conn)
|
|
238
293
|
|
|
239
294
|
if history_has_admin:
|
|
240
|
-
history_user_conn =
|
|
295
|
+
history_user_conn = connect_as_runtime("HISTORY", connections.history.username)
|
|
241
296
|
try:
|
|
242
297
|
summary["history_database_links"] = engine.ensure_database_links(history_user_conn, [history_db_link])
|
|
243
298
|
finally:
|