lectures: 07-sql draft
This commit is contained in:
parent
99ae445ee2
commit
3c0f8333e0
22 changed files with 369 additions and 0 deletions
3
lectures/07-sql/alive/ping.go
Normal file
3
lectures/07-sql/alive/ping.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
if err := db.Ping(ctx); err != nil {
|
||||||
|
// do something with error
|
||||||
|
}
|
7
lectures/07-sql/conn/conn.go
Normal file
7
lectures/07-sql/conn/conn.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
c, err := db.Conn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ := c.PingContext(ctx)
|
||||||
|
_, _ := c.QueryContext(...)
|
9
lectures/07-sql/exec/exec.go
Normal file
9
lectures/07-sql/exec/exec.go
Normal 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)
|
4
lectures/07-sql/exec/result.go
Normal file
4
lectures/07-sql/exec/result.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
type Result interface {
|
||||||
|
LastInsertId() (int64, error)
|
||||||
|
RowsAffected() (int64, error)
|
||||||
|
}
|
5
lectures/07-sql/named/insert.go
Normal file
5
lectures/07-sql/named/insert.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
"INSERT INTO users(name) VALUES(@name)"),
|
||||||
|
sql.Named("name", "Rocinante"),
|
||||||
|
)
|
4
lectures/07-sql/named/named.go
Normal file
4
lectures/07-sql/named/named.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
type NamedArg struct {
|
||||||
|
Name string
|
||||||
|
Value interface{}
|
||||||
|
}
|
6
lectures/07-sql/nulls/insert_null.go
Normal file
6
lectures/07-sql/nulls/insert_null.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
var name sql.NullString
|
||||||
|
db.ExecContext(
|
||||||
|
ctx,
|
||||||
|
"INSERT INTO users(name) VALUES(@name)"),
|
||||||
|
sql.Named("name", name),
|
||||||
|
)
|
6
lectures/07-sql/nulls/insert_value.go
Normal file
6
lectures/07-sql/nulls/insert_value.go
Normal 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),
|
||||||
|
)
|
4
lectures/07-sql/nulls/nulls.go
Normal file
4
lectures/07-sql/nulls/nulls.go
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
type NullString struct {
|
||||||
|
String string
|
||||||
|
Valid bool
|
||||||
|
}
|
12
lectures/07-sql/nulls/rows.go
Normal file
12
lectures/07-sql/nulls/rows.go
Normal 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 {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
7
lectures/07-sql/open/pgx.go
Normal file
7
lectures/07-sql/open/pgx.go
Normal 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")
|
11
lectures/07-sql/open/sql.go
Normal file
11
lectures/07-sql/open/sql.go
Normal 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()
|
19
lectures/07-sql/prepare/prepare.go
Normal file
19
lectures/07-sql/prepare/prepare.go
Normal 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)
|
||||||
|
}
|
19
lectures/07-sql/query/query.go
Normal file
19
lectures/07-sql/query/query.go
Normal 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)
|
||||||
|
}
|
13
lectures/07-sql/query/queryrow.go
Normal file
13
lectures/07-sql/query/queryrow.go
Normal 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
170
lectures/07-sql/sql.slide
Normal 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
|
7
lectures/07-sql/sql/driver.go
Normal file
7
lectures/07-sql/sql/driver.go
Normal 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)
|
||||||
|
}
|
21
lectures/07-sql/sqlx/example.go
Normal file
21
lectures/07-sql/sqlx/example.go
Normal 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)
|
||||||
|
}
|
14
lectures/07-sql/sqlx/ext.go
Normal file
14
lectures/07-sql/sqlx/ext.go
Normal 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
|
||||||
|
}
|
10
lectures/07-sql/sqlx/open.go
Normal file
10
lectures/07-sql/sqlx/open.go
Normal 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()
|
5
lectures/07-sql/sqlx/structscan.go
Normal file
5
lectures/07-sql/sqlx/structscan.go
Normal 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
13
lectures/07-sql/tx/tx.go
Normal 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)
|
||||||
|
}
|
Loading…
Reference in a new issue