Lässt es sich mit cremefarbenem Papier, einer Tönung, mehr Kontrast oder größerer Schrift besser lesen? Wählen Sie Ihre Farben und Ihre Schrift, einmalig für alle Seiten.

Kostenlose Lektion

Modell-SQL nur lesend und mit Grenzen ausführen

Nach dieser Lektion: Ich kann die Abfrage eines Modells so ausführen, dass sie nur lesen kann, und zwar wenig auf einmal.

Lektion, 70 Min.11 BefehleLab für KI-Coding-Agenten: mit opencode Code erklären und reparieren lassen und die Arbeit prüfen

Warum Modell-SQL nur lesen darf

Modell-SQL sicher auszuführen heißt, dass es lesen und nie etwas ändern kann. Ein Modell könnte DELETE oder DROP aus Versehen schreiben, oder weil eine Frage es hereingelegt hat.

Modul 4 lehrt, Daten absichtlich zu ändern. Diese Lektion stellt drei Schlösser vor die Abfrage eines Modells: einen Wächter, Limits und eine Verbindung, die nur lesen kann.

Am Ende dieser Lektion

Notiz

Ich kann die Abfrage eines Modells so ausführen, dass sie nur lesen kann, und zwar wenig auf einmal.

Schlüsselwörter

BegriffWas er bedeutetBeispiel
WächterCode, der eine Abfrage vor dem Ausführen ablehnt
Anweisungein vollständiger SQL-BefehlSELECT titel FROM buecher
Zeilenlimitdie höchste Zahl Zeilen, die zurückkommen darf
Zeitlimitdie längste Zeit, die eine Abfrage laufen darf
Nur lesenddarf Zeilen lesen, aber nie ändern

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Genau ein SELECT erlauben, sonst nichts

Der Wächter räumt die Abfrage auf und streicht ein Semikolon am Schluss. Bleibt ein weiteres Semikolon übrig, gibt es eine zweite Anweisung, also lehnt er ab.

Das erste Wort muss SELECT oder WITH sein. Jedes schreibende Wort wie DELETE, DROP oder UPDATE wird abgelehnt, wo immer es steht. Ebenso ein Kommentar, der Text verstecken kann.

Beispiel: was der Wächter sagt

AbfrageUrteilGrund
SELECT titel FROM buecher WHERE jahr > 2020ErlaubtEin SELECT
SELECT titel FROM buecher; DROP TABLE ausleihenAbgelehntMehr als eine Anweisung
DELETE FROM ausleihen WHERE zurueck = 1AbgelehntKein SELECT
SELECT titel FROM buecher -- NotizAbgelehntEin Kommentar

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Ein Zeilenlimit und ein Zeitlimit

Auch eine richtige Abfrage kann eine Million Zeilen liefern oder endlos laufen. Packen Sie sie in SELECT * FROM (die Abfrage) LIMIT 5, dann kommen höchstens fünf Zeilen zurück.

Führen Sie sie in einem eigenen Prozess mit Zeitlimit aus. Läuft die Zeit ab, wird der Prozess gestoppt, und die Seite antwortet, statt zu hängen.

Lesender Zugriff ist das letzte Schloss

Ein Wächter prüft Wörter, und Wörter können durchrutschen. Öffnen Sie die Datenbank deshalb nur lesend, oder melden Sie sich als Benutzer an, der nur SELECT darf.

Führen Sie Modell-SQL nie mit Schreibrechten auf Live-Daten aus. Nutzen Sie eine Kopie, die nur lesbar ist, dann ist das Schlimmste, was eine schlechte Abfrage tun kann, langsam zu sein.

Ein häufiger Fehler

Warnung

Viele denken, ein Wächter, der die Wörter prüft, genüge. Ein Wächter hat Lücken, weil SQL viele Wege kennt, dasselbe zu sagen. Behalten Sie auch die Verbindung, die nur lesen kann, damit eine übersehene Abfrage trotzdem nicht schreibt.

Die Modellhilfe schreiben, Teil 1

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const basis = process.env.AI_URL || \"http://ai:4000\";' '// Die Token einer Antwort oder null, wenn sie keine nennt.' 'function zaehlen(daten, name) {' '  return daten.usage && typeof daten.usage[name] === \"number\" ? daten.usage[name] : null;' '}' '// Eine Antwort kann die Abfrage in einen umzäunten Block setzen: Wir behalten das Innere.' 'function sqlVon(text) {' '  const teile = text.split(String.fromCharCode(96).repeat(3));' '  const innen = (teile.length > 2 ? teile[1] : text).trim();' '  return innen.toLowerCase().startsWith(\"sql\") ? innen.slice(3).trim() : innen;' '}' > model.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Die Modellhilfe schreiben, Teil 2

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function fragen(nachrichten, probe, max) {' '  try {' '    const res = await fetch(basis + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: nachrichten, max_tokens: max || 100 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"Status \" + res.status);' '    const d = await res.json();' '    return { text: d.choices[0].message.content, eingabe: zaehlen(d, \"prompt_tokens\"), ausgabe: zaehlen(d, \"completion_tokens\"), aufgezeichnet: false };' '  } catch (fehler) {' '    console.log(\"Aufgezeichnete Probe: Kein Modell hat geantwortet (\" + fehler.message + \").\");' '    return { text: probe, eingabe: null, ausgabe: null, aufgezeichnet: true };' '  }' '}' 'module.exports = { fragen, sqlVon };' >> model.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Die kleine Bibliotheksdatenbank schreiben, Teil 1

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { DatabaseSync } = require(\"node:sqlite\");' '// Eine erfundene Bibliothek: sechs Bücher, vier Mitglieder, neun Ausleihen.' 'const BUECHER = [[1, \"Der Salzweg\", 2019], [2, \"Hafen aus Glas\", 2021], [3, \"Eine Karte aus Regen\", 2015],' '  [4, \"Leise Maschinen\", 2023], [5, \"Sieben Kessel\", 2018], [6, \"Nördlich vom Mittag\", 2022]];' 'const MITGLIEDER = [[1, \"Anke\"], [2, \"Bernd\"], [3, \"Carla\"], [4, \"Dirk\"]];' '// id, buch_id, mitglied_id, monat, zurueck (1 zurück, 0 noch draußen)' 'const AUSLEIHEN = [[1, 1, 1, 3, 1], [2, 2, 1, 3, 1], [3, 3, 2, 3, 0], [4, 1, 3, 3, 1], [5, 1, 1, 4, 0],' '  [6, 4, 2, 4, 0], [7, 2, 3, 4, 1], [8, 5, 2, 5, 0], [9, 2, 4, 5, 0]];' > bibliothek.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Die kleine Bibliotheksdatenbank schreiben, Teil 2

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function fuellen(db, tabelle, zeilen) {' '  const marken = zeilen[0].map(function () { return \"?\"; }).join(\", \");' '  const einfuegen = db.prepare(\"INSERT INTO \" + tabelle + \" VALUES (\" + marken + \")\");' '  zeilen.forEach(function (zeile) { einfuegen.run(...zeile); });' '}' 'function oeffnen(datei) {' '  const db = new DatabaseSync(datei || \":memory:\");' '  db.exec(\"CREATE TABLE buecher (id INTEGER PRIMARY KEY, titel TEXT, jahr INTEGER)\");' '  db.exec(\"CREATE TABLE mitglieder (id INTEGER PRIMARY KEY, name TEXT)\");' '  db.exec(\"CREATE TABLE ausleihen (id INTEGER PRIMARY KEY, buch_id INTEGER REFERENCES buecher(id), \"' '    + \"mitglied_id INTEGER REFERENCES mitglieder(id), monat INTEGER, zurueck INTEGER)\");' '  fuellen(db, \"buecher\", BUECHER);' '  fuellen(db, \"mitglieder\", MITGLIEDER);' '  fuellen(db, \"ausleihen\", AUSLEIHEN);' '  return db;' '}' 'module.exports = { oeffnen };' >> bibliothek.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Den nur lesenden Ausführer schreiben

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { DatabaseSync } = require(\"node:sqlite\");' '// Nur lesend geöffnet: Auch eine Abfrage, die der Wächter übersehen hat, kann keine Zeile ändern.' 'const db = new DatabaseSync(\"bibliothek.db\", { readOnly: true });' 'try {' '  const zeilen = db.prepare(process.argv[2]).all().map(function (r) { return Object.values(r); });' '  console.log(\"Zeilen \" + zeilen.length + \": \" + JSON.stringify(zeilen));' '} catch (fehler) { console.log(\"Fehler: \" + fehler.message); }' > einzeln.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Das Skript mit dem Wächter schreiben, Teil 1

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { fragen, sqlVon } = require(\"./model\");' 'const { oeffnen } = require(\"./bibliothek\");' 'const fs = require(\"node:fs\");' 'const { spawnSync } = require(\"node:child_process\");' 'fs.rmSync(\"bibliothek.db\", { force: true });' 'oeffnen(\"bibliothek.db\").close();' 'const VERBOTEN = [\"INSERT\", \"UPDATE\", \"DELETE\", \"DROP\", \"ALTER\", \"CREATE\", \"ATTACH\", \"PRAGMA\", \"REPLACE\"];' 'const LIMIT = 5;' > waechter.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Das Skript mit dem Wächter schreiben, Teil 2

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function waechter(sql) {' '  let text = sql.trim();' '  if (text.endsWith(\";\")) text = text.slice(0, -1).trim();' '  if (text.indexOf(\";\") >= 0) return { ok: false, grund: \"mehr als eine Anweisung\" };' '  if (text.indexOf(\"--\") >= 0 || text.indexOf(\"/*\") >= 0) return { ok: false, grund: \"ein Kommentar\" };' '  const woerter = text.toUpperCase().split(/[^A-Z_]+/).filter(function (w) { return w.length > 0; });' '  if ([\"SELECT\", \"WITH\"].indexOf(woerter[0]) < 0) return { ok: false, grund: \"kein SELECT\" };' '  const boese = woerter.filter(function (w) { return VERBOTEN.indexOf(w) >= 0; });' '  if (boese.length) return { ok: false, grund: \"verbotenes Wort \" + boese[0] };' '  return { ok: true, sql: \"SELECT * FROM (\" + text + \") LIMIT \" + LIMIT };' '}' >> waechter.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Das Skript mit dem Wächter schreiben, Teil 3

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function einzeln(sql) {' '  const aus = spawnSync(process.execPath, [\"--experimental-sqlite\", \"--no-warnings\", \"einzeln.js\", sql],' '    { encoding: \"utf8\", timeout: 2000 });' '  return aus.error ? \"gestoppt: über 2 Sekunden\" : aus.stdout.trim();' '}' 'function ausfuehren(sql) {' '  const w = waechter(sql);' '  return w.ok ? einzeln(w.sql) : \"abgelehnt: \" + w.grund;' '}' 'const N = \"WITH RECURSIVE n(i) AS (SELECT 1 UNION ALL SELECT i + 1 FROM n\";' >> waechter.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Das Skript mit dem Wächter schreiben, Teil 4

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const versuche = [\"SELECT titel FROM buecher WHERE jahr > 2020\", \"SELECT titel FROM buecher; DROP TABLE ausleihen\",' '    \"DELETE FROM ausleihen WHERE zurueck = 1\", N + \") SELECT COUNT(*) FROM n\",' '    N + \" WHERE i < 1000) SELECT i FROM n\"];' '  versuche.forEach(function (sql, i) { console.log((i + 1) + \" \" + ausfuehren(sql)); });' '  console.log(\"6 ohne Wächter, Datei nur lesend: \" + einzeln(\"DELETE FROM ausleihen\"));' '  await live();' '}' >> waechter.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Das Skript mit dem Wächter schreiben, Teil 5

Befehl
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function live() {' '  const f = \"buecher(id, titel, jahr)\nFrage: Welche Bücher sind nach 2020 erschienen?\";' '  const probe = \"SELECT titel FROM buecher WHERE jahr > 2020;\";' '  const antwort = await fragen([{ role: \"system\", content: \"Antworten Sie mit genau einer SQLite-SELECT-Abfrage und sonst nichts.\" },' '    { role: \"user\", content: f }], probe, 120);' '  const sql = sqlVon(antwort.text);' '  console.log(\"Das Modell schrieb: \" + sql);' '  console.log(\"  durch den Wächter: \" + ausfuehren(sql));' '}' 'main();' >> waechter.js"

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Sechs Abfragen durch die Schlösser schicken

Befehl
docker exec -w /home/node/project agent node --experimental-sqlite --no-warnings waechter.js

Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.

Was der Run ausgibt

ZeileWas sie zeigt
1 Zeilen 3: [["Hafen aus Glas"],["Leise Maschinen"],["Nördlich vom Mittag"]]Eine erlaubte Abfrage
2 abgelehnt: mehr als eine AnweisungEin verstecktes DROP
3 abgelehnt: kein SELECTEin Schreibversuch
4 gestoppt: über 2 SekundenEine Abfrage, die nie endet
5 Zeilen 5: [[1],[2],[3],[4],[5]]1.000 Zeilen auf fünf gekürzt
6 ohne Wächter, Datei nur lesend: Fehler: attempt to write a readonly databaseDas letzte Schloss hält

Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.

Über dieses Lab und seinen Quellcode: Lab für KI-Coding-Agenten: mit opencode Code erklären und reparieren lassen und die Arbeit prüfen

Sie unterrichten eine Gruppe? Weisen Sie dieses Modul Ihren Studierenden zu, mit Handbuch für Lehrende, automatisch geprüften Aufgaben und einem Lab für jede Person.