pgkiln 0.48.0 → 0.49.0

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.
Files changed (48) hide show
  1. package/SECURITY.md +2 -2
  2. package/db/migrations/116_dump_jobs.sql +41 -0
  3. package/db/migrations/117_rest_handlers.sql +63 -0
  4. package/db/migrations/118_change_log.sql +54 -0
  5. package/docs/apex-feature-parity.md +7 -7
  6. package/docs/guide/01-installation.md +4 -0
  7. package/docs/guide/03-builder.md +179 -5
  8. package/docs/guide/08-security.md +11 -0
  9. package/docs/guide/12-development.md +44 -6
  10. package/docs/guide/13-rest-api.md +59 -1
  11. package/docs/guide/15-extensions.md +1 -1
  12. package/examples/hr/hr_62_rest_module.sql +39 -0
  13. package/package.json +1 -1
  14. package/public/app.css +1 -1
  15. package/public/builder-icons.svg +2 -0
  16. package/public/builder.css +420 -173
  17. package/public/builder.js +48 -0
  18. package/public/sql-monitor.js +73 -0
  19. package/src/app.ts +6 -0
  20. package/src/automations.ts +6 -0
  21. package/src/binds.ts +2 -1
  22. package/src/builder/advisor.ts +24 -2
  23. package/src/builder/autorest.ts +3 -2
  24. package/src/builder/changelog.ts +240 -0
  25. package/src/builder/components.ts +1 -1
  26. package/src/builder/datapump.ts +364 -0
  27. package/src/builder/ddl-tools.ts +13 -4
  28. package/src/builder/find-workspace.ts +2 -2
  29. package/src/builder/plan-view.ts +25 -0
  30. package/src/builder/rest-modules.ts +629 -0
  31. package/src/builder/rest.ts +1 -0
  32. package/src/builder/routes.ts +4 -2
  33. package/src/builder/scheduling.ts +495 -0
  34. package/src/builder/sql-monitor.ts +397 -0
  35. package/src/builder/sql-tools.ts +6 -2
  36. package/src/builder/sql-utilities.ts +7 -39
  37. package/src/builder/sql.ts +6 -0
  38. package/src/builder/table-editor.ts +4 -1
  39. package/src/builder/top-sql.ts +24 -6
  40. package/src/builder/ui.ts +6 -4
  41. package/src/builder/worksheet.ts +5 -21
  42. package/src/changelog.ts +109 -0
  43. package/src/cron-schedule.ts +282 -0
  44. package/src/datapump.ts +546 -0
  45. package/src/dumpscript.ts +407 -0
  46. package/src/pgcron.ts +225 -0
  47. package/src/runtime/rest-handlers.ts +75 -0
  48. package/src/runtime/rest.ts +116 -22
@@ -70,6 +70,8 @@ src/
70
70
  sqlworksheet.ts (sprint 91) the worksheet's runs: pickStatements() (at the cursor, a selection, all), runStatements() (a log per
71
71
  statement, psql command tags, notices, error line and column), explainStatement() (EXPLAIN / rolled-back ANALYZE)
