Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions docs/sqlrunner-test.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
---
unlisted: true
title: SQLRunner — pagina di test
description: Sandbox di verifica del componente SQLRunner (SQL Live Blocks)
---

# SQLRunner — sandbox di test

Pagina temporanea per validare gli SQL Live Blocks. Non è linkata dalla sidebar
(`unlisted: true`). Il dataset di prova è `static/sql-datasets/archivista/biblioteca.sql`
(autori, libri, prestiti). Il motore (sql.js, ~660 KB di WASM self-hosted) viene
scaricato **solo al primo Esegui**: questa pagina a riposo non deve caricare nulla.

## 1. Fence `sql live` — SELECT base con dataset

```sql live dataset=archivista/biblioteca
SELECT titolo, anno, genere
FROM libri
WHERE anno > 1950
ORDER BY anno;
```

## 2. JOIN tra tabelle

```sql live dataset=archivista/biblioteca title="Chi ha scritto cosa"
SELECT l.titolo, a.nome AS autore, a.paese
FROM libri AS l
JOIN autori AS a ON a.id = l.autore_id
ORDER BY a.nome;
```

## 3. Errore SQL (deve mostrare il messaggio in rosso)

```sql live dataset=archivista/biblioteca
SELECT titolo FROM romanzi; -- la tabella si chiama "libri"!
```

## 4. Restore trasparente: DROP e poi riesegui

Esegui il `DROP TABLE`, poi rimetti la `SELECT` originale (bottone ↺) e riesegui:
la tabella **c'è di nuovo**. In modalità stateless il database riparte dal seed
a ogni esecuzione, senza che lo studente debba fare nulla.

```sql live dataset=archivista/biblioteca
DROP TABLE prestiti;
SELECT name FROM sqlite_master WHERE type = 'table';
```

## 5. Readonly

```sql live dataset=archivista/biblioteca readonly
SELECT COUNT(*) AS numero_libri FROM libri;
```

## 6. Stateful: lo stato sopravvive tra i run

Con `stateful` il database **non** viene ripristinato a ogni esecuzione: esegui
l'INSERT due volte e i conteggi crescono. In toolbar compare il bottone con il
database per il reset esplicito; dopo il reset (o dopo un timeout) appare la
notice «database ripristinato».

```sql live dataset=archivista/biblioteca stateful title="biblioteca (stateful)"
INSERT INTO prestiti (libro_id, studente, classe, data_prestito)
VALUES (2, 'Nuovo Studente', '3A', '2026-06-10');

SELECT COUNT(*) AS prestiti_totali FROM prestiti;
```

## 7. Troncamento risultati (`maxRows`)

La CTE genera 1000 righe ma `maxRows=15`: il worker taglia il risultato prima
di spedirlo alla pagina e compare il banner di troncamento.

```sql live maxRows=15
WITH RECURSIVE numeri(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM numeri WHERE n < 1000
)
SELECT n, n * n AS quadrato FROM numeri;
```

## 8. Query runaway → watchdog timeout

Ricorsione senza condizione di stop: dopo 3 secondi il worker viene terminato,
la pagina resta reattiva e il run successivo funziona normalmente.

```sql live dataset=archivista/biblioteca timeout=3000 title="bomba ricorsiva"
WITH RECURSIVE c(x) AS (
SELECT 1
UNION ALL
SELECT x + 1 FROM c
)
SELECT COUNT(*) FROM c;
```

## 9. Senza dataset: DB vuoto per lezioni su CREATE TABLE

```sql live
CREATE TABLE pianeti (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
lune INTEGER DEFAULT 0
);

INSERT INTO pianeti (nome, lune) VALUES ('Marte', 2), ('Giove', 95);

SELECT * FROM pianeti;
```

## 10. Vincoli: foreign key attive (`PRAGMA foreign_keys = ON`)

L'INSERT viola la foreign key (`autore_id = 999` non esiste): deve fallire con
un errore di vincolo, non inserire silenziosamente.

```sql live dataset=archivista/biblioteca
INSERT INTO libri (titolo, autore_id, anno)
VALUES ('Libro fantasma', 999, 2026);
```

## 11. NULL e valori particolari

I `NULL` (prestiti non ancora restituiti) devono comparire in corsivo tenue.

```sql live dataset=archivista/biblioteca
SELECT studente, data_prestito, data_restituzione
FROM prestiti
ORDER BY data_prestito;
```

## 12. Più statement → più tabelle di risultati

```sql live dataset=archivista/biblioteca
SELECT genere, COUNT(*) AS quanti FROM libri GROUP BY genere ORDER BY quanti DESC;
SELECT paese, COUNT(*) AS autori FROM autori GROUP BY paese;
```

## 13. `maxLines` piccolo per forzare lo scroll interno

```sql live dataset=archivista/biblioteca maxLines=5
SELECT
a.nome,
COUNT(l.id) AS libri_scritti,
MIN(l.anno) AS primo_libro,
MAX(l.anno) AS ultimo_libro
FROM autori AS a
LEFT JOIN libri AS l ON l.autore_id = a.id
GROUP BY a.id
HAVING libri_scritti > 1
ORDER BY libri_scritti DESC;
```

## 14. `noExplain` — niente bacchetta magica

```sql live dataset=archivista/biblioteca noExplain
SELECT nome FROM autori WHERE paese = 'Italia';
```

## 15. Componente JSX diretto con prompt custom

