Pulling schema changes into the model

bluep pull brings the schema of a source into a model's physical target and, by default, into the logical layer too. The source is a live database (read through its command-line client), a DDL script, or a target of another model directory. Nothing is written until you accept a plan.

Workflow

  1. Produce a plan:

    bluep pull my-model --target pg --from-db --database shop --plan plan.json
    

    or compare with another model directory or a DDL script:

    bluep pull my-model --target pg --from-dir ../other-model --plan plan.json
    bluep pull my-model --target pg --from-ddl schema.sql --plan plan.json
    

    Without --plan the plan is printed with markers and the command exits 1 when there is anything to pull:

    [x] + column public.payment.refund_at
    [ ] - column public.order_line.qty
    [x] + column public.order_line.quantity
    [?] ? rename public.order_line.qty -> public.order_line.quantity (same type and nullability)
    plan: 3 changes (1 destructive), 1 rename candidates
    

    [x] items are applied, [ ] items are listed but skipped, [?] lines are rename candidates.

  2. Review the plan file. Every item has "apply": true|false; flip the ones you want. Items marked "destructive": true (drops and type changes) start as false unless you pass --allow-destructive.

  3. Apply it:

    bluep pull my-model --apply plan.json
    

    The output names the files that changed:

    applied 3, skipped 1; changed files: model.bluep
    

    --yes performs both steps at once.

What applying does

Changes are applied to the named target and then propagated to entities, attributes, keys and relationships:

  • a new table becomes a new entity (or is mapped to an existing entity with the same name);
  • a new column becomes a new attribute of the mapped entity, placed next to its neighbours;
  • a dropped column removes its attribute when no other table maps it;
  • a type or nullability change updates the attribute, unless the attribute uses a domain;
  • renames rename the mapped entity or attribute when the names were equal;
  • a new foreign key becomes a relationship named after the key (without the fk_ prefix);
  • a dropped foreign key removes its relationship when no other key maps it.

Pass --no-logical to change only the physical target.

The model is validated after applying; if the result would be invalid nothing is written and the first problem is reported.

Minimal edits

Only the declarations that changed are rewritten, in canonical form. Every other declaration keeps its exact bytes, including non-canonical spacing or extra blank lines, so a pull produces a small, reviewable diff in git. New declarations are appended to the file that owns their kind of object (the main file when in doubt). Run bluep fmt afterwards if you want the whole model in canonical form.

Options

Option Effect
--target <name> the target of the model to compare and update
--from-db --database <d> [--dialect --host --port --user --schema --option] live database source
--from-ddl <file> [--dialect <id>] DDL script source
--from-dir <dir> [--from-target <name>] another model directory
--plan <file> write the plan as JSON
--apply <file> apply a plan file
--yes plan and apply in one step
--allow-destructive also apply drops and type changes
--no-logical do not touch entities and attributes
--accept-renames treat rename candidates as renames
--ignore-comments ignore comment differences

Exit codes: 0 nothing to do or applied; 1 the printed plan has changes; 2 usage error; 3 any other error.