# SQL templates (`xlyEntry/templesql/`)

The framework keeps a small library of **SQL scaffolds** under
`xlyEntry/src/main/resources/templates/templesql/`. They are not used
at runtime to generate SQL on the fly — they're templates an engineer
copies and fills in when authoring a new stored procedure that the
framework will invoke through [generic procedure dispatch](proc-dispatch.md).

## The scaffolds shipped

Eight files ship in the live tree (`xlyEntry/src/main/resources/templates/templesql/`):

| File | What it scaffolds |
|---|---|
| `sSaveProName.sql` | A standard save procedure with the conventional parameter list and OUT codes |
| `sSaveProNameBefore.sql` | A pre-save validation procedure |
| `sDeleteProName.sql` | A standard delete procedure |
| `sProcName.sql` | A generic logic procedure (audit/calc/check) |
| `sButtonParam.sql` | A procedure invoked by a button press, parameterised through `gdsconfigformslave.sButtonParam` |
| `sp_btn_calc.sql` | A button-driven calculation procedure |
| `sProTempleByDb.sql` | A procedure that switches behaviour by database / tenant |
| `sSqlStr.sql` | A SELECT-shape scaffold for `gdsconfigformmaster.sSqlStr` |

## Placeholder convention

Every scaffold uses `{placeholderName}` syntax (single braces, no
escaping). A real-world example from `sSaveProName.sql`:

```sql
DROP PROCEDURE IF EXISTS {sProName};
CREATE PROCEDURE {sProName}(
    IN sGuid VARCHAR(100),
    IN sFormGuid VARCHAR(100),
    IN sLoginId VARCHAR(100),
    IN sBrId VARCHAR(100),
    IN sSuId VARCHAR(100),
    OUT sCode INT(1),
    OUT sReturn LONGTEXT
)
BEGIN
    -- … placeholder logic …
    Update {sMasterTableName} AS A
        JOIN {sSlaveTableName} AS B
        ON A.sId = B.sProductId
    SET A.dProductQty = B.dProductQty
    WHERE B.sId = sGuid
      AND B.sBrandsId = sBrId
      AND B.sSubsidiaryId = sSuId;
END
```

The engineer copies this file, fills in `{sProName}`,
`{sMasterTableName}`, `{sSlaveTableName}`, replaces the placeholder
logic with the real business rules, and applies the resulting SQL to
the target schema.

## What the placeholders are for

- `{sProName}` — the procedure's name (e.g., `Sp_Sales_Save`).
- `{sMasterTableName}`, `{sSlaveTableName}`, `{sAllTableName}` — the
  document family the proc operates on.
- Other placeholders depending on the scaffold.

## "Template" library, not a code generator — and what that costs

The framework does **not** auto-generate procs from these templates
based on metadata. The scaffolds are convention-enforcing copy-paste
starters, nothing more. They exist to nudge a new proc into the
shape that [generic dispatch](proc-dispatch.md) can call:

- Standard parameter list `(sGuid, sFormGuid, sLoginId, sBrId, sSuId)`.
- Returns success/error via `OUT sCode INT, OUT sReturn LONGTEXT`.
- Honours the multi-tenant filter `sBrandsId = sBrId AND sSubsidiaryId = sSuId`.

Costs of staying at "template" instead of "generator":

- **No enforcement.** A proc that drifts from the convention compiles
  fine. The framework discovers the mismatch at runtime as a
  `BadSqlGrammarException` or wrong-shaped result. There is no
  pre-merge check.
- **No regeneration.** When the convention itself changes (e.g., a
  new standard `OUT` param), the existing procs do not update.
  Engineers have to grep + rewrite, with no automation.
- **No knowledge of which proc came from which template.** A proc in
  the live DB doesn't record its origin scaffold; understanding what
  was customised away requires diffing against the scaffold by hand.
- **Customer overrides under `script/客户/` can — and do — diverge
  from the scaffold shape.** This is reasonable per customer but
  means the conventions are observed by social contract, not by
  any mechanical check.

A real code-generation pipeline (template + metadata → emitted SQL,
checked in or applied at deploy time) would catch these. The
trade xly made: less tooling to maintain, but discipline-rather-
than-enforcement on proc shapes — visible in the 1,687 procs the
schema currently carries, not all of which follow the conventions.

## Two loaders

There are **two** classes named `FileSqlUtil` in the codebase, with
very different reliability:

| Loader | What it points at | State |
|---|---|---|
| `xlyFlow/src/main/java/com/xly/sqltemplate/util/FileSqlUtil.java` | The 8 scaffolds above | **All 8 files exist** — this is the loader BACK's script-authoring screen actually uses. |
| `xlyApi/src/main/java/com/xly/api/util/FileSqlUtil.java` | 7 different template constants: `sInSqlStrTemple.sql`, `sOutSqlStrTemple.sql`, `sDataSqlTmpDef.sql`, `sDataSqlTmp.sql`, `sJsonSqlTmp.sql`, `sDbPro.sql`, `sJsonSqlTmpOut.sql` | **None of these files exist** in the live tree. The constants are dead source — likely from an older xlyApi codepath that no longer ships templates. Calls through them will throw `FileNotFoundException` at runtime. |

A maintainer touching the `xlyApi` `FileSqlUtil` should treat it as
broken-by-default and either restore the missing templates or remove
the loader.

## See also

- [Generic procedure dispatch](proc-dispatch.md) — how the framework
  invokes the procs once they're authored.
- [Slice 5 (per-customer SQL override)](../../slices/05-customer-sql-override.md)
  — when a customer needs a *replacement* of a standard proc, which
  reuses the same template structure but `DROPs` and `CREATEs` an
  override on the target schema.
