lectures: 07-sql draft

This commit is contained in:
Ilya Sinelnikov 2020-04-08 22:20:23 +03:00
parent 99ae445ee2
commit 3c0f8333e0
22 changed files with 369 additions and 0 deletions

View file

@ -0,0 +1,3 @@
if err := db.Ping(ctx); err != nil {
// do something with error
}

View file

@ -0,0 +1,7 @@
c, err := db.Conn(ctx)
if err != nil {
log.Fatal(err)
}
_ := c.PingContext(ctx)
_, _ := c.QueryContext(...)

View file

@ -0,0 +1,9 @@
res, err := db.ExecContext(ctx, "UPDATE users SET name = $1 WHERE id = $2", "William Mandella", 1)
if err != nil {
log.Fatal(err)
}
lastID, _ := res.LastInsertId()
rowsAffeced, := res.RowsAffected()
log.Println(lastID, rowsAffected)

View file

@ -0,0 +1,4 @@
type Result interface {
LastInsertId() (int64, error)
RowsAffected() (int64, error)
}

View file

@ -0,0 +1,5 @@
db.ExecContext(
ctx,
"INSERT INTO users(name) VALUES(@name)"),
sql.Named("name", "Rocinante"),
)

View file

@ -0,0 +1,4 @@
type NamedArg struct {
Name string
Value interface{}
}

View file

@ -0,0 +1,6 @@
var name sql.NullString
db.ExecContext(
ctx,
"INSERT INTO users(name) VALUES(@name)"),
sql.Named("name", name),
)

View file

@ -0,0 +1,6 @@
name := sql.NullString{Value: "The Shrike",Valid: true}
db.ExecContext(
ctx,
"INSERT INTO users(name) VALUES(@name)"),
sql.Named("name", name),
)

View file

@ -0,0 +1,4 @@
type NullString struct {
String string
Valid bool
}

View file

@ -0,0 +1,12 @@
for rows.Next() {
var s sql.NullString
if err := rows.Scan(&s); err != nil {
log.Fatal(err)
}
if s.Valid {
//
} else {
//
}
}

View file

@ -0,0 +1,7 @@
import (
"context"
"github.com/jackc/pgx/v4"
)
conn, err := pgx.Connect(context.Background(), "postgres://pgx_md5:secret@localhost:5432/pgx_test")

View file

@ -0,0 +1,11 @@
import (
"database/sql"
_ "github.com/jackc/pgx/v4/stdlib"
)
db, err := sql.Open("pgx", "postgres://pgx_md5:secret@localhost:5432/pgx_test")
if err != nil {
return err
}
defer db.Close()

View file

@ -0,0 +1,19 @@
stmt, err := db.PrepareContext(ctx, "SELECT name FROM users WHERE id = $1")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
for i := 1; ; i++ {
row, err := stmt.QueryRowContext(ctx, i)
if err != nil {
log.Fatal(err)
}
var name string
if err = row.Scan(&name); err != nil {
log.Fatal(err)
}
log.Println(name)
}

View file

@ -0,0 +1,19 @@
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users WHERE id = $1", 1)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Fatal(err)
}
log.Println(id, name)
}
if err = rows.Err(); err != nil {
log.Fatal(err)
}

View file

@ -0,0 +1,13 @@
var id int
var name string
err = db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE id = $1", 1).Scan(&id, &name);
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
log.Println("nothing found")
return
}
log.Fatal(err)
}
log.Println(name)

170
lectures/07-sql/sql.slide Normal file
View file