import SQLRunner from '@site/src/theme/SQLRunner';

<SQLRunner
dataset="archivista/biblioteca"
title="Aggregazione guidata"
code={`SELECT classe, COUNT(*) AS prestiti
FROM prestiti
GROUP BY classe;`}
explainPrompt={`Sto imparando GROUP BY in SQL. Spiegami questa query in 3 frasi, in italiano, dicendo cosa finisce in ogni gruppo e cosa conta la COUNT.\n\nQuery:\n\`\`\`sql\n{code}\n\`\`\`\n`}
/>

## 16. `runOnMount` — esegue da solo al caricamento

<SQLRunner
dataset="archivista/biblioteca"
runOnMount
readonly
title="prestiti aperti (auto-run)"
code={`SELECT l.titolo, p.studente
FROM prestiti AS p
JOIN libri AS l ON l.id = p.libro_id
WHERE p.data_restituzione IS NULL;`}
/>
12 changes: 7 additions & 5 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type * as Preset from '@docusaurus/preset-classic';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const remarkPyRunner = require('./plugins/pyrunner/remark.js');
// eslint-disable-next-line @typescript-eslint/no-require-imports
const remarkSqlRunner = require('./plugins/sqlrunner/remark.js');
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { protectCode, restoreCode } = require('./plugins/smartypants-guard.js');

// Keyword admonition custom (Notion-style callouts). Vedi
Expand Down Expand Up @@ -79,7 +81,7 @@ export default async function createConfig(): Promise<Config> {
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl: 'https://github.com/marcofarina/python-doesnt-byte',
beforeDefaultRemarkPlugins: [remarkPyRunner],
beforeDefaultRemarkPlugins: [remarkPyRunner, remarkSqlRunner],
remarkPlugins: [protectCode, smartypants, restoreCode],
admonitions,
},
Expand Down Expand Up @@ -125,7 +127,7 @@ export default async function createConfig(): Promise<Config> {
routeBasePath: 'programmatore',
sidebarPath: './sidebars/programmatore.ts',
editUrl: 'https://github.com/marcofarina/python-doesnt-byte',
beforeDefaultRemarkPlugins: [remarkPyRunner],
beforeDefaultRemarkPlugins: [remarkPyRunner, remarkSqlRunner],
remarkPlugins: [protectCode, smartypants, restoreCode],
admonitions,
},
Expand All @@ -138,7 +140,7 @@ export default async function createConfig(): Promise<Config> {
routeBasePath: 'artefice',
sidebarPath: './sidebars/artefice.ts',
editUrl: 'https://github.com/marcofarina/python-doesnt-byte',
beforeDefaultRemarkPlugins: [remarkPyRunner],
beforeDefaultRemarkPlugins: [remarkPyRunner, remarkSqlRunner],
remarkPlugins: [protectCode, smartypants, restoreCode],
admonitions,
},
Expand All @@ -151,7 +153,7 @@ export default async function createConfig(): Promise<Config> {
routeBasePath: 'archivista',
sidebarPath: './sidebars/archivista.ts',
editUrl: 'https://github.com/marcofarina/python-doesnt-byte',
beforeDefaultRemarkPlugins: [remarkPyRunner],
beforeDefaultRemarkPlugins: [remarkPyRunner, remarkSqlRunner],
remarkPlugins: [protectCode, smartypants, restoreCode],
admonitions,
},
Expand All @@ -164,7 +166,7 @@ export default async function createConfig(): Promise<Config> {
routeBasePath: 'apprendista',
sidebarPath: './sidebars/apprendista.ts',
editUrl: 'https://github.com/marcofarina/python-doesnt-byte',
beforeDefaultRemarkPlugins: [remarkPyRunner],
beforeDefaultRemarkPlugins: [remarkPyRunner, remarkSqlRunner],
remarkPlugins: [protectCode, smartypants, restoreCode],
admonitions,
},
Expand Down
50 changes: 46 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "python-doesnt-byte",
"version": "0.3.0",
"version": "0.4.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
Expand All @@ -21,6 +21,7 @@
"dependencies": {
"@codemirror/commands": "^6.10.3",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/lang-sql": "^6.10.0",
"@codemirror/language": "^6.12.3",
"@codemirror/state": "^6.6.0",
"@codemirror/view": "^6.43.0",
Expand All @@ -37,15 +38,18 @@
"@fortawesome/react-fontawesome": "^0.2.2",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.0.0",
"copy-text-to-clipboard": "^3.2.2",
"prism-react-renderer": "^2.3.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"sql.js": "^1.14.1"
},
"devDependencies": {
"@docusaurus/eslint-plugin": "^3.10.1",
"@docusaurus/module-type-aliases": "^3.10.1",
"@docusaurus/tsconfig": "^3.10.1",
"@docusaurus/types": "^3.10.1",
"@types/sql.js": "^1.4.11",
"@typescript-eslint/eslint-plugin": "^8.48.0",
"@typescript-eslint/parser": "^8.48.0",
"eslint": "^8.57.1",
Expand Down
7 changes: 6 additions & 1 deletion plugins/pyrunner/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ const DEFAULT_OPTIONS = {

function walkPyFiles(dir, baseDir) {
const out = {};
if (!fs.existsSync(dir)) return out;
if (!fs.existsSync(dir)) {
console.warn(
`[pyrunner] Directory esempi non trovata: ${dir} — nessun file .py caricato.`,
);
return out;
}
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
Expand Down
Loading
Loading