72
72
  datamodel.ts (sprint 98) Data Modeler diagrams: readModel() (catalog), boxSize(), fkPath() (crow's feet), autoLayout(), diagramSvg()
73
+ changelog.ts (sprint 106, migration 118) the change log: isSchemaDdl(), ddlTarget() (schema and object), changeName()
74
+ (add_bonus, create_index_x), recordChanges() (grouped per schema), Flyway / Liquibase / plain SQL writers
73
75
  tabledef.ts (sprint 95) a table as a definition: readTable(), checkTable(), tableSql() (CREATE, or ALTER from the catalog's version)
74
76
  sqlformat.ts (sprint 93) formatSql(): the worksheet's Format (clauses per line, keyword case; strings and comments kept)
75
77
  sqlplan.ts (sprint 92) EXPLAIN (FORMAT JSON) as a tree: parsePlan() (self time, slowest step, misestimates), planText()
@@ -87,9 +89,19 @@ src/
87
89
  mvt.ts Mapbox Vector Tile encoding for map layers served as tiles
88
90
  yamltext.ts the text style of the directory export: a strict YAML subset, written and read
89
91
  webclient.ts outgoing HTTP to web services: allow-list, address checks at connect time (SSRF), redirects, limits
92
+ datapump.ts (sprint 103, DA-16) Schema Export / Import jobs (meta.dump_job): findTools (pg_dump/pg_restore ≥ the
93
+ server's major), startExport (pg_dump, argument array, PGPASSWORD), readToc/parseToc (pg_restore --list),
94
+ startImport (pg_restore -f - run in one transaction between schema renames; data-only in FK order),
95
+ cancel, delete, dumpTick (interrupted jobs, expired files; called by the scheduler)
96
+ dumpscript.ts (sprint 103) the import's script runner: ScriptLexer (psql-style split, COPY data, \restrict),
97
+ checkStatement (only the dump's schema; no roles, DO, COPY to/from files…), COPY FROM STDIN as a pg submittable
90
98
  dbroles.ts (sprint 99, DA-13) Database Users' SQL: roleSql (CREATE/ALTER ROLE, memberships, search path, schema
91
99
  and default privileges: only what differs from the catalog), dropSql, checkRole/checkPassword,
92
100
  scramVerifier (SCRAM-SHA-256, SASLprep) and the password slot (maskSql for review and log, withVerifier to run)
101
+ cron-schedule.ts (sprint 101, DA-12) pg_cron schedules without a database: parseSchedule (cron fields, $, n seconds, macros),
102
+ nextRuns in a time zone, describeSchedule in words, the builder's presets and fields
103
+ pgcron.ts (sprint 101, DA-12) pg_cron for SQL Workshop → Scheduling: cronState (installed, loaded, cron.database_name,
104
+ access), jobs and runs, jobSql/activeSql/unscheduleSql/purgeSql (reviewed, then applySql), runNow
93
105
  secrets.ts secrets at rest (web credentials): AES-256-GCM with PGKILN_SECRET_KEY
94
106
  websources.ts web credentials (OAuth2 client credentials/password/refresh token grants, token cache, stored refresh
95
107
  tokens) and REST data sources: requests, JSON paths, typed rows, response cache, write-back operations
@@ -152,7 +164,10 @@ src/
152
164
  maps.ts map region (data for Leaflet: layers with a query each, markers, clusters or heat, PostGIS geometry as GeoJSON, report filter by area or distance; list fallback, head assets)
153
165
  spatial.ts spatial filtering on the server: map area and distance parsing, PostGIS detection and SQL (ST_Intersects, ST_DWithin), lat/lng fallback (bounding box, haversine)
154
166
  pwa.ts Progressive Web App: manifest, service worker route, icons (PNG encoder), offline page
155
- rest.ts REST modules: handler checks, matching, bearer tokens, execution (collections stream from a cursor), OpenAPI
167
+ rest.ts REST modules: handler and template checks, matching, bearer tokens, module privilege, the constrained-role
168
+ check, execution (collections stream from a cursor), OpenAPI (templates, documented parameters, roles)
169
+ rest-handlers.ts (sprint 104, migration 117) PL/pgSQL handlers (binds as settings, DO block, meta.rest_reply) and media
170
+ handlers (content type and file name cleaned, sandboxed response)
156
171
  autorest.ts (sprint 100, migration 113) AutoREST: /a/:alias/autorest/<object>/ for the tables and views SQL Workshop →
157
172
  RESTful Services published (meta.rest_object): caller() of rest.ts, SET ROLE to the app role, owner/BYPASSRLS
158
173
  check, columns from the catalog (column grants), filters/q/order/paging, POST/PUT/DELETE by primary key, OpenAPI
@@ -246,9 +261,14 @@ src/
246
261
  quicksql.ts SQL Workshop → Quick SQL page (preview, save as script, run)
247
262
  querybuilder.ts SQL Workshop → Query Builder: catalog, joins by foreign key or drawn, functions/group by, buildQuery() from the URL; the canvas is in builder.js
248
263
  users.ts user directory and identity providers
264
+ datapump.ts (sprint 103, DA-16) SQL Workshop → Schema Export / Import (/builder/sql/data-pump, administrators only):
265
+ jobs, export form, upload (streamed to PGKILN_DUMP_DIR), archive preview and import options, job log, download
249
266
  db-users.ts (sprint 99, DA-13) Administration → Database Users (/builder/db-users, administrators only): PostgreSQL
250
267
  roles with highlights, create/edit (review the masked SQL, then Apply in one transaction), drop with
251
268
  reassigning; pgkiln's own roles read-only, roles of applications and workspaces not dropped
269
+ scheduling.ts (sprint 101, DA-12) SQL Workshop → Scheduling (/builder/sql/scheduling, administrators only): pg_cron jobs
270
+ with next runs, create/edit with a schedule builder and preview (review the SQL, then Apply), disable,
271
+ run now, unschedule, run history and purge; without pg_cron the install note and pgkiln's automations
252
272
  api.ts per-app REST API page (API role, tokens)
253
273
  globalization.ts translations, XLIFF/CSV, text messages
254
274
  dataload.ts SQL Workshop → Load Data (with definitions, save a mapping as one); data load definition spec (Shared Components)
@@ -260,11 +280,18 @@ src/
260
280
  region-settings.ts page designer: settings forms for grid, chart (gauge, drill-down), cards, calendar (views, create, drag and drop), facets, smart filters, display selector, list
261
281
  search.ts app search, "where used" (appEntries, search, whereUsed, usedInPanel)
262
282
  advisor.ts Advisor: EXPLAIN every SQL fragment, reference checks, plpgsql_check
263
- ddl-tools.ts SQL Workshop → DDL and Compare (src/ddl.ts)
283
+ ddl-tools.ts SQL Workshop → DDL and Compare (src/ddl.ts); (sprint 106) the Apply and record form
284
+ changelog.ts (sprint 106) SQL Workshop → Change Log (/builder/sql/changes): change sets per schema, latest app export,
285
+ downloads (POST, mark exported_at), rename, delete; /sql/compare/apply (one transaction, recorded)
264
286
  sql-utilities.ts SQL Workshop → Utilities (sprint 62): the tools' tiles, OBJECT_REPORTS (fixed catalogue SQL over the
265
- connection's usable schemas), About Database, Database Monitor (administrators); read only, with a timeout
287
+ connection's usable schemas), About Database; read only, with a timeout; /sql/utilities/monitor redirects to the SQL Monitor
266
288
  login-page.ts Application settings → Login page and Forgot password; Create a login page (page 9999)
267
- top-sql.ts Top SQL per app role from pg_stat_statements
289
+ top-sql.ts Top SQL per app role from pg_stat_statements; (sprint 102) statementsState / statementsNote (installed and loaded?)
290
+ sql-monitor.ts (sprint 102, DA-14) SQL Workshop → SQL Monitor (administrators): /sql/monitor (sessions, wait events, progress;
291
+ ?fragment=1 the live part), POST /sql/monitor/session (cancel / terminate), /sql/monitor/top (topStatements:
292
+ pg_stat_statements sortable, bars), /reset, /statements/:queryid (totals by role, explainForMonitor: EXPLAIN only,
293
+ one statement by the extended protocol, read only, generic plan for $1), /statements/:queryid/worksheet
294
+ plan-view.ts (sprint 92; shared since 102) planView(): a plan (src/sqlplan.ts) as a tree, for the worksheet and the SQL Monitor
268
295
  diagnostics.ts Activity → Debug messages (level, list, one request's entries, purge); Workspace utilities →
269
296
  Installation (version, install/upgrade runs, applied and missing migrations; administrators)
270
297
  ldap.ts Users → LDAP directories
@@ -279,6 +306,9 @@ src/
279
306
  autorest.ts (sprint 100) SQL Workshop → RESTful Services (/builder/sql/rest): publish a table or view through an
280
307
  application of the workspace (checks: schema the workshop may use, app role not the owner), its page
281
308
  (grants per method, RLS, security_invoker, endpoints, cURL/fetch, OpenAPI link), settings, disable
309
+ rest-modules.ts (sprint 104) SQL Workshop → RESTful Services → Modules (/builder/sql/rest/modules): modules of the
310
+ workspace's applications, the module › template › handler tree, handler forms, Try it (inject with a
311
+ short-lived token), Run only / lock checks
282
312
  workflows.ts workflow versions, diagram and instances (Shared Components)
283
313
  process-jobs.ts page designer: the Jobs tab of a background chain process
284
314
  template-spec.ts template component property form (Shared Components)
@@ -311,12 +341,14 @@ public/
311
341
  app.js client runtime: dialogs (dialog_closed actions), popup LOVs, dynamic actions (focus, classes, messages), grids (add/duplicate rows, master-detail refresh, move/resize columns, copy/paste of cell ranges), menus, lazy regions, maps (Leaflet layers, marker clusters, heat layer, layer legend, area/distance filter) (no inline JS)
312
342
  code-editor.js, .css builder code editor: enhances <textarea data-code>, highlighting, suggestions (no dependencies)
313
343
  palette.js (sprint 96) the builder's search dialog (Ctrl+K), recently visited SQL Workshop pages, the Launchpad's tabs
344
+ sql-monitor.js (sprint 102) the SQL Monitor: reloads the sessions' live part every 5 / 15 / 60 s, Pause (localStorage pgkiln.sqlMonitor)
314
345
  data-modeler.js, .css (sprint 98) the Data Modeler: drag boxes, undo/redo, save, zoom/fit, auto layout, mini-map, SVG/PNG export, print
315
346
  worksheet.js, .css (sprint 91) the SQL Worksheet: run keys with the cursor, output tabs, navigator swap and search, drag and drop, splitters;
316
347
  (92) grid sort, value dialog, history filter; (93) worksheet tabs in localStorage, Files, binds dialog, format, find, case
317
- builder.css builder only: IDE look (dark chrome, icon rail, panes), builder light/dark tokens
348
+ builder.css builder only: IDE look (dark chrome, icon rail, panes); design tokens at the top (colour,
349
+ elevation, radius, type, motion; light and dark sets, sprint 105), then the shared controls
318
350
  builder.js builder only: tabs, component tree, property filter, drag and drop on the layout
319
- builder-icons.svg builder only: icons of the rail, toolbar and designer (b-*)
351
+ builder-icons.svg builder only: icons of the rail, toolbar and designer (b-*); b-kiln is the brand mark (brandMark() in ui.ts)
320
352
  test/
321
353
  binds.test.ts unit tests
322
354
  security.test.ts security regression tests (in-process, against the database)
@@ -333,9 +365,14 @@ test/
333
365
  unload.test.ts Unload Data: CSV/JSON/XLSX/XML output, read back with Load Data, read-only and one-statement checks, streaming
334
366
  app-from-file.test.ts Create → From a file: proposed names and types, app + table + rows + pages, row errors, login, validation
335
367
  workshop.test.ts SQL scripts, Quick SQL pages, query builder, data load definitions (Load Data, the process, export)
368
+ cron-schedule.test.ts (sprint 101) pg_cron schedules: parsing and errors, next runs across time zones and daylight saving, words
369
+ scheduling.test.ts (sprint 101) SQL Workshop → Scheduling: the SQL, the no-pg_cron page, create/edit/disable/run now/unschedule,
370
+ history and purge; against pg_cron when the test database has it, else test/pgcron-mock.ts (a stand-in)
336
371
  sql-charts.test.ts (sprint 97) the worksheet's Chart tab, saving a chart, the Charts dashboard, a chart's page, read-only runs
337
372
  launchpad.test.ts (sprint 96) Launchpad groups, pins (builder pages only, at most 24), the search (JSON and page)
338
373
  tabledef.test.ts (sprint 95) table definitions round trip: CREATE, read, ALTER, read again; mistakes
374
+ changelog.test.ts (sprint 106) DDL recognition and names, recording from Table Properties, the worksheet and Compare,
375
+ downloads (Flyway, Liquibase, SQL), stable numbers, the latest application export
339
376
  table-editor.test.ts (sprint 95) the Table Properties page: preview, apply, a failing apply rolls back, template, worksheet
340
377
  object-actions.test.ts (sprint 94) object menus: Quick DDL, Drop with dependents, Add index / trigger / policy, the panel, not-found
341
378
  data-modeler.test.ts (sprint 98) Data Modeler: "hr core" with its FK lines, saved positions, SVG, objects, DDL, dictionary, geometry
@@ -352,6 +389,7 @@ test/
352
389
  charts.test.ts chart markup per kind (geometry as classes), gauges, Gantt time axis and dependencies, pyramid, polar, range, box plot, stock, drill-down links
353
390
  calendar.test.ts calendar views, create links, moving events (pure and over HTTP)
354
391
  rest-sources.test.ts REST data sources, web credentials, SSRF checks, invoke_api (mock service + HR page 23)
392
+ rest-modules.test.ts (sprint 104) modules, templates, PL/pgSQL and media handlers, OpenAPI, the builder's tree and Try it
355
393
  workflow-invoke.test.ts workflow invoke_api steps: the call between transactions, faults, retry, lease, Advisor, export (mock service)
356
394
  large-tables.test.ts row ranges, max_rows, row limits, lazy regions, region caching, streamed downloads (HR page 25)
357
395
  grid.test.ts interactive grid: aggregates, layouts per user, saved grid reports, master-detail, row actions (HR page 27)
@@ -31,11 +31,12 @@ answer 404.
31
31
  |---|---|
32
32
  | `method` | `GET`, `POST`, `PUT`, `PATCH` or `DELETE` |
33
33
  | `path` | Segments with `:parameters`, e.g. `orders/:id/lines` |
34
- | `type` | `collection`: a SELECT, returned page by page as `{items, offset, limit, has_more}` (`?limit=` up to 500, `?offset=`, `page_size` for the default); the page streams from a cursor as a chunked JSON array (an error before the first rows is still a 4xx/5xx, a failure after them ends the response short) · `item`: one row as an object, 404 when there is none · `sql`: statements; the first row of the last one is the response (201 for POST, 204 without a row; `status` overrides) |
34
+ | `type` | `collection`: a SELECT, returned page by page as `{items, offset, limit, has_more}` (`?limit=` up to 500, `?offset=`, `page_size` for the default); the page streams from a cursor as a chunked JSON array (an error before the first rows is still a 4xx/5xx, a failure after them ends the response short) · `item`: one row as an object, 404 when there is none · `sql`: statements; the first row of the last one is the response (201 for POST, 204 without a row; `status` overrides) · `plpgsql` (sprint 104): a PL/pgSQL block that answers with `meta.rest_reply(body, status)` ([below](#modules-templates-and-handlers-in-the-sql-workshop)) · `media`: a query whose row has `content_type`, `content` and optionally `filename`, sent as that content |
35
35
  | `source` | The SQL. Binds: path parameters, query parameters and the fields of a JSON (or form) body, upper case (`:ID`, `:CUSTOMER`); `:BODY` is the whole JSON body. `:APP_USER` (the caller), `:APP_ID` and `:APP_ALIAS` are set by the server: a parameter of that name (or `APP_SESSION`, `APP_PAGE_ID`, `REQUEST`, `APP_LANGUAGE`) is ignored, and can't be a path parameter |
36
36
  | `roles` | The caller needs one of these roles (as `meta.has_role()` sees them) |
37
37
  | `auth` | `token` (default) or `public` (no token; the SQL runs with `meta.app_user()` = `nobody`) |
38
38
  | `description` | For the OpenAPI description |
39
+ | `params` | (sprint 104) Documented parameters: `[{"name": "start_date", "in": "body", "type": "string", "required": true, "description": "…"}]` (`in`: `query` or `body`; types `string`, `integer`, `number`, `boolean`). They go into the OpenAPI document; a required one that is missing or empty answers 400 |
39
40
 
40
41
  **Calling them.** Send `Authorization: Bearer <token>` with a token from **App → REST API**: a token
41
42
  issued for an account, or one from `POST /oauth/token` for an OAuth client ([below](#tokens)). An
@@ -63,6 +64,63 @@ curl -X POST http://127.0.0.1:3100/a/hr/rest/v1/leave -H "Authorization: Bearer
63
64
  -H "Content-Type: application/json" -d '{"start_date": "2027-05-03", "end_date": "2027-05-04", "reason": "dentist"}'
64
65
  ```
65
66
 
67
+ ## Modules, templates and handlers in the SQL Workshop
68
+
69
+ **SQL Workshop → RESTful Services → Modules** (sprint 104; Oracle Database Actions / ORDS: REST › Modules) edits
70
+ the same REST modules the ORDS way, for every application of the workspace:
71
+
72
+ - a **module** is a base path, `/a/<alias>/rest/<name>/`, with a title, a description, *Enabled* and its
73
+ **privilege**: application roles (as `meta.has_role()` sees them) of which a caller needs one for *every*
74
+ handler of the module, public ones included;
75
+ - a **template** is a URI below it with `:parameters`, e.g. `emp/:id`, with a description for the OpenAPI document
76
+ (a template can exist before its first handler; renaming it moves its handlers);
77
+ - a **handler** answers one method of a template: its source type (*collection query*, *single row*, *SQL*,
78
+ *PL/pgSQL*, *media*), the source, who may call it (a token, or explicitly *public*), further roles, the page size
79
+ or status, and documented parameters, one per line: `start_date body string required First day`.
80
+
81
+ The tree on the left of each page shows module › templates › handlers. A Run only or locked application refuses
82
+ changes. The module's page checks the role the handlers run as and links the module's **OpenAPI** document.
83
+
84
+ **PL/pgSQL handlers** run their block as a `DO` statement as the application's database role. A `DO` block takes
85
+ no parameters, so pgkiln sets the request's binds as transaction-local settings (with query parameters: values
86
+ never become SQL text) and turns `:NAME` in the block into `meta.rest_param('NAME')` (text, NULL when absent):
87
+
88
+ ```sql
89
+ declare
90
+ v_id int;
91
+ begin
92
+ v_id := sales.create_order(:CUSTOMER, :TOTAL::numeric);
93
+ perform meta.rest_reply(jsonb_build_object('id', v_id), 201);
94
+ end
95
+ ```
96
+
97
+ `meta.rest_reply(body jsonb, status int)` sets the response (status 200–599, also 4xx for your own errors; the
98
+ transaction is still committed); without it the handler answers 204. A `raise exception` answers 400 with its
99
+ message and rolls back. Statements without `begin … end` are wrapped in one.
100
+
101
+ **Media handlers** run a query whose first row has `content_type`, `content` (bytea or text) and optionally
102
+ `filename`; the response is that content, sent with `X-Content-Type-Options: nosniff` and a sandboxing
103
+ Content-Security-Policy, so HTML or SVG from a table can't run script in the application's origin. No row: 404.
104
+
105
+ **Try it.** A handler's page sends a request to the real endpoint, with path parameters, a query string and a JSON
106
+ body, either without a token (to see the 401) or *as* an account of the application with a short-lived token (the
107
+ same as issuing one on App → REST API). A PUT, POST or DELETE changes data. The response's status and body are
108
+ shown in the page; it works without JavaScript.
109
+
110
+ **Example:** the HR example's module `hr` (`examples/hr/hr_62_rest_module.sql`): `GET emp/:id` (single row),
111
+ `GET emp/:id/photo` (media), `POST leave` (PL/pgSQL with body parameters) and `GET payroll/:deptno` (roles manager or
112
+ admin):
113
+
114
+ ```bash
115
+ curl http://127.0.0.1:3100/a/hr/rest/hr/emp/7839 -H "Authorization: Bearer $TOKEN"
116
+ curl -X POST http://127.0.0.1:3100/a/hr/rest/hr/leave -H "Authorization: Bearer $TOKEN" \
117
+ -H "Content-Type: application/json" -d '{"start_date": "2027-05-03", "end_date": "2027-05-04", "reason": "dentist"}'
118
+ # {"id": 12, "status": "requested"}
119
+ ```
120
+
121
+ Handlers never run as a superuser, a role that bypasses row level security or pgkiln's owner: such a request
122
+ answers 403 (set `RUNTIME_DATABASE_URL` and give the application its own database role).
123
+
66
124
  ## AutoREST: tables and views from the SQL Workshop
67
125
 
68
126
  **SQL Workshop → RESTful Services** (`/builder/sql/rest`; Database Actions: a table's *REST › Enable*, ORDS
@@ -19,7 +19,7 @@ setup`) and on practically every managed service. Install with `create extension
19
19
  | **unaccent** | Accent-insensitive search (`café` finds `cafe`), essential in multilingual apps | Text search options |
20
20
  | **citext** | Case-insensitive text columns (e-mail addresses, codes) without `lower()` everywhere | NLS_COMP=LINGUISTIC |
21
21
  | **btree_gist** | Exclusion constraints on ranges, e.g. **no overlapping bookings/leave** enforced by the database, race-free | Custom PL/SQL checks |
22
- | **pg_stat_statements** | Which SQL is slow or called most, per role, so per app (`db_role`) | Monitor Activity → Top SQL |
22
+ | **pg_stat_statements** | Which SQL is slow or called most, per role, so per app (`db_role`) | Monitor Activity → Top SQL; SQL Workshop → SQL Monitor (Top SQL, a statement's totals) |
23
23
  | **tablefunc** | `crosstab()` for pivot tables | Interactive report *Pivot* |
24
24
  | **ltree** | Hierarchies (org charts, categories, menus) with fast subtree queries | Tree region, `CONNECT BY` |
25
25
  | **postgres_fdw**, **file_fdw** | Query another PostgreSQL database, or a CSV file, as if it were a table | Remote servers / database links |
@@ -0,0 +1,39 @@
1
+ -- =====================================================================
2
+ -- HR example, part 62: a REST module built in SQL Workshop › RESTful
3
+ -- Services (sprint 104; docs/guide/13-rest-api.md → Modules, templates
4
+ -- and handlers). Module "hr" at /a/hr/rest/hr/, with a token from
5
+ -- App › REST API:
6
+ --
7
+ -- emp/ GET collection query: the active employees
8
+ -- emp/:id GET single row: one employee
9
+ -- emp/:id/photo GET media: the employee's photo
10
+ -- leave POST PL/pgSQL: request leave with
11
+ -- {"start_date": "…", "end_date": "…", "reason": "…"}
12
+ -- payroll/:deptno GET collection query; managers and administrators only
13
+ -- =====================================================================
14
+ insert into meta.rest_module (app_id, name, title, description, templates, handlers)
15
+ select id, 'hr', 'HR (RESTful Services)', 'Employees and leave requests: a module of templates and handlers.',
16
+ $t$[
17
+ {"uri": "emp", "description": "The employees"},
18
+ {"uri": "emp/:id", "description": "One employee by number"},
19
+ {"uri": "emp/:id/photo", "description": "An employee's photo"},
20
+ {"uri": "leave", "description": "Leave requests of the caller"},
21
+ {"uri": "payroll/:deptno", "description": "Payroll of a department"}
22
+ ]$t$::jsonb,
23
+ $h$[
24
+ {"method": "GET", "path": "emp", "type": "collection", "description": "The active employees",
25
+ "source": "select empno, initcap(ename) as name, initcap(job) as job, deptno from hr.emp where active order by empno"},
26
+ {"method": "GET", "path": "emp/:id", "type": "item", "description": "One employee",
27
+ "source": "select empno, initcap(ename) as name, initcap(job) as job, deptno, hiredate, mgr from hr.emp where empno = :ID::int"},
28
+ {"method": "GET", "path": "emp/:id/photo", "type": "media", "description": "The employee's photo",
29
+ "source": "select photo_mime as content_type, photo as content, photo_name as filename from hr.emp where empno = :ID::int and photo is not null"},
30
+ {"method": "POST", "path": "leave", "type": "plpgsql", "description": "Request leave (as the caller)",
31
+ "params": [{"name": "start_date", "in": "body", "type": "string", "required": true, "description": "First day (YYYY-MM-DD)"},
32
+ {"name": "end_date", "in": "body", "type": "string", "required": true, "description": "Last day (YYYY-MM-DD)"},
33
+ {"name": "reason", "in": "body", "type": "string"}],
34
+ "source": "declare\n v_id int;\nbegin\n v_id := hr.request_leave(:START_DATE::date, :END_DATE::date, :REASON);\n perform meta.rest_reply(jsonb_build_object('id', v_id, 'status', 'requested'), 201);\nend"},
35
+ {"method": "GET", "path": "payroll/:deptno", "type": "collection", "roles": ["manager", "admin"], "description": "Salaries of a department",
36
+ "source": "select empno, initcap(ename) as name, sal from hr.emp where deptno = :DEPTNO::int and active order by empno"}
37
+ ]$h$::jsonb
38
+ from meta.app where alias = 'hr'
39
+ on conflict (app_id, name) do nothing;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pgkiln",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "Open source low-code, SQL-driven application builder for PostgreSQL, inspired by Oracle APEX",
5
5
  "keywords": [
6
6
  "postgresql",
package/public/app.css CHANGED
@@ -416,7 +416,7 @@ dialog.region-inline_drawer:target { inset: 0 0 0 auto; }
416
416
  .req { color: var(--danger); margin-left: 0.2em; }
417
417
  input:not([type=checkbox]):not([type=radio]), select, textarea {
418
418
  font: inherit; color: var(--text); background: var(--surface);
419
- border: 1px solid var(--border); border-radius: calc(var(--radius) * 0.75); padding: 0.45rem 0.6rem; width: 100%; min-width: 0;
419
+ border: 1px solid var(--field-border, var(--border)); border-radius: calc(var(--radius) * 0.75); padding: 0.45rem 0.6rem; width: 100%; min-width: 0;
420
420
  }
421
421
  input:disabled, select:disabled, textarea:disabled { opacity: 0.6; }
422
422
  textarea.code { font-family: var(--mono); font-size: 0.84rem; line-height: 1.45; tab-size: 2; }
@@ -30,4 +30,6 @@
30
30
  <symbol id="b-save" viewBox="0 0 24 24"><path d="M5 4h11l3 3v13H5z"/><path d="M8 4v5h7V4M8 20v-6h8v6"/></symbol>
31
31
  <symbol id="b-dots" viewBox="0 0 24 24"><path d="M5 12h.01M12 12h.01M19 12h.01" stroke-width="3.2"/></symbol>
32
32
  <symbol id="b-pages" viewBox="0 0 24 24"><path d="M8 3h8l4 4v11H8z"/><path d="M16 3v4h4"/><path d="M4 7v14h11"/></symbol>
33
+ <!-- (sprint 105) the pgkiln brand mark: an ember flame with a hollow core; filled, unlike the stroke icons -->
34
+ <symbol id="b-kiln" viewBox="0 0 24 24"><path fill="currentColor" stroke="none" fill-rule="evenodd" d="M12.3 1.8c.6 3.1 2.5 4.7 4.2 6.6 1.8 1.9 3.1 4 3.1 6.7a7.6 7.6 0 0 1-15.2 0c0-2.6 1.1-4.6 2.8-6.1.2 1.6 1 2.8 2.2 3.4C9.2 8.6 10.5 4.7 12.3 1.8zM12 20.4a2.9 2.9 0 0 0 2.9-2.9c0-1.4-.5-2.9-1.4-4-.8.8-1.3 1.8-1.4 3-.7-.3-1.1-.9-1.3-1.6-.9.9-1.8 1.9-1.8 3.3a3 3 0 0 0 3 2.2z"/></symbol>
33
35
  </svg>