Внешний ключ в Go
Столбец с номером чужой строки можно закрепить ограничением внешнего ключа. Оно не дает записать номер, которого нет в родительской таблице, если движок действительно проверяет ссылку.
В SQLite проверку включают командой
PRAGMA foreign_keys = ON на
открытом соединении. Без нее вставка
с несуществующим номером может пройти
тихо, и связь в данных развалится.
Создадим пользователей и заказы со
ссылкой REFERENCES users (id):
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "modernc.org/sqlite"
)
func main() {
os.Remove("shop.db")
db, err := sql.Open("sqlite", "shop.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys = ON")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`INSERT INTO users (name) VALUES (?)`, "ann")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, title) VALUES (?, ?)`,
1, "pen pack",
)
if err != nil {
log.Fatal(err)
}
fmt.Println("ok")
}
На экране "ok". Номер 1 в
user_id совпал с существующим
пользователем.
Попробуем записать заказ с чужим номером:
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "modernc.org/sqlite"
)
func main() {
os.Remove("shop.db")
db, err := sql.Open("sqlite", "shop.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys = ON")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, title) VALUES (?, ?)`,
99, "ghost",
)
if err != nil {
fmt.Println("rejected")
return
}
fmt.Println("saved")
}
Даны таблицы:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
);
INSERT INTO users (name) VALUES ('kim');
Включите проверку внешних ключей и
добавьте заказ "box" для
пользователя с номером 1.
Выведите "done" при успехе.
Даны те же таблицы и включенная проверка.
Попробуйте вставить заказ с user_id
50. При ошибке выведите "bad id".
Скажите, зачем команду PRAGMA foreign_keys = ON
нужно выполнять на каждом новом открытии
shop.db, а не один раз навсегда.