Compare commits

..

4 Commits
main ... sqlc

Author SHA1 Message Date
dd49a1e687 Finish adding sqlc and fix some bugs 2024-08-01 16:49:59 +02:00
459a4e5c7d 1. change db to sqlc, 2.use repo for db drivers 2024-08-01 10:04:50 +02:00
cec183b416 finish pgc impl 2024-07-31 22:15:37 +02:00
f20f256313 Use sqlc 2024-07-30 22:02:26 +02:00
15 changed files with 1650 additions and 22 deletions

View File

@ -5,11 +5,12 @@ import (
"flag"
"fmt"
"go-udemy-web-1/internal/config"
"go-udemy-web-1/internal/driver"
"go-udemy-web-1/internal/handlers"
"go-udemy-web-1/internal/helpers"
"go-udemy-web-1/internal/models"
"go-udemy-web-1/internal/render"
"go-udemy-web-1/internal/repository"
"go-udemy-web-1/internal/repository/driverrepo"
"log"
"net/http"
"os"
@ -50,7 +51,7 @@ func main() {
log.Fatal(err)
}
func run() (*driver.DB, error) {
func run() (*repository.DB, error) {
// what am I going to put in the session
gob.Register(models.Reservation{})
gob.Register(models.User{})
@ -103,7 +104,8 @@ func run() (*driver.DB, error) {
log.Println("Connecting to database...")
dsn := fmt.Sprintf("host=%s port=%s dbname=%s user=%s password=%s sslmode=%s",
*dbHost, *dbPort, *dbName, *dbUser, *dbPass, *dbSSL)
db, err := driver.ConnectSQL(dsn)
dbdriver := driverrepo.NewSqlcRepo()
db, err := dbdriver.ConnectSQL(dsn)
if err != nil {
log.Fatal("Cannot connect to database! Dying...")
}

View File

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"go-udemy-web-1/internal/config"
"go-udemy-web-1/internal/driver"
"go-udemy-web-1/internal/forms"
"go-udemy-web-1/internal/helpers"
"go-udemy-web-1/internal/models"
@ -30,10 +29,10 @@ type Repository struct {
}
// NewRepo creates a new repository
func NewRepo(a *config.AppConfig, db *driver.DB) *Repository {
func NewRepo(a *config.AppConfig, db *repository.DB) *Repository {
return &Repository{
App: a,
DB: dbrepo.NewPostgresRepo(db.SQL, a),
DB: dbrepo.NewPgcRepo(db.PG, a),
}
}

View File

@ -3,8 +3,8 @@ package handlers
import (
"context"
"encoding/json"
"go-udemy-web-1/internal/driver"
"go-udemy-web-1/internal/models"
"go-udemy-web-1/internal/repository"
"log"
"net/http"
"net/http/httptest"
@ -24,7 +24,7 @@ type postData struct {
// {{{ Test NewRepo
func Test_NewRepo(t *testing.T) {
var db driver.DB
var db repository.DB
repo := NewRepo(&app, &db)
if reflect.TypeOf(repo).String() != "*handlers.Repository" {

View File

@ -4,6 +4,9 @@ import (
"database/sql"
"go-udemy-web-1/internal/config"
"go-udemy-web-1/internal/repository"
"go-udemy-web-1/internal/repository/sqlc"
"github.com/jackc/pgx/v5"
)
type postgresDBRepo struct {
@ -11,6 +14,11 @@ type postgresDBRepo struct {
DB *sql.DB
}
type pgcDBRepo struct {
App *config.AppConfig
Q *sqlc.Queries
}
type testDBRepo struct {
App *config.AppConfig
DB *sql.DB
@ -28,3 +36,11 @@ func NewTestingRepo(a *config.AppConfig) repository.DatabaseRepo {
App: a,
}
}
func NewPgcRepo(conn *pgx.Conn, a *config.AppConfig) repository.DatabaseRepo {
q := sqlc.New(conn)
return &pgcDBRepo{
App: a,
Q: q,
}
}

View File

@ -0,0 +1,416 @@
package dbrepo
import (
"context"
"errors"
"go-udemy-web-1/internal/models"
"go-udemy-web-1/internal/repository/sqlc"
"time"
"github.com/jackc/pgx/v5/pgtype"
"golang.org/x/crypto/bcrypt"
)
func (m *pgcDBRepo) AllUsers() bool {
return true
}
// InsertReservation inserts a reservation into the database
func (m *pgcDBRepo) InsertReservation(res models.Reservation) (int, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var newId int32
newId, err := m.Q.InsertReservation(ctx, sqlc.InsertReservationParams{
FirstName: res.FirstName,
LastName: res.LastName,
Email: res.Email,
Phone: res.Phone,
StartDate: pgtype.Date{Time: res.StartDate, Valid: true},
EndDate: pgtype.Date{Time: res.EndDate, Valid: true},
RoomID: int32(res.RoomID),
CreatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
})
if err != nil {
m.App.ErrorLog.Println(err)
return 0, err
}
return int(newId), nil
}
// InsertRoomRestriction inserts a room restriction into the database
func (m *pgcDBRepo) InsertRoomRestriction(r models.RoomRestriction) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.InsertRoomRestriction(ctx, sqlc.InsertRoomRestrictionParams{
StartDate: pgtype.Date{Time: r.StartDate, Valid: true},
EndDate: pgtype.Date{Time: r.EndDate, Valid: true},
RoomID: int32(r.RoomID),
ReservationID: pgtype.Int4{Int32: int32(r.ReservationID), Valid: true},
RestrictionID: int32(r.RestrictionID),
CreatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
})
if err != nil {
m.App.ErrorLog.Println(err)
return err
}
return nil
}
// SearchAvailabilityByDatesByRoomID returns true if availability exists for roomID, and false if no availability
func (m *pgcDBRepo) SearchAvailabilityByDatesByRoomID(start, end time.Time, roomID int) (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
numRows, err := m.Q.SearchAvailabilityByDatesByRoomID(ctx, sqlc.SearchAvailabilityByDatesByRoomIDParams{
RoomID: int32(roomID),
EndDate: pgtype.Date{Time: start, Valid: true},
StartDate: pgtype.Date{Time: end, Valid: false},
})
if err != nil {
return false, err
}
if numRows == 0 {
return false, nil
}
return true, nil
}
// SearchAvailabilityForAllRooms returns a slice of rooms, if any, for given date range
func (m *pgcDBRepo) SearchAvailabilityForAllRooms(start, end time.Time) ([]models.Room, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var rooms []models.Room
rows, err := m.Q.SearchAvailabilityForAllRooms(ctx, sqlc.SearchAvailabilityForAllRoomsParams{
EndDate: pgtype.Date{Time: start, Valid: true},
StartDate: pgtype.Date{Time: start, Valid: true},
})
if err != nil {
return nil, err
}
for _, row := range rows {
room := models.Room{
RoomName: row.RoomName,
ID: int(row.ID),
}
rooms = append(rooms, room)
}
return rooms, nil
}
// GetRoomById gets a room by id
func (m *pgcDBRepo) GetRoomById(id int) (models.Room, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var room models.Room
row, err := m.Q.GetRoomById(ctx, int32(id))
if err != nil {
return room, err
}
room = models.Room{
ID: int(row.ID),
RoomName: row.RoomName,
}
return room, nil
}
// GetUserByID gets a user by id
func (m *pgcDBRepo) GetUserByID(id int) (models.User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var u models.User
row, err := m.Q.GetUserByID(ctx, int32(id))
if err != nil {
return u, err
}
u = models.User{
ID: int(row.ID),
FirstName: row.FirstName,
LastName: row.LastName,
Email: row.Email,
Password: row.Password,
AccessLevel: int(row.AccessLevel),
}
return u, nil
}
// UpdateUser updates a user in the database
func (m *pgcDBRepo) UpdateUser(u models.User) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.UpdateUser(ctx, sqlc.UpdateUserParams{
FirstName: u.FirstName,
LastName: u.LastName,
Email: u.Email,
AccessLevel: int32(u.AccessLevel),
UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
})
if err != nil {
return err
}
return nil
}
// Authenticate authenticates a user
func (m *pgcDBRepo) Authenticate(email, testPassword string) (int, string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var id int
var hashedPassword string
row, err := m.Q.GetUserCred(ctx, email)
if err != nil {
return id, "", err
}
id = int(row.ID)
hashedPassword = row.Password
err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(testPassword))
if err == bcrypt.ErrMismatchedHashAndPassword {
return 0, "", errors.New("incorrect password")
} else if err != nil {
return 0, "", err
}
return id, hashedPassword, nil
}
// AllReservations returns a slice of all reservations
func (m *pgcDBRepo) AllReservations() ([]models.Reservation, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var reservations []models.Reservation
rows, err := m.Q.AllReservations(ctx)
if err != nil {
return nil, err
}
for _, row := range rows {
r := models.Reservation{
StartDate: row.StartDate.Time,
EndDate: row.EndDate.Time,
FirstName: row.FirstName,
LastName: row.LastName,
Email: row.Email,
Phone: row.Phone,
Room: models.Room{ID: int(row.RoomID), RoomName: row.RoomName.String},
ID: int(row.ID),
RoomID: int(row.RoomID),
Processed: int(row.Processed),
}
reservations = append(reservations, r)
}
return reservations, nil
}
// AllNewReservations returns a slice of all new reservations
func (m *pgcDBRepo) AllNewReservations() ([]models.Reservation, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var reservations []models.Reservation
rows, err := m.Q.AllNewReservations(ctx)
if err != nil {
return nil, err
}
for _, row := range rows {
r := models.Reservation{
StartDate: row.StartDate.Time,
EndDate: row.EndDate.Time,
FirstName: row.FirstName,
LastName: row.LastName,
Email: row.Email,
Phone: row.Phone,
Room: models.Room{ID: int(row.RoomID), RoomName: row.RoomName.String},
ID: int(row.ID),
RoomID: int(row.RoomID),
Processed: int(row.Processed),
}
reservations = append(reservations, r)
}
return reservations, nil
}
// GetReservationByID returns one reservation by ID
func (m *pgcDBRepo) GetReservationByID(id int) (models.Reservation, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var res models.Reservation
row, err := m.Q.GetReservationByID(ctx, int32(id))
if err != nil {
return res, err
}
res = models.Reservation{
StartDate: row.StartDate.Time,
EndDate: row.EndDate.Time,
FirstName: row.FirstName,
LastName: row.LastName,
Email: row.Email,
Phone: row.Phone,
Room: models.Room{ID: int(row.RoomID), RoomName: row.RoomName.String},
ID: int(row.ID),
RoomID: int(row.RoomID),
Processed: int(row.Processed),
}
return res, nil
}
// UpdateReservation updates a user in the database
func (m *pgcDBRepo) UpdateReservation(r models.Reservation) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.UpdateReservation(ctx, sqlc.UpdateReservationParams{
ID: int32(r.ID),
FirstName: r.FirstName,
LastName: r.LastName,
Email: r.Email,
Phone: r.Phone,
UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
})
if err != nil {
return err
}
return nil
}
func (m *pgcDBRepo) DeleteReservation(id int) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.DeleteReservation(ctx, int32(id))
if err != nil {
return err
}
return nil
}
// UpdateProcessedForReservation set processed for a reservation
func (m *pgcDBRepo) UpdateProcessedForReservation(id, processed int) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.UpdateProcessedForReservation(ctx, sqlc.UpdateProcessedForReservationParams{
Processed: int32(processed),
ID: int32(id),
})
if err != nil {
return err
}
return nil
}
func (m *pgcDBRepo) AllRooms() ([]models.Room, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var rooms []models.Room
rows, err := m.Q.AllRooms(ctx)
if err != nil {
return nil, err
}
for _, row := range rows {
room := models.Room{
RoomName: row.RoomName,
ID: int(row.ID),
}
rooms = append(rooms, room)
}
return rooms, nil
}
// GetRestrictionsForRoomByDate returns restrictions for a room by date range
func (m *pgcDBRepo) GetRestrictionsForRoomByDate(roomId int, start, end time.Time) ([]models.RoomRestriction, error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
var restrictions []models.RoomRestriction
rows, err := m.Q.GetRestrictionsForRoomByDate(ctx, sqlc.GetRestrictionsForRoomByDateParams{
EndDate: pgtype.Date{Time: end, Valid: true},
StartDate: pgtype.Date{Time: start, Valid: true},
RoomID: int32(roomId),
})
if err != nil {
return restrictions, err
}
for _, row := range rows {
r := models.RoomRestriction{
StartDate: row.StartDate.Time,
EndDate: row.EndDate.Time,
ID: int(row.ID),
RoomID: int(row.RoomID),
ReservationID: int(row.ReservationID),
RestrictionID: int(row.RestrictionID),
}
restrictions = append(restrictions, r)
}
return restrictions, nil
}
// InsertBlockForRoom inserts a room restriction
func (m *pgcDBRepo) InsertBlockForRoom(id int, startDate time.Time) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.InsertBlockForRoom(ctx, sqlc.InsertBlockForRoomParams{
StartDate: pgtype.Date{Time: startDate, Valid: true},
EndDate: pgtype.Date{Time: startDate.AddDate(0, 0, 1)},
RoomID: int32(id),
RestrictionID: 2,
CreatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
UpdatedAt: pgtype.Timestamp{Time: time.Now(), Valid: true},
})
if err != nil {
return err
}
return nil
}
// DeleteBlockByID deletes a block by ID
func (m *pgcDBRepo) DeleteBlockByID(id int) error {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
err := m.Q.DeleteBlockByID(ctx, int32(id))
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,20 @@
package driverrepo
import (
"go-udemy-web-1/internal/repository"
)
type (
sqlDriverRepo struct{}
sqlcDriverRepo struct{}
)
var dbConn = &repository.DB{}
func NewSqlRepo() repository.DatabaseDriverRepo {
return &sqlDriverRepo{}
}
func NewSqlcRepo() repository.DatabaseDriverRepo {
return &sqlcDriverRepo{}
}

View File

@ -0,0 +1,48 @@
package driverrepo
import (
"context"
"go-udemy-web-1/internal/repository"
"github.com/jackc/pgx/v5"
)
func (s *sqlcDriverRepo) ConnectSQL(dsn string) (*repository.DB, error) {
ctx := context.Background()
c, err := s.newConn(ctx, dsn)
if err != nil {
panic(err)
}
dbConn.PG = c
err = s.testDB(ctx, c)
if err != nil {
return nil, err
}
return dbConn, nil
}
// testDB tries to ping the database
func (s *sqlcDriverRepo) testDB(ctx context.Context, c *pgx.Conn) error {
err := c.Ping(ctx)
if err != nil {
return err
}
return nil
}
// newDatabase creates a new database for the application
func (s *sqlcDriverRepo) newConn(ctx context.Context, dsn string) (*pgx.Conn, error) {
conn, err := pgx.Connect(ctx, dsn)
if err != nil {
return nil, err
}
// Ping test the conn
if err = conn.Ping(ctx); err != nil {
return nil, err
}
return conn, err
}

View File

@ -1,7 +1,8 @@
package driver
package driverrepo
import (
"database/sql"
"go-udemy-web-1/internal/repository"
"time"
_ "github.com/jackc/pgconn"
@ -9,13 +10,6 @@ import (
_ "github.com/jackc/pgx/v5/stdlib"
)
// DB holds the database connection pool
type DB struct {
SQL *sql.DB
}
var dbConn = &DB{}
const (
maxOpenDbConn = 10
maxIdleDbConn = 5
@ -23,8 +17,8 @@ const (
)
// ConnectSQL creates SQL pool for Postgres
func ConnectSQL(dsn string) (*DB, error) {
d, err := NewDatabase(dsn)
func (s *sqlDriverRepo) ConnectSQL(dsn string) (*repository.DB, error) {
d, err := s.newDatabase(dsn)
if err != nil {
panic(err)
}
@ -35,7 +29,7 @@ func ConnectSQL(dsn string) (*DB, error) {
dbConn.SQL = d
err = testDB(d)
err = s.testDB(d)
if err != nil {
return nil, err
}
@ -43,7 +37,7 @@ func ConnectSQL(dsn string) (*DB, error) {
}
// testDB tries to ping the database
func testDB(d *sql.DB) error {
func (s *sqlDriverRepo) testDB(d *sql.DB) error {
err := d.Ping()
if err != nil {
return err
@ -51,8 +45,8 @@ func testDB(d *sql.DB) error {
return nil
}
// NewDatabase creates a new datavase for the application
func NewDatabase(dsn string) (*sql.DB, error) {
// newDatabase creates a new database for the application
func (s *sqlDriverRepo) newDatabase(dsn string) (*sql.DB, error) {
db, err := sql.Open("pgx", dsn)
if err != nil {
return nil, err

View File

@ -1,8 +1,11 @@
package repository
import (
"database/sql"
"go-udemy-web-1/internal/models"
"time"
"github.com/jackc/pgx/v5"
)
type DatabaseRepo interface {
@ -27,3 +30,13 @@ type DatabaseRepo interface {
InsertBlockForRoom(id int, startDate time.Time) error
DeleteBlockByID(id int) error
}
// DB holds the database connection pool
type DB struct {
SQL *sql.DB
PG *pgx.Conn
}
type DatabaseDriverRepo interface {
ConnectSQL(dsn string) (*DB, error)
}

View File

@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.26.0
package sqlc
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

View File

@ -0,0 +1,63 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.26.0
package sqlc
import (
"github.com/jackc/pgx/v5/pgtype"
)
type Reservation struct {
ID int32
FirstName string
LastName string
Email string
Phone string
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
Processed int32
}
type Restriction struct {
ID int32
RestrictionName string
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type Room struct {
ID int32
RoomName string
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type RoomRestriction struct {
ID int32
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
ReservationID pgtype.Int4
RestrictionID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
type SchemaMigration struct {
Version string
}
type User struct {
ID int32
FirstName string
LastName string
Email string
Password string
AccessLevel int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}

View File

@ -0,0 +1,685 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.26.0
// source: query.sql
package sqlc
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const allNewReservations = `-- name: AllNewReservations :many
SELECT
r.id,
r.first_name,
r.last_name,
r.email,
r.phone,
r.start_date,
r.end_date,
r.room_id,
r.created_at,
r.updated_at,
r.processed,
rm.id,
rm.room_name
FROM
reservations r
LEFT JOIN rooms rm ON (r.room_id = rm.id)
WHERE
r.processed = 0
ORDER BY
r.start_date ASC
`
type AllNewReservationsRow struct {
ID int32
FirstName string
LastName string
Email string
Phone string
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
Processed int32
ID_2 pgtype.Int4
RoomName pgtype.Text
}
func (q *Queries) AllNewReservations(ctx context.Context) ([]AllNewReservationsRow, error) {
rows, err := q.db.Query(ctx, allNewReservations)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AllNewReservationsRow
for rows.Next() {
var i AllNewReservationsRow
if err := rows.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.Email,
&i.Phone,
&i.StartDate,
&i.EndDate,
&i.RoomID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Processed,
&i.ID_2,
&i.RoomName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const allReservations = `-- name: AllReservations :many
SELECT
r.id,
r.first_name,
r.last_name,
r.email,
r.phone,
r.start_date,
r.end_date,
r.room_id,
r.created_at,
r.updated_at,
r.processed,
rm.id,
rm.room_name
FROM
reservations r
LEFT JOIN rooms rm ON (r.room_id = rm.id)
ORDER BY
r.start_date ASC
`
type AllReservationsRow struct {
ID int32
FirstName string
LastName string
Email string
Phone string
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
Processed int32
ID_2 pgtype.Int4
RoomName pgtype.Text
}
func (q *Queries) AllReservations(ctx context.Context) ([]AllReservationsRow, error) {
rows, err := q.db.Query(ctx, allReservations)
if err != nil {
return nil, err
}
defer rows.Close()
var items []AllReservationsRow
for rows.Next() {
var i AllReservationsRow
if err := rows.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.Email,
&i.Phone,
&i.StartDate,
&i.EndDate,
&i.RoomID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Processed,
&i.ID_2,
&i.RoomName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const allRooms = `-- name: AllRooms :many
SELECT
id,
room_name,
created_at,
updated_at
FROM
rooms
ORDER BY
room_name
`
func (q *Queries) AllRooms(ctx context.Context) ([]Room, error) {
rows, err := q.db.Query(ctx, allRooms)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Room
for rows.Next() {
var i Room
if err := rows.Scan(
&i.ID,
&i.RoomName,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const deleteBlockByID = `-- name: DeleteBlockByID :exec
DELETE FROM room_restrictions
WHERE
id = $1
`
func (q *Queries) DeleteBlockByID(ctx context.Context, id int32) error {
_, err := q.db.Exec(ctx, deleteBlockByID, id)
return err
}
const deleteReservation = `-- name: DeleteReservation :exec
DELETE FROM reservations
WHERE
id = $1
`
func (q *Queries) DeleteReservation(ctx context.Context, id int32) error {
_, err := q.db.Exec(ctx, deleteReservation, id)
return err
}
const getReservationByID = `-- name: GetReservationByID :one
SELECT
r.id,
r.first_name,
r.last_name,
r.email,
r.phone,
r.start_date,
r.end_date,
r.room_id,
r.created_at,
r.updated_at,
r.processed,
rm.id,
rm.room_name
FROM
reservations r
LEFT JOIN rooms rm ON (r.room_id = rm.id)
WHERE
r.id = $1
`
type GetReservationByIDRow struct {
ID int32
FirstName string
LastName string
Email string
Phone string
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
Processed int32
ID_2 pgtype.Int4
RoomName pgtype.Text
}
func (q *Queries) GetReservationByID(ctx context.Context, id int32) (GetReservationByIDRow, error) {
row := q.db.QueryRow(ctx, getReservationByID, id)
var i GetReservationByIDRow
err := row.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.Email,
&i.Phone,
&i.StartDate,
&i.EndDate,
&i.RoomID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Processed,
&i.ID_2,
&i.RoomName,
)
return i, err
}
const getRestrictionsForRoomByDate = `-- name: GetRestrictionsForRoomByDate :many
SELECT
id,
coalesce(reservation_id, 0),
restriction_id,
room_id,
start_date,
end_date
FROM
room_restrictions
WHERE
$1 < end_date
AND $2 >= start_date
AND room_id = $3
`
type GetRestrictionsForRoomByDateParams struct {
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
}
type GetRestrictionsForRoomByDateRow struct {
ID int32
ReservationID int32
RestrictionID int32
RoomID int32
StartDate pgtype.Date
EndDate pgtype.Date
}
func (q *Queries) GetRestrictionsForRoomByDate(ctx context.Context, arg GetRestrictionsForRoomByDateParams) ([]GetRestrictionsForRoomByDateRow, error) {
rows, err := q.db.Query(ctx, getRestrictionsForRoomByDate, arg.StartDate, arg.EndDate, arg.RoomID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetRestrictionsForRoomByDateRow
for rows.Next() {
var i GetRestrictionsForRoomByDateRow
if err := rows.Scan(
&i.ID,
&i.ReservationID,
&i.RestrictionID,
&i.RoomID,
&i.StartDate,
&i.EndDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getRoomById = `-- name: GetRoomById :one
SELECT
id,
room_name,
created_at,
updated_at
FROM
rooms
WHERE
id = $1
`
func (q *Queries) GetRoomById(ctx context.Context, id int32) (Room, error) {
row := q.db.QueryRow(ctx, getRoomById, id)
var i Room
err := row.Scan(
&i.ID,
&i.RoomName,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT
id,
first_name,
last_name,
email,
password,
access_level,
created_at,
updated_at
FROM
users
WHERE
id = $1
`
func (q *Queries) GetUserByID(ctx context.Context, id int32) (User, error) {
row := q.db.QueryRow(ctx, getUserByID, id)
var i User
err := row.Scan(
&i.ID,
&i.FirstName,
&i.LastName,
&i.Email,
&i.Password,
&i.AccessLevel,
&i.CreatedAt,
&i.UpdatedAt,
)
return i, err
}
const getUserCred = `-- name: GetUserCred :one
SELECT
id,
password
FROM
users
WHERE
email = $1
`
type GetUserCredRow struct {
ID int32
Password string
}
func (q *Queries) GetUserCred(ctx context.Context, email string) (GetUserCredRow, error) {
row := q.db.QueryRow(ctx, getUserCred, email)
var i GetUserCredRow
err := row.Scan(&i.ID, &i.Password)
return i, err
}
const insertBlockForRoom = `-- name: InsertBlockForRoom :exec
INSERT INTO
room_restrictions (
start_date,
end_date,
room_id,
restriction_id,
created_at,
updated_at
)
VALUES
($1, $2, $3, $4, $5, $6)
`
type InsertBlockForRoomParams struct {
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
RestrictionID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
func (q *Queries) InsertBlockForRoom(ctx context.Context, arg InsertBlockForRoomParams) error {
_, err := q.db.Exec(ctx, insertBlockForRoom,
arg.StartDate,
arg.EndDate,
arg.RoomID,
arg.RestrictionID,
arg.CreatedAt,
arg.UpdatedAt,
)
return err
}
const insertReservation = `-- name: InsertReservation :one
INSERT INTO
reservations (
first_name,
last_name,
email,
phone,
start_date,
end_date,
room_id,
created_at,
updated_at
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING
id
`
type InsertReservationParams struct {
FirstName string
LastName string
Email string
Phone string
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
func (q *Queries) InsertReservation(ctx context.Context, arg InsertReservationParams) (int32, error) {
row := q.db.QueryRow(ctx, insertReservation,
arg.FirstName,
arg.LastName,
arg.Email,
arg.Phone,
arg.StartDate,
arg.EndDate,
arg.RoomID,
arg.CreatedAt,
arg.UpdatedAt,
)
var id int32
err := row.Scan(&id)
return id, err
}
const insertRoomRestriction = `-- name: InsertRoomRestriction :exec
INSERT INTO
room_restrictions (
start_date,
end_date,
room_id,
reservation_id,
restriction_id,
created_at,
updated_at
)
VALUES
($1, $2, $3, $4, $5, $6, $7)
`
type InsertRoomRestrictionParams struct {
StartDate pgtype.Date
EndDate pgtype.Date
RoomID int32
ReservationID pgtype.Int4
RestrictionID int32
CreatedAt pgtype.Timestamp
UpdatedAt pgtype.Timestamp
}
func (q *Queries) InsertRoomRestriction(ctx context.Context, arg InsertRoomRestrictionParams) error {
_, err := q.db.Exec(ctx, insertRoomRestriction,
arg.StartDate,
arg.EndDate,
arg.RoomID,
arg.ReservationID,
arg.RestrictionID,
arg.CreatedAt,
arg.UpdatedAt,
)
return err
}
const searchAvailabilityByDatesByRoomID = `-- name: SearchAvailabilityByDatesByRoomID :one
SELECT
count(id)
FROM
room_restrictions
WHERE
room_id = $1
AND $2 < end_date
AND $3 > start_date
`
type SearchAvailabilityByDatesByRoomIDParams struct {
RoomID int32
EndDate pgtype.Date
StartDate pgtype.Date
}
func (q *Queries) SearchAvailabilityByDatesByRoomID(ctx context.Context, arg SearchAvailabilityByDatesByRoomIDParams) (int64, error) {
row := q.db.QueryRow(ctx, searchAvailabilityByDatesByRoomID, arg.RoomID, arg.EndDate, arg.StartDate)
var count int64
err := row.Scan(&count)
return count, err
}
const searchAvailabilityForAllRooms = `-- name: SearchAvailabilityForAllRooms :many
SELECT
r.id,
r.room_name
FROM
rooms r
WHERE
r.id NOT IN (
SELECT
room_id
FROM
room_restrictions rr
WHERE
$1 < rr.end_date
AND $2 > rr.start_date
)
`
type SearchAvailabilityForAllRoomsParams struct {
EndDate pgtype.Date
StartDate pgtype.Date
}
type SearchAvailabilityForAllRoomsRow struct {
ID int32
RoomName string
}
func (q *Queries) SearchAvailabilityForAllRooms(ctx context.Context, arg SearchAvailabilityForAllRoomsParams) ([]SearchAvailabilityForAllRoomsRow, error) {
rows, err := q.db.Query(ctx, searchAvailabilityForAllRooms, arg.EndDate, arg.StartDate)
if err != nil {
return nil, err
}
defer rows.Close()
var items []SearchAvailabilityForAllRoomsRow
for rows.Next() {
var i SearchAvailabilityForAllRoomsRow
if err := rows.Scan(&i.ID, &i.RoomName); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateProcessedForReservation = `-- name: UpdateProcessedForReservation :exec
UPDATE reservations
SET
processed = $1
WHERE
id = $2
`
type UpdateProcessedForReservationParams struct {
Processed int32
ID int32
}
func (q *Queries) UpdateProcessedForReservation(ctx context.Context, arg UpdateProcessedForReservationParams) error {
_, err := q.db.Exec(ctx, updateProcessedForReservation, arg.Processed, arg.ID)
return err
}
const updateReservation = `-- name: UpdateReservation :exec
UPDATE reservations
SET
first_name = $1,
last_name = $2,
email = $3,
phone = $4,
updated_at = $5
WHERE
id = $6
`
type UpdateReservationParams struct {
FirstName string
LastName string
Email string
Phone string
UpdatedAt pgtype.Timestamp
ID int32
}
func (q *Queries) UpdateReservation(ctx context.Context, arg UpdateReservationParams) error {
_, err := q.db.Exec(ctx, updateReservation,
arg.FirstName,
arg.LastName,
arg.Email,
arg.Phone,
arg.UpdatedAt,
arg.ID,
)
return err
}
const updateUser = `-- name: UpdateUser :exec
UPDATE users
SET
first_name = $1,
last_name = $2,
email = $3,
access_level = $4,
updated_at = $5
`
type UpdateUserParams struct {
FirstName string
LastName string
Email string
AccessLevel int32
UpdatedAt pgtype.Timestamp
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
_, err := q.db.Exec(ctx, updateUser,
arg.FirstName,
arg.LastName,
arg.Email,
arg.AccessLevel,
arg.UpdatedAt,
)
return err
}

234
sql/query.sql Normal file
View File

@ -0,0 +1,234 @@
-- name: InsertReservation :one
INSERT INTO
reservations (
first_name,
last_name,
email,
phone,
start_date,
end_date,
room_id,
created_at,
updated_at
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING
id;
-- name: InsertRoomRestriction :exec
INSERT INTO
room_restrictions (
start_date,
end_date,
room_id,
reservation_id,
restriction_id,
created_at,
updated_at
)
VALUES
($1, $2, $3, $4, $5, $6, $7);
-- name: SearchAvailabilityByDatesByRoomID :one
SELECT
count(id)
FROM
room_restrictions
WHERE
room_id = $1
AND $2 < end_date
AND $3 > start_date;
-- name: SearchAvailabilityForAllRooms :many
SELECT
r.id,
r.room_name
FROM
rooms r
WHERE
r.id NOT IN (
SELECT
room_id
FROM
room_restrictions rr
WHERE
$1 < rr.end_date
AND $2 > rr.start_date
);
-- name: GetRoomById :one
SELECT
id,
room_name,
created_at,
updated_at
FROM
rooms
WHERE
id = $1;
-- name: GetUserByID :one
SELECT
id,
first_name,
last_name,
email,
password,
access_level,
created_at,
updated_at
FROM
users
WHERE
id = $1;
-- name: UpdateUser :exec
UPDATE users
SET
first_name = $1,
last_name = $2,
email = $3,
access_level = $4,
updated_at = $5;
-- name: GetUserCred :one
SELECT
id,
password
FROM
users
WHERE
email = $1;
-- name: AllReservations :many
SELECT
r.id,
r.first_name,
r.last_name,
r.email,
r.phone,
r.start_date,
r.end_date,
r.room_id,
r.created_at,
r.updated_at,
r.processed,
rm.id,
rm.room_name
FROM
reservations r
LEFT JOIN rooms rm ON (r.room_id = rm.id)
ORDER BY
r.start_date ASC;
-- name: AllNewReservations :many
SELECT
r.id,
r.first_name,
r.last_name,
r.email,
r.phone,
r.start_date,
r.end_date,
r.room_id,
r.created_at,
r.updated_at,
r.processed,
rm.id,
rm.room_name
FROM
reservations r
LEFT JOIN rooms rm ON (r.room_id = rm.id)
WHERE
r.processed = 0
ORDER BY
r.start_date ASC;
-- name: GetReservationByID :one
SELECT
r.id,
r.first_name,
r.last_name,
r.email,
r.phone,
r.start_date,
r.end_date,
r.room_id,
r.created_at,
r.updated_at,
r.processed,
rm.id,
rm.room_name
FROM
reservations r
LEFT JOIN rooms rm ON (r.room_id = rm.id)
WHERE
r.id = $1;
-- name: UpdateReservation :exec
UPDATE reservations
SET
first_name = $1,
last_name = $2,
email = $3,
phone = $4,
updated_at = $5
WHERE
id = $6;
-- name: DeleteReservation :exec
DELETE FROM reservations
WHERE
id = $1;
-- name: UpdateProcessedForReservation :exec
UPDATE reservations
SET
processed = $1
WHERE
id = $2;
-- name: AllRooms :many
SELECT
id,
room_name,
created_at,
updated_at
FROM
rooms
ORDER BY
room_name;
-- name: GetRestrictionsForRoomByDate :many
SELECT
id,
coalesce(reservation_id, 0),
restriction_id,
room_id,
start_date,
end_date
FROM
room_restrictions
WHERE
@start_date < end_date
AND @end_date >= start_date
AND room_id = @room_id;
-- name: InsertBlockForRoom :exec
INSERT INTO
room_restrictions (
start_date,
end_date,
room_id,
restriction_id,
created_at,
updated_at
)
VALUES
($1, $2, $3, $4, $5, $6);
-- name: DeleteBlockByID :exec
DELETE FROM room_restrictions
WHERE
id = $1;

96
sql/schema.sql Normal file
View File

@ -0,0 +1,96 @@
-- Adminer 4.8.1 PostgreSQL 16.3 (Debian 16.3-1.pgdg120+1) dump
CREATE SEQUENCE reservations_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."reservations" (
"id" integer DEFAULT nextval('reservations_id_seq') NOT NULL,
"first_name" character varying(255) DEFAULT '' NOT NULL,
"last_name" character varying(255) DEFAULT '' NOT NULL,
"email" character varying(255) NOT NULL,
"phone" character varying(255) DEFAULT '' NOT NULL,
"start_date" date NOT NULL,
"end_date" date NOT NULL,
"room_id" integer NOT NULL,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
"processed" integer DEFAULT '0' NOT NULL,
CONSTRAINT "reservations_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE INDEX "reservations_email_idx" ON "public"."reservations" USING btree ("email");
CREATE INDEX "reservations_last_name_idx" ON "public"."reservations" USING btree ("last_name");
CREATE SEQUENCE restrictions_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."restrictions" (
"id" integer DEFAULT nextval('restrictions_id_seq') NOT NULL,
"restriction_name" character varying(255) NOT NULL,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
CONSTRAINT "restrictions_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE SEQUENCE room_restrictions_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."room_restrictions" (
"id" integer DEFAULT nextval('room_restrictions_id_seq') NOT NULL,
"start_date" date NOT NULL,
"end_date" date NOT NULL,
"room_id" integer NOT NULL,
"reservation_id" integer,
"restriction_id" integer NOT NULL,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
CONSTRAINT "room_restrictions_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE INDEX "room_restrictions_reservation_id_idx" ON "public"."room_restrictions" USING btree ("reservation_id");
CREATE INDEX "room_restrictions_room_id_idx" ON "public"."room_restrictions" USING btree ("room_id");
CREATE INDEX "room_restrictions_start_date_end_date_idx" ON "public"."room_restrictions" USING btree ("start_date", "end_date");
CREATE SEQUENCE rooms_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."rooms" (
"id" integer DEFAULT nextval('rooms_id_seq') NOT NULL,
"room_name" character varying(255) NOT NULL,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
CONSTRAINT "rooms_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE TABLE "public"."schema_migration" (
"version" character varying(14) NOT NULL,
CONSTRAINT "schema_migration_pkey" PRIMARY KEY ("version"),
CONSTRAINT "schema_migration_version_idx" UNIQUE ("version")
) WITH (oids = false);
CREATE SEQUENCE users_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."users" (
"id" integer DEFAULT nextval('users_id_seq') NOT NULL,
"first_name" character varying(255) DEFAULT '' NOT NULL,
"last_name" character varying(255) DEFAULT '' NOT NULL,
"email" character varying(255) NOT NULL,
"password" character varying(60) NOT NULL,
"access_level" integer DEFAULT '1' NOT NULL,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
CONSTRAINT "users_email_idx" UNIQUE ("email"),
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
ALTER TABLE ONLY "public"."reservations" ADD CONSTRAINT "reservations_rooms_id_fk" FOREIGN KEY (room_id) REFERENCES rooms(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE;
ALTER TABLE ONLY "public"."room_restrictions" ADD CONSTRAINT "room_restrictions_reservations_id_fk" FOREIGN KEY (reservation_id) REFERENCES reservations(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE;
ALTER TABLE ONLY "public"."room_restrictions" ADD CONSTRAINT "room_restrictions_restrictions_id_fk" FOREIGN KEY (restriction_id) REFERENCES restrictions(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE;
ALTER TABLE ONLY "public"."room_restrictions" ADD CONSTRAINT "room_restrictions_rooms_id_fk" FOREIGN KEY (room_id) REFERENCES rooms(id) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE;

10
sqlc.yml Normal file
View File

@ -0,0 +1,10 @@
version: "2"
sql:
- engine: "postgresql"
queries: "sql/query.sql"
schema: "sql/schema.sql"
gen:
go:
package: "sqlc"
out: "internal/repository/sqlc"
sql_package: "pgx/v5"