Ga naar inhoud

SQL-koppelingen via de API

Het concept achter tools staat in Tools.

Een SQL-koppeling geeft het model één vaste query op een database van je organisatie. Jij schrijft de query bij het registreren; het model vult alleen de waarden in. Elke koppeling levert één tool op, sql__<naam>. Core maakt zelf verbinding met je databaseserver, over TLS, met een gebruikersnaam en wachtwoord die je één keer opslaat. Postgres, MySQL, SQL Server en Oracle worden ondersteund.

Een SQL-koppeling stel je in via de API, zoals hieronder, of in de console onder Tools > SQL-koppelingen. Daar voeg je er een toe op een eigen pagina, lees je de verbinding, de query en de versies van een koppeling, bewerk je hem tot een nieuwe versie, verwijder je hem, en laat je Core met Testen inloggen en SELECT 1 uitvoeren. Het wachtwoord zie je nooit terug; laat het leeg bij bewerken om het opgeslagen wachtwoord te houden. Een koppeling die uit een publicatie is geïnstalleerd, kun je daar niet bewerken of verwijderen. Je sleutel heeft de capability tooling nodig, en de host van de databaseserver moet bij de toegestane hosts van je organisatie staan.

Een koppeling registreren

curl -s -X POST "$CORE_URL/sql_connectors" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d '{"name": "orders",
         "description": "Zoekt de recente bestellingen van één klant op.",
         "dialect": "postgres",
         "host": "db.jouw-app.example",
         "database": "shop",
         "statement": "SELECT id, status, total FROM {$schema}.orders WHERE customer_id = {customer} ORDER BY created_at DESC",
         "parameters": {"type": "object",
                        "properties": {"customer": {"type": "string", "description": "Het klantnummer"}},
                        "required": ["customer"]},
         "install_values": {"schema": {"type": "identifier", "value": "public"}},
         "max_rows": 20,
         "auth": {"type": "basic", "username": "core_reader", "password": "..."}}'

Het antwoord is 201 met de koppeling, versie 1. Bied hem aan met {"type": "tool", "name": "sql__orders"} in tools[], of neem hem op in een toolset.

Veld Wat je invult
name hoe het model de tool noemt (sql__<name>): kleine letters, cijfers, - en _, maximaal 48 tekens. Wijzigt na aanmaken niet meer
description wat de tool doet, zoals het model het leest, maximaal 1024 tekens
dialect postgres, mysql, sqlserver of oracle
host de hostnaam of het adres van de server, zonder schema, poort of pad
port optioneel; standaard 5432, 3306, 1433 of 1521, naar het dialect
database de database; bij Oracle de servicenaam
statement de ene query die de tool uitvoert, zie De query
parameters een JSON Schema-object met de argumenten die het model invult
mode read (standaard) of write, zie Lezen en schrijven
max_rows hoeveel rijen één aanroep hoogstens teruggeeft: 1 tot 1.000, standaard 100
timeout seconden die één aanroep mag duren; Core neemt de kortste van deze en de tooltime-out van de installatie, 30 seconden tenzij je operator een andere instelde
tls, ca_pem hoe Core het certificaat van de server controleert, zie TLS
install_values waarden die je één keer vastlegt, zie De query
call_variables waarden die je applicatie per verzoek meestuurt, zie De query
auth {"type": "basic", "username", "password"}; een andere soort is er niet
region, labels, background, metadata zoals bij een API-koppeling, zie verderop

Het wachtwoord wordt versleuteld opgeslagen en nooit teruggegeven: GET antwoordt met de soort en de gebruikersnaam en has_secret. Geef Core een databasegebruiker die precies mag wat de query nodig heeft, en voor een leestool een gebruiker die niet kan schrijven.

GET /sql_connectors toont de koppelingen van je organisatie, GET /sql_connectors/{id} er één, PATCH wijzigt er een en DELETE verwijdert er een. Verwijderen geeft 409 met de namen van de agents zolang de nieuwste versie van een agent de tool noemt; haal hem daar eerst weg.

De query

De query ligt vast; het model kiest alleen waarden. In statement staan drie soorten plaatshouders:

Plaatshouder Wie de waarde geeft Voorbeeld
{naam} het model, als argument uit parameters WHERE customer_id = {customer}
{$naam} jij, één keer, in install_values FROM {$schema}.orders
{@naam} je applicatie, per verzoek, in tool_variables AND tenant = {@account}

Core geeft elke waarde als gebonden parameter aan de driver, nooit als stuk SQL-tekst. Een waarde in een argument kan de query dus niet veranderen, wat er ook in staat. Daarom mag een plaatshouder alleen staan waar een waarde hoort: na een operator, een komma, een haakje of een woord als WHERE, VALUES of LIMIT.

De enige uitzondering is een invulwaarde van het type identifier: de naam van een schema, een tabel of een kolom. Core schrijft die in de query, gequote volgens de regel van het dialect. Alleen jij zet zo'n naam, nooit het model. In Oracle behoudt een gequote naam zijn hoofdletters, dus schrijf hem zoals de server hem bewaart, meestal in hoofdletters.

Een invulwaarde is een object {type, value, allowed, description} met als type string, number, enum (met allowed) of identifier. Elke {$naam} heeft een waarde nodig en elke waarde moet in de query staan. Een invulwaarde is geen geheim; het wachtwoord hoort in auth.

Aanroepvariabelen declareer je in call_variables zoals bij een API-koppeling, en je applicatie stuurt hun waarden in tool_variables op het verzoek (Een modelaanroep doen). Het model ziet ze nooit. Een optionele variabele zonder waarde en zonder standaard wordt NULL; een verplichte die het verzoek weglaat, geeft 400 op tool_variables.<naam>.

Een optioneel argument dat het model weglaat, wordt ook NULL. Ontbreekt een verplicht argument, dan draait er niets en leest het model Tool arguments were missing a required value.

Core weigert bij het registreren, met 422 op statement:

  • meer dan één statement;
  • een plaatshouder in een tekstwaarde, een gequote naam of commentaar, of waar een naam hoort;
  • een plaatshouder die parameters, install_values of call_variables niet declareert;
  • een invulwaarde die de query niet gebruikt. Een aanroepvariabele die de query niet gebruikt, wordt ook geweigerd, met 422 op call_variables; een argument in parameters dat de query niet gebruikt, mag wel;
  • CREATE, ALTER, DROP, TRUNCATE, RENAME, GRANT, REVOKE, COMMIT en ROLLBACK;
  • wat afhangt van instellingen van de server: in Postgres en MySQL een backslash in een tekstwaarde, en in MySQL een -- zonder spatie erna en de commentaarvormen /*!, /*+ en /*M!.

Per dialect komt daar iets bij. SQL Server weigert een GO. Oracle weigert een puntkomma aan het eind, een PL/SQL-blok (BEGIN, DECLARE), RETURNING en een eigen bindvariabele zoals :naam.

Een argument dat een lijst is, gaat in SQL Server en Oracle mee als JSON-tekst, omdat die drivers geen lijstparameter kennen.

Lezen en schrijven

Met mode: "read" accepteert Core alleen een query die begint met SELECT of WITH (in Postgres ook VALUES of TABLE), zonder INSERT, UPDATE, DELETE, MERGE of INTO. De query draait in een alleen-lezentransactie die altijd wordt teruggedraaid. SQL Server kent geen alleen-lezentransactie: daar is de controle van de query wat een leestool tegen schrijven beschermt. Daarom weigert ze in een leesquery ook EXEC, EXECUTE, OPENROWSET, OPENQUERY en OPENDATASOURCE. Geef zo'n koppeling een gebruiker die niet kan schrijven.

Met mode: "write" mag de query ook met een van deze beginnen:

Dialect Schrijfstatements
Postgres INSERT, UPDATE, DELETE, MERGE, CALL
MySQL INSERT, UPDATE, DELETE, REPLACE, CALL
SQL Server INSERT, UPDATE, DELETE, MERGE, EXEC van een procedure
Oracle INSERT, UPDATE, DELETE, MERGE, CALL

INTO wordt overal geweigerd behalve in een INSERT, een REPLACE of een MERGE, want SELECT ... INTO maakt een tabel en MySQL's INTO OUTFILE schrijft een bestand op de server. De query draait in één transactie die wordt vastgelegd. Core probeert een aanroep nooit opnieuw. In SQL Server weigert een schrijftool dynamische SQL (EXEC (...), sp_executesql) en EXECUTE AS.

TLS

Core praat altijd versleuteld met je database; een instelling zonder TLS bestaat niet.

tls Wat Core controleert
verify-full (standaard) het certificaat van de server en of het de naam van de host draagt
require versleutelt, en controleert het certificaat alleen tegen een ca_pem die je meegeeft. Bij SQL Server controleert de driver het certificaat altijd, dus daar slaat require alleen de controle van de naam over

ca_pem is de certificaatautoriteit van je eigen server, als PEM. Laat hem weg om het certificaat te controleren tegen de autoriteiten die de installatie vertrouwt. Een MySQL-server die geen TLS aanbiedt, wordt geweigerd voordat Core inlogt.

Wat het model terugkrijgt

Het antwoord is gestructureerd: {columns, rows, row_count, truncated}, met elke rij als een lijst waarden in de volgorde van columns. Het model leest het als compacte JSON; een directe aanroep geeft het in structured.

{"columns": ["id", "status", "total"],
 "rows": [["A-1042", "shipped", "129.95"], ["A-1038", "open", "18.50"]],
 "row_count": 2, "truncated": false}
  • Een decimaal getal is tekst, zodat er geen cijfer verloren gaat; een tijd is ISO 8601; bytes zijn [binary].
  • Er komen hoogstens max_rows rijen terug, en hoogstens wat in een toolresultaat past (32 KiB). truncated: true zegt dat er rijen zijn weggelaten.
  • Een query die geen rijen teruggeeft, zoals een UPDATE, geeft in row_count het aantal gewijzigde rijen.

Mislukt een aanroep, dan leest het model één vaste zin, nooit de foutmelding van de server, want die kan de waarden van de query bevatten. De beurt gaat door.

Het model leest Wat er gebeurde Wat je kunt doen
Denied by policy. de host staat niet bij je toegestane hosts, de regels van de sleutel weigeren hem, of de installatie blokkeert de host of het adres waar hij naar verwijst (een privé- of loopback-adres, een naam als localhost of een naam op .internal) zet de host op de lijst, of maak de server bereikbaar op een adres dat de installatie niet blokkeert, zie Toegestane hosts
Tool authentication failed. de server weigerde de gebruikersnaam of het wachtwoord sla met PATCH een nieuwe auth op
Database unreachable. de server antwoordde niet, of de TLS-controle mislukte controleer host, poort, tls en ca_pem met de test
Database query timed out. de query duurde langer dan de timeout maak de query sneller of de timeout ruimer
Database refused the query. de server weigerde de query, of een schrijfquery gaf meer terug dan Core mag lezen probeer de query zelf op de server met dezelfde gebruiker

Het auditspoor en de trace van de beurt noemen de soort fout en de foutcode van de server (de SQLSTATE-klasse of het foutnummer), niet de melding zelf.

Een koppeling testen

POST /sql_connectors/{id}/test logt in en voert SELECT 1 uit, onder dezelfde regels voor hosts als een aanroep. Er wordt niets opgeslagen.

curl -s -X POST "$CORE_URL/sql_connectors/sqlc_01J.../test" \
    -H "Authorization: Bearer $CORE_KEY"

Het antwoord is altijd 200:

{"object": "sql_connector_test", "ok": true, "code": null}

Bij een mislukking is ok false en zegt code waarom: egress_denied (de host of zijn adres is niet toegestaan), unreachable, auth_failed, timeout of refused.

Toegestane hosts

Core bereikt je database alleen als de hostnaam bij de toegestane hosts van je organisatie staat, dezelfde lijst als voor een API-koppeling (Toegestane bestemmingen). Het veld egress op elke read zegt per host of hij is toegestaan (allowed) en of de installatie hem blokkeert (blocked).

De installatie blokkeert een adres in een privénetwerk, een loopback-adres en een naam die alleen binnen een netwerk bestaat, zoals localhost of een naam op .internal. Je databaseserver moet dus bereikbaar zijn op een adres dat Core mag bellen. Stuurt de server Core door naar een andere host, zoals een Azure SQL-gateway of een Oracle-listener doet, dan moet die host ook zijn toegestaan.

region zegt waar de server draait, zoals bij een API-koppeling (Waar de host draait).

Versies

Een koppeling is geversioneerd. Een PATCH die wijzigt wat de tool doet (de query, de argumenten, de server, de modus, de grenzen, de invulwaarden, de aanroepvariabelen of background) maakt een nieuwe versie. Een PATCH van alleen auth, region, labels of metadata maakt er geen.

GET /sql_connectors/{id}/versions toont de versies, nieuwste eerst (?order=asc voor oudste eerst), en GET /sql_connectors/{id}/versions/{version} er één. Pin een versie op een verzoek:

{ "type": "tool", "name": "sql__orders", "version": 2 }

Zonder version krijgt een beurt de nieuwste (Een versie vastpinnen).

Op de achtergrond, labels en een afbeelding

  • Achtergrond. background bepaalt of het model de tool als taak mag starten, met dezelfde velden als bij een API-koppeling (Een tool op de achtergrond draaien). Het hoort bij de versie.
  • Labels. labels is de eigen indeling van je organisatie, waarover regels voor personen worden geschreven (Labels op alles). Voor een verzoek dat een persoon noemt, beslissen dezelfde regels over sql__<naam> of GET /sql_connectors de koppeling toont en of ze gewijzigd of verwijderd mag worden (Registraties).
  • Afbeelding. PUT /tools/sql__orders/cover zet een afbeelding bij de tool die de console naast de naam toont: PNG, JPEG of WebP als ruwe body, hoogstens 512 KiB tenzij je operator een andere grens instelde. DELETE haalt hem weg. Het model ziet de afbeelding nooit.

Delen in een publicatie

Een SQL-koppeling kan mee in een publicatie, als lid van de soort sql_connector. Zeg net als bij een API-koppeling wat er met de inloggegevens gebeurt:

{"kind": "sql_connector", "id": "sqlc_01J...", "auth": "parameter", "auth_parameter": "database_login"}
auth Wat er gebeurt
parameter elke organisatie die installeert, vult een eigen gebruikersnaam en wachtwoord in
carried je eigen gegevens reizen versleuteld mee; alleen bij een publicatie aan organisaties onder je
none er gaan geen gegevens mee, en de geïnstalleerde kopie heeft er geen

Elke invulwaarde wordt een parameter van de bundel, met jouw waarde als standaard. Wie installeert, vult dus een eigen schemanaam of waarde in, en Core controleert de query daarmee opnieuw bij het installeren. Een kopie die met auth: "none" is geïnstalleerd, kan niet inloggen: een verzoek dat haar tool aanbiedt, antwoordt 503 met The SQL connector's stored credential could not be read. Kies parameter als elke installeerder een eigen login moet gebruiken. Een installatie onder de voorwaarde Inhoud verbergen toont de query, de host, de database en de argumenten als null; de tool werkt gewoon. Een kopie uit een publicatie wijzig je niet, ook de afbeelding niet: zo'n PUT of DELETE geeft 409.

Fouten bij het registreren

Status Wanneer
409 er is al een SQL-koppeling met deze naam; of bij DELETE noemt een agent de tool nog
422 op statement de query mag niet, zie De query
422 op call_variables een declaratie die de query niet gebruikt, of een naam die ook een argument of invulwaarde is
422 op ca_pem geen PEM, of geen certificaat dat de server kan laden
503 de installatie heeft geen sleutel om het wachtwoord mee te versleutelen; vraag het je operator

Elke route heeft een tweeling onder /tenants/{tenant_id}/sql_connectors voor een beheerder die met een beheerderstoken werkt, met dezelfde velden en antwoorden.