@ -0,0 +1,170 @@
database/sql
Лекция 7
Синельников Илья
* Способы работы с базами данных
- database/sql - любые SQL базы данных
- всё остальное
* database/sql
- абстракция для работы с SQL базами данных
- набор интерфейсов для написания драйверов
- многие драйверы имеют "расширенный" функционал
.code sql/driver.go /^type Driver/,/^}/
.code sql/driver.go /^type QueryerContext/,/^}/
* Подключение
- через database/sql
.code open/sql.go
- драйвером на прямую
.code open/pgx.go
* Основные сущности
- DB - connection pool к базе
- Rows - результаты запроса
- Row - одна строка из результата запроса
- Null* - типы для работы с Null значениями
- Tx - транзакция
- Stmt - подготовленый запрос
- Conn - одно подключение к базе
* Проверка доступности
.code alive/ping.go
* Запрос с получением результатов
.code query/query.go
* Запрос с получением одного результата
.code query/queryrow.go
* Запрос без результатов
.code exec/exec.go
Возвращаемый тип
.code exec/result.go
* Nulls
.code nulls/nulls.go
* Null в аргументах
Передаём null
.code nulls/insert_null.go
Передаём значение
.code nulls/insert_value.go
* Null в результатах
.code nulls/rows.go
* Именованые аргументы
- реализуются на уровне драйвера
.code named/named.go
* Именованые аргументы - использование
.code named/insert.go
* Prepared Statements
Плюсы:
- решают проблему sql-injection
- производительность
Минусы:
- удобство
- производительность
* Prepare
.code prepare/prepare.go
* Одно подключение
.code conn/conn.go
* Транзакции
.code tx/tx.go
* Типовые ошибки и подводные камни
* Deadlock
- ПРИМЕР много SQL вызовов подряд без вычитки данных
* Исчерпание ресурсов
- ПРИМЕР незакрытие транзакций
- ПРИМЕР незакрытие rows
- ПРИМЕР неотдача коннектов
* Рефлексия
ПРИМЕР ColumnType
* Удобства и расширения
* github.com/jmoiron/sqlx
- drop-in replacement для database/sql
- добавляет множество вспомогательных методов
- реализует именованые аргументы
* sqlx - подключение
.code sqlx/open.go
* sqlx - обобщенный интерфейс
.code sqlx/ext.go
* sqlx - StructScan
.code sqlx/structscan.go
* sqlx - StructScan на практике
.code sqlx/example.go
* GORM
- ORM которая умеет всё
- ORM которая не умеет ничего
* Моки
Здесь будет пара слайдов о github.com/DATA-DOG/go-sqlmock
* Нетипичные драйверы
- драйвер к кликхаусу использует транзакции как батчинг (ЗДЕСЬ БУДЕТ ПРИМЕР)
* Пример не-sql драйвера
ПРИМЕР go-redis
* Популярные базы данных и их драйверы
Совместимые с database/sql:
.link https://github.com/jackc/pgx - PostgreSQL
.link https://github.com/go-sql-driver/mysql - MySQL
.link https://github.com/ClickHouse/clickhouse-go - ClickHouse
Разное:
.link https://github.com/go-redis/redis - Redis
.link https://github.com/mongodb/mongo-go-driver - MongoDB

View file

@ -0,0 +1,7 @@
type Driver interface {
Open(name string) (Conn, error)
}
type QueryerContext interface {
QueryContext(ctx context.Context, query string, args []NamedValue) (Rows, error)
}

View file

@ -0,0 +1,21 @@
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users WHERE id = $1", 1)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var value struct {
ID int `db:"id"`
Name string `db:"name"`
}
if err := rows.StructScan(&value); err != nil {
log.Fatal(err)
}
log.Println(value)
}
if err = rows.Err(); err != nil {
log.Fatal(err)
}

View file

@ -0,0 +1,14 @@
type QueryerContext interface {
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryxContext(ctx context.Context, query string, args ...interface{}) (*Rows, error)
QueryRowxContext(ctx context.Context, query string, args ...interface{}) *Row
}
type ExecerContext interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
}
type ExtContext interface {
QueryerContext
ExecerContext
}

View file

@ -0,0 +1,10 @@
import (
"github.com/jmoiron/sqlx"
_ "github.com/jackc/pgx/v4/stdlib"
)
db, err := sqlx.Open("pgx", "postgres://pgx_md5:secret@localhost:5432/pgx_test")
if err != nil {
return err
}
defer db.Close()

View file

@ -0,0 +1,5 @@
func StructScan(rows rowsi, dest interface{}) error
func (r *Rows) StructScan(dest interface{}) error
func (r *Row) StructScan(dest interface{}) error

13
lectures/07-sql/tx/tx.go Normal file
View file

@ -0,0 +1,13 @@
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Fatal(err)
}
defer tx.Rollback()
if _, err = tx.ExecContext(ctx, ...); err != nil {
log.Fatal(err)
}
if err = tx.Commit(); err != nil {
log.Fatal(err)
}