mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
docs: verify and refactor documentation to match current codebase
This commit is contained in:
+174
-244
@@ -1,323 +1,253 @@
|
||||
# Database Reference
|
||||
|
||||
Post-ERG SQLite database — schema, configuration, and operations.
|
||||
XAMXAM SQLite database — schema, configuration, and operations.
|
||||
|
||||
**Version:** 1.0 · **Engine:** SQLite 3 · **Mode:** WAL
|
||||
The database lives at **`app/storage/xamxam.db`**. The canonical schema is
|
||||
**`app/storage/schema.sql`** (base tables + seed data); incremental changes are
|
||||
applied through versioned **migrations** in `app/migrations/` (see the
|
||||
*Schema changes* section).
|
||||
|
||||
> Legacy docs referenced `posterg.db`, `database/schema.sql`, and
|
||||
> `config/bootstrap.php`. Those are obsolete — the schema now lives under
|
||||
> `app/storage/` and migrations are handled by `app/migrations/run.php`.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
## Engine & mode
|
||||
|
||||
- **Engine:** SQLite 3
|
||||
- **Mode:** WAL (`journal_mode=wal`) for safe concurrent reads + hot backups
|
||||
- **Driver:** PDO (`ext-pdo` + `ext-sqlite3`), wrapped by `app/src/Database.php`
|
||||
|
||||
---
|
||||
|
||||
## Development quick start
|
||||
|
||||
```bash
|
||||
cd database/
|
||||
sqlite3 posterg.db < schema.sql # Create DB
|
||||
sqlite3 posterg.db "SELECT name FROM sqlite_master WHERE type='table';"
|
||||
sqlite3 posterg.db "SELECT * FROM orientations;" # Verify seed data
|
||||
just migrate # run pending migrations (creates DB from schema if missing)
|
||||
just init-db # create DB from app/storage/schema.sql
|
||||
just reset-db # rm xamxam.db + init-db
|
||||
just query # open an interactive sqlite3 shell on app/storage/xamxam.db
|
||||
sqlite3 app/storage/xamxam.db ".tables"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
## Migrations
|
||||
|
||||
Database paths are centralized in `config/bootstrap.php`:
|
||||
Migrations live in `app/migrations/`. They are run via
|
||||
`app/migrations/run.php` (wrapped by `just migrate`) and tracked in the
|
||||
`_migrations` table. Apply the same way in production with
|
||||
`just deploy-migrate`.
|
||||
|
||||
- **Development**: `APP_ROOT . '/storage/test.db'` (gitignored)
|
||||
- **Production**: `APP_ROOT . '/storage/posterg.db'`
|
||||
|
||||
The `Database` class (`src/Database.php`) auto-detects: if `test.db` exists → use it, otherwise → use `posterg.db`. Override with `DB_ENV` env var (`test` or `prod`) or pass a custom path to the constructor.
|
||||
|
||||
---
|
||||
|
||||
## Schema Overview
|
||||
|
||||
### Entity Relationship
|
||||
Each migration is a numbered file:
|
||||
|
||||
```
|
||||
authors ──1:N──► thesis_authors ──N:1──► theses
|
||||
supervisors ──1:N──► thesis_supervisors ──N:1──► theses
|
||||
keywords ──1:N──► thesis_keywords ──N:1──► theses
|
||||
languages ──1:N──► thesis_languages ──N:1──► theses
|
||||
format_types ──1:N──► thesis_formats ──N:1──► theses
|
||||
orientations ──N:1──► theses
|
||||
ap_programs ──N:1──► theses
|
||||
finality_types ──N:1──► theses
|
||||
access_types ──N:1──► theses
|
||||
license_types ──N:1──► theses
|
||||
thesis_files ──N:1──► theses
|
||||
app/migrations/applied/
|
||||
├── 001_add_objet_column.sql
|
||||
├── …
|
||||
├── 041_combined_duration.sql
|
||||
├── 042_fix_contact_columns.sql
|
||||
└── 043_relabel_promoteur_ulb.php # PHP migrations can run logic too
|
||||
```
|
||||
|
||||
### Table Categories
|
||||
|
||||
| Category | Tables |
|
||||
|----------|--------|
|
||||
| **Core** | `theses`, `authors`, `supervisors`, `thesis_files`, `pages` |
|
||||
| **Lookup** | `orientations` (15), `ap_programs` (4), `finality_types` (3), `languages` (2+), `format_types` (7), `access_types` (3), `license_types`, `keywords` (dynamic) |
|
||||
| **Junction** | `thesis_authors`, `thesis_supervisors`, `thesis_keywords`, `thesis_languages`, `thesis_formats` |
|
||||
| **Views** | `v_theses_full` (admin), `v_theses_public` (published only) |
|
||||
`schema.sql` is the fully-migrated baseline; it is regenerated from the local
|
||||
DB so it always reflects the applied set of migrations.
|
||||
|
||||
---
|
||||
|
||||
## Core Tables
|
||||
## Tables
|
||||
|
||||
### `theses`
|
||||
31 tables. Grouped by purpose:
|
||||
|
||||
| Column | Type | Required | Description |
|
||||
|--------|------|----------|-------------|
|
||||
| `id` | INTEGER PK | auto | Primary key |
|
||||
| `identifier` | TEXT UNIQUE | no | Human-readable ID (e.g., "2025-002") |
|
||||
| `title` | TEXT | **yes** | Thesis title |
|
||||
| `subtitle` | TEXT | no | Optional subtitle |
|
||||
| `year` | INTEGER | **yes** | Academic year |
|
||||
| `is_doctoral` | BOOLEAN | no | 0=TFE, 1=Doctoral |
|
||||
| `orientation_id` | INTEGER FK | no | → `orientations` |
|
||||
| `ap_program_id` | INTEGER FK | no | → `ap_programs` |
|
||||
| `finality_id` | INTEGER FK | no | → `finality_types` |
|
||||
| `synopsis` | TEXT | no | ~200 word summary |
|
||||
| `context_note` | TEXT | no | Jury president note (max 150 words) |
|
||||
| `remarks` | TEXT | no | Internal remarks |
|
||||
| `duration_minutes` | INTEGER | no | For audio/video |
|
||||
| `duration_pages` | INTEGER | no | For written works |
|
||||
| `file_size_info` | TEXT | no | Free-form size description |
|
||||
| `access_type_id` | INTEGER FK | no | → `access_types` |
|
||||
| `license_id` | INTEGER FK | no | → `license_types` |
|
||||
| `jury_points` | DECIMAL(4,2) | no | Grade (0–20) |
|
||||
| `jury_note_added` | BOOLEAN | no | Jury context note flag |
|
||||
| `submitted_at` | DATETIME | no | Student submission |
|
||||
| `defense_date` | DATETIME | no | Defense date |
|
||||
| `published_at` | DATETIME | no | Publication date |
|
||||
| `is_published` | BOOLEAN | no | Publication status |
|
||||
| `baiu_link` | TEXT | no | Institutional repository link |
|
||||
| `created_at` | DATETIME | auto | Record creation |
|
||||
| `updated_at` | DATETIME | auto | Last update (trigger) |
|
||||
### Core
|
||||
|
||||
**Indexes:** `idx_theses_year`, `idx_theses_published`, `idx_theses_identifier`, `idx_theses_orientation`, `idx_theses_ap_program`, `idx_theses_access_type`
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `theses` | The main entity (a TFE or doctoral work) |
|
||||
| `authors` | People who authored a thesis |
|
||||
| `supervisors` | Jury members (promoteur / lecteur / président) |
|
||||
| `thesis_files` | Uploaded files attached to a thesis |
|
||||
| `theses` subtypes | via `objet` column (`tfe`, `these`, …) and `is_doctoral` |
|
||||
|
||||
### `authors`
|
||||
### Lookup / reference
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `id` | INTEGER PK | Auto |
|
||||
| `name` | TEXT NOT NULL | Full name |
|
||||
| `email` | TEXT | Contact email (optional) |
|
||||
| `created_at` / `updated_at` | DATETIME | Auto timestamps |
|
||||
| Table | Notes |
|
||||
|-------|-------|
|
||||
| `orientations` (15) | Arts Numériques, Dessin, …, Gravure |
|
||||
| `ap_programs` (5) | Narration Spéculative, DPM, APS, LIENS, PACS |
|
||||
| `finality_types` (3) | Approfondie, Enseignement, Spécialisée |
|
||||
| `languages` | français, anglais, néerlandais, italian (lowercase; expandable) |
|
||||
| `format_types` | Audio, Vidéo, Site web, Performance, Objet éditorial, Installation, Autre |
|
||||
| `access_types` (3) | Libre / Interne / Interdit |
|
||||
| `license_types` | CC BY 4.0 …, Domaine public, Tous droits réservés |
|
||||
| `tags` | Keywords (dynamic, lowercase-normalised, max 10/thesis) |
|
||||
|
||||
**Index:** `idx_authors_email`
|
||||
### Junction (many-to-many)
|
||||
|
||||
### `supervisors`
|
||||
`thesis_authors`, `thesis_supervisors`, `thesis_languages`,
|
||||
`thesis_formats`, `thesis_tags`.
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `id` | INTEGER PK | Auto |
|
||||
| `name` | TEXT NOT NULL | Full name |
|
||||
| `created_at` / `updated_at` | DATETIME | Auto timestamps |
|
||||
### Sharing / access
|
||||
|
||||
### `thesis_files`
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `share_links` | Student/partage submission links (slug, password, expiry, archived) |
|
||||
| `file_access_requests` | Requests to access restricted files |
|
||||
| `file_access_tokens` | One-time access tokens |
|
||||
| `file_access_sessions` | Session tokens for granted access |
|
||||
| `file_access_audit` | Audit trail of access events |
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `id` | INTEGER PK | Auto |
|
||||
| `thesis_id` | INTEGER FK | → `theses` (CASCADE) |
|
||||
| `file_type` | TEXT | `main`, `annex`, `written_part`, `other` |
|
||||
| `file_path` | TEXT | Relative path |
|
||||
| `file_name` | TEXT | Original filename |
|
||||
| `file_size` | INTEGER | Size in bytes |
|
||||
| `mime_type` | TEXT | MIME type |
|
||||
| `description` | TEXT | Optional |
|
||||
| `uploaded_at` | DATETIME | Upload timestamp |
|
||||
### Integrations / settings / content
|
||||
|
||||
### `pages`
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `peertube_settings` | PeerTube video-hosting config (instance, channel, labels) |
|
||||
| `smtp_settings` | SMTP relay config + notify email (singleton `id=1`) |
|
||||
| `site_settings` | Feature flags (access types, objets, upload, admin password hash) |
|
||||
| `pages` | Editable content pages (`about`, `charte`, `licenses`) |
|
||||
| `apropos_contents` | Structured contact / about content |
|
||||
| `form_help_blocks` | Help texts on admin/partage forms |
|
||||
| `system_cache` | Key/value cache |
|
||||
|
||||
| Column | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| `id` | INTEGER PK | Auto |
|
||||
| `slug` | TEXT UNIQUE | URL identifier |
|
||||
| `title` | TEXT NOT NULL | Page title |
|
||||
| `content` | TEXT | Markdown/HTML |
|
||||
| `is_published` | BOOLEAN | Default 1 |
|
||||
| `created_at` / `updated_at` | DATETIME | Auto timestamps |
|
||||
### Auditing
|
||||
|
||||
**Pre-loaded:** `charte`, `about`, `licenses`, `contact`
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `admin_audit_log` | Audit log of admin operations (resource, action, status, IP/UA) |
|
||||
| `audit_log` | Low-level row audit (actor, action, before/after JSON) |
|
||||
|
||||
---
|
||||
|
||||
## Lookup Tables
|
||||
## Key columns — `theses`
|
||||
|
||||
### `orientations` (15 predefined)
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | INTEGER PK | Auto |
|
||||
| `identifier` | TEXT | Human-readable id (e.g. `2025-003`) |
|
||||
| `title` | TEXT NOT NULL | |
|
||||
| `subtitle` | TEXT | |
|
||||
| `year` | INTEGER NOT NULL | |
|
||||
| `is_doctoral` | BOOLEAN | `0`=TFE, `1`=doctoral |
|
||||
| `objet` | TEXT | `'tfe'` default; other objets (`these`, `frart`) gated by site_settings |
|
||||
| `orientation_id` / `ap_program_id` / `finality_id` | INT FK | Lookup refs |
|
||||
| `synopsis` / `context_note` / `remarks` | TEXT | |
|
||||
| `duration_pages`, `duration_minutes` | INT | Combined duration handled at app level |
|
||||
| `has_annexes` | BOOLEAN | |
|
||||
| `access_type_id` | INT FK | Libre / Interne / Interdit |
|
||||
| `license_id` | INT FK | → `license_types` |
|
||||
| `cc2r` | BOOLEAN | CC2r licence checkbox |
|
||||
| `license_custom` | TEXT | Free-form licence text |
|
||||
| `jury_points` | DECIMAL(4,2) | Grade / 20 |
|
||||
| `jury_note_added` | BOOLEAN | |
|
||||
| `contact_visible` | TEXT | `'public'`/`'internal'` control for author contact |
|
||||
| `submitted_at`, `defense_date`, `published_at` | DATETIME | Lifecycle timestamps |
|
||||
| `is_published` | BOOLEAN | Public visibility flag |
|
||||
| `status` | TEXT | Default `'active'` |
|
||||
| `baiu_link` | TEXT | Institutional repo link |
|
||||
| `exemplaire_baiu`, `exemplaire_erg` | BOOLEAN | Physical copy flags |
|
||||
| `deleted_at` | DATETIME | Soft delete |
|
||||
| `created_at` / `updated_at` | DATETIME | Auto (`updated_at` trigger) |
|
||||
|
||||
Arts Numériques, Dessin, Cinéma d'animation, Installation-Performance, Peinture, Photographie, Sculpture, Vidéographie, Graphisme, Typographie, Design Numérique, Illustration, Bande-Dessinée, Sérigraphie, Gravure
|
||||
### `thesis_supervisors`
|
||||
|
||||
### `ap_programs` (4)
|
||||
Tracked via an explicit `role`/flag system rather than separate entity tables:
|
||||
|
||||
| Code | Name |
|
||||
|------|------|
|
||||
| — | Narration Spéculative |
|
||||
| DPM | Design et Politique du Multiple |
|
||||
| APS | Atelier Pratiques Situées |
|
||||
| LIENS | Lieux, Interdisciplinarités, Écologie, Nécessité, Systèmes |
|
||||
|
||||
### `finality_types` (3)
|
||||
|
||||
Approfondi, Enseignement, Spécialisé
|
||||
|
||||
### `format_types` (7)
|
||||
|
||||
Site web, Audio, Vidéo, Performance, Objet éditorial, Installation, Autre
|
||||
|
||||
### `access_types` (3)
|
||||
|
||||
| Name | Description |
|
||||
|------|-------------|
|
||||
| Libre | Full access online + library |
|
||||
| Interne | Physical only; note online |
|
||||
| Interdit | No access; note only |
|
||||
|
||||
**Business rule:** Access can only be restricted (Libre → Interne → Interdit), never opened.
|
||||
|
||||
### `languages`
|
||||
|
||||
Français, Anglais (expandable)
|
||||
|
||||
### `keywords`
|
||||
|
||||
Dynamic, grows organically. Max 10 per thesis (application-enforced).
|
||||
|
||||
---
|
||||
|
||||
## Junction Tables
|
||||
|
||||
All use composite PKs (`thesis_id`, `*_id`) with `ON DELETE CASCADE`.
|
||||
|
||||
| Table | Links | Order column |
|
||||
|-------|-------|-------------|
|
||||
| `thesis_authors` | theses ↔ authors | `author_order` |
|
||||
| `thesis_supervisors` | theses ↔ supervisors | `supervisor_order` |
|
||||
| `thesis_keywords` | theses ↔ keywords | — |
|
||||
| `thesis_languages` | theses ↔ languages | — |
|
||||
| `thesis_formats` | theses ↔ format_types | — |
|
||||
|
||||
**Indexes on junction tables:** `idx_thesis_keywords_thesis`, `idx_thesis_keywords_keyword` (and equivalents for authors)
|
||||
| Column | Notes |
|
||||
|--------|-------|
|
||||
| `role` | `promoteur`, `lecteur`, `president` |
|
||||
| `is_external` | Lecturer is external |
|
||||
| `is_ulb` | Promoteur is from the university (UCLouvain) side |
|
||||
|
||||
---
|
||||
|
||||
## Views
|
||||
|
||||
### `v_theses_full` — Admin view
|
||||
|
||||
All theses with joined relationships (GROUP_CONCAT for authors, supervisors, keywords, languages, formats, plus human-readable names for orientation, AP, finality, access type, license).
|
||||
|
||||
### `v_theses_public` — Public view
|
||||
|
||||
Same as `v_theses_full` filtered to `is_published = 1`. Unpublished theses never exposed.
|
||||
- **`v_theses_full`** — all theses with joined, human-readable fields
|
||||
(orientations, AP, finality, access, license, authors, supervisors with
|
||||
role splits, languages, formats, tags/keywords, contact fields).
|
||||
- **`v_theses_public`** — `v_theses_full` filtered to `is_published = 1`.
|
||||
- **`v_smtp_active`** — active SMTP settings row.
|
||||
|
||||
---
|
||||
|
||||
## Automatic Features
|
||||
## Scaffolded / helper tables
|
||||
|
||||
- **Auto-increment IDs:** All PKs use `AUTOINCREMENT`
|
||||
- **Auto timestamps:** `created_at` defaults to `CURRENT_TIMESTAMP`; `updated_at` refreshed by triggers on UPDATE
|
||||
- **Cascade deletes:** Deleting a thesis removes all junction + file records
|
||||
- `_migrations` — applied migration bookkeeping (used by the runner).
|
||||
|
||||
---
|
||||
|
||||
## Common Operations
|
||||
## Business rules
|
||||
|
||||
### Querying
|
||||
- **Access:** can only be *restricted* (Libre → Interne → Interdit), never opened back up — enforced in the UI/flow.
|
||||
- **Keywords (`tags`):** lowercase-normalised, de-duplicated, min 3 (form) / max 10 per thesis.
|
||||
- **Soft deletes:** reference tables such as `languages` carry `deleted_at`; hard `DELETE` is avoided where recoverability matters.
|
||||
- **Files:** stored outside the webroot under `app/storage/`; DB rows use storage-relative paths (`theses/<year>/<year>_<AUTHOR>/…`).
|
||||
|
||||
---
|
||||
|
||||
## Common operations
|
||||
|
||||
```sql
|
||||
-- Published theses
|
||||
SELECT * FROM v_theses_public ORDER BY year DESC;
|
||||
|
||||
-- Single thesis (admin)
|
||||
-- Single thesis (admin view)
|
||||
SELECT * FROM v_theses_full WHERE id = ?;
|
||||
|
||||
-- By year + orientation
|
||||
SELECT * FROM v_theses_public WHERE year = 2025 AND orientation = 'Arts Numériques';
|
||||
|
||||
-- By keyword
|
||||
-- Search by keyword (tag)
|
||||
SELECT DISTINCT t.* FROM theses t
|
||||
JOIN thesis_keywords tk ON t.id = tk.thesis_id
|
||||
JOIN keywords k ON tk.keyword_id = k.id
|
||||
WHERE k.keyword = 'écologie' AND t.is_published = 1;
|
||||
JOIN thesis_tags tt ON t.id = tt.thesis_id
|
||||
JOIN tags g ON tt.tag_id = g.id
|
||||
WHERE g.name = 'écologie' AND t.is_published = 1;
|
||||
|
||||
-- Theses per year
|
||||
SELECT year, COUNT(*) FROM theses WHERE is_published = 1 GROUP BY year ORDER BY year DESC;
|
||||
|
||||
-- Unpublished (admin)
|
||||
SELECT identifier, title, submitted_at FROM theses
|
||||
WHERE submitted_at IS NOT NULL AND is_published = 0 ORDER BY submitted_at DESC;
|
||||
```
|
||||
|
||||
### Inserting
|
||||
|
||||
```sql
|
||||
INSERT INTO authors (name, email) VALUES ('Marie Dupont', 'marie@example.com');
|
||||
INSERT INTO theses (identifier, title, year, orientation_id, finality_id, synopsis)
|
||||
VALUES ('2026-001', 'Mon Titre', 2026, 8, 1, 'Synopsis...');
|
||||
INSERT INTO thesis_authors (thesis_id, author_id, author_order) VALUES (1, 5, 1);
|
||||
INSERT OR IGNORE INTO keywords (keyword) VALUES ('performance');
|
||||
INSERT INTO thesis_keywords (thesis_id, keyword_id)
|
||||
SELECT 1, id FROM keywords WHERE keyword = 'performance';
|
||||
```
|
||||
|
||||
### Updating
|
||||
|
||||
```sql
|
||||
-- Publish a thesis
|
||||
UPDATE theses SET is_published = 1, published_at = CURRENT_TIMESTAMP WHERE id = 5;
|
||||
UPDATE theses SET jury_points = 16.5, context_note = '…', jury_note_added = 1 WHERE id = 5;
|
||||
```
|
||||
|
||||
> Note: keyword/contact fields in `v_theses_*` are `GROUP_CONCAT` aggregates —
|
||||
> prefer joining the junction tables (`thesis_tags`/`tags`) when you need
|
||||
> per-row access.
|
||||
|
||||
---
|
||||
|
||||
## Backup & maintenance
|
||||
|
||||
```bash
|
||||
just backup # SQL dump → app/storage/backup_<timestamp>.sql
|
||||
just backup-snapshot # WAL-safe hot backup + gzip → app/storage/backups/
|
||||
```
|
||||
|
||||
On the server, backups are scheduled to `/var/backups/xamxam/` via
|
||||
`/etc/cron.d/xamxam-backup` (see [deployment.md](deployment.md)).
|
||||
|
||||
Manual maintenance:
|
||||
|
||||
```bash
|
||||
sqlite3 app/storage/xamxam.db "VACUUM;" # after large deletes
|
||||
sqlite3 app/storage/xamxam.db "ANALYZE;"
|
||||
sqlite3 app/storage/xamxam.db "PRAGMA integrity_check;" # expect "ok"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup & Maintenance
|
||||
## Schema changes
|
||||
|
||||
### Backup
|
||||
1. Back up first: `cp app/storage/xamxam.db app/storage/xamxam.db.bak`
|
||||
2. Add a numbered migration file under `app/migrations/applied/`
|
||||
(`.sql` for DDL/DML, `.php` when data logic is needed).
|
||||
3. Run `just migrate` (or `just deploy-migrate` on the server) — idempotent,
|
||||
tracked via `_migrations`.
|
||||
4. When appropriate, regenerate `app/storage/schema.sql` from the applied set
|
||||
so it stays a faithful baseline.
|
||||
|
||||
```bash
|
||||
# File copy (simplest)
|
||||
cp posterg.db backups/posterg_$(date +%Y%m%d).db
|
||||
|
||||
# SQL dump (portable)
|
||||
sqlite3 posterg.db .dump > backups/posterg_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
### Maintenance
|
||||
|
||||
```bash
|
||||
sqlite3 posterg.db "VACUUM;" # Reclaim space (after large deletes, monthly)
|
||||
sqlite3 posterg.db "ANALYZE;" # Update query stats (after schema/data changes)
|
||||
sqlite3 posterg.db "PRAGMA integrity_check;" # Verify → should output "ok"
|
||||
sqlite3 posterg.db "PRAGMA journal_mode=WAL;" # Enable WAL for better concurrency
|
||||
```
|
||||
|
||||
### Recovery
|
||||
|
||||
```bash
|
||||
sqlite3 posterg.db ".recover" | sqlite3 recovered.db # Corrupted DB
|
||||
sqlite3 posterg.db .dump | sqlite3 new.db # Dump + reimport
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
- All critical foreign keys and search fields are indexed
|
||||
- Views pre-compute joins for common queries
|
||||
- For 1000+ theses: ensure WAL mode, run `ANALYZE` periodically, consider `VACUUM`
|
||||
- Cache size: `PRAGMA cache_size=-64000;` (64MB)
|
||||
- Memory-mapped I/O: `PRAGMA mmap_size=268435456;` (256MB)
|
||||
|
||||
---
|
||||
|
||||
## Schema Changes
|
||||
|
||||
### Making changes
|
||||
|
||||
1. Always backup first: `cp posterg.db posterg_before.db`
|
||||
2. Test on backup: `sqlite3 posterg_test.db < migration.sql`
|
||||
3. Use transactions: wrap ALTER/INSERT in `BEGIN; … COMMIT;`
|
||||
4. Document in `storage/migrations/` with numbered SQL files
|
||||
|
||||
### Change request format
|
||||
Change request format:
|
||||
|
||||
```
|
||||
Table: [table_name]
|
||||
|
||||
Reference in New Issue
Block a user