What is the Database Schema?
The Slim database schema is defined in the database
package. This package contains the definitions for the tables and their corresponding columns.
Tables
The Slim database uses the following tables:
- projects: This table stores information about each project.
- images: This table stores information about each image.
- builds: This table stores information about each build.
- layers: This table stores information about each layer.
- manifests: This table stores information about each manifest.
- tags: This table stores information about each tag.
- metadata: This table stores metadata about each project, image, build, layer, manifest, and tag.
Database Schema in Code
Here are some code examples demonstrating how to access the database schema.
Example 1: Retrieving the schema for the projects
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// ProjectsSchema represents the schema for the projects table.
type ProjectsSchema struct {
ID int `db:"id"`
Name string `db:"name"`
Repo string `db:"repo"`
Owner string `db:"owner"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
Metadata string `db:"metadata"`
}
// GetProjectsSchema retrieves the schema for the projects table.
func GetProjectsSchema(db *sqlx.DB) ([]ProjectsSchema, error) {
var projects []ProjectsSchema
err := db.Select(&projects, `SELECT * FROM projects`)
if err != nil {
return nil, err
}
return projects, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the projects table.
projects, err := GetProjectsSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, project := range projects {
fmt.Printf("ID: %d, Name: %s, Repo: %s, Owner: %s, Created: %d, Updated: %d, Deleted: %d, Metadata: %s\n",
project.ID, project.Name, project.Repo, project.Owner, project.Created, project.Updated, project.Deleted, project.Metadata)
}
}
Example 2: Retrieving the schema for the images
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// ImagesSchema represents the schema for the images table.
type ImagesSchema struct {
ID int `db:"id"`
ProjectID int `db:"project_id"`
Digest string `db:"digest"`
Name string `db:"name"`
Tag string `db:"tag"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
Metadata string `db:"metadata"`
}
// GetImagesSchema retrieves the schema for the images table.
func GetImagesSchema(db *sqlx.DB) ([]ImagesSchema, error) {
var images []ImagesSchema
err := db.Select(&images, `SELECT * FROM images`)
if err != nil {
return nil, err
}
return images, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the images table.
images, err := GetImagesSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, image := range images {
fmt.Printf("ID: %d, ProjectID: %d, Digest: %s, Name: %s, Tag: %s, Created: %d, Updated: %d, Deleted: %d, Metadata: %s\n",
image.ID, image.ProjectID, image.Digest, image.Name, image.Tag, image.Created, image.Updated, image.Deleted, image.Metadata)
}
}
Example 3: Retrieving the schema for the builds
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// BuildsSchema represents the schema for the builds table.
type BuildsSchema struct {
ID int `db:"id"`
ProjectID int `db:"project_id"`
Digest string `db:"digest"`
Name string `db:"name"`
Tag string `db:"tag"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
Metadata string `db:"metadata"`
}
// GetBuildsSchema retrieves the schema for the builds table.
func GetBuildsSchema(db *sqlx.DB) ([]BuildsSchema, error) {
var builds []BuildsSchema
err := db.Select(&builds, `SELECT * FROM builds`)
if err != nil {
return nil, err
}
return builds, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the builds table.
builds, err := GetBuildsSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, build := range builds {
fmt.Printf("ID: %d, ProjectID: %d, Digest: %s, Name: %s, Tag: %s, Created: %d, Updated: %d, Deleted: %d, Metadata: %s\n",
build.ID, build.ProjectID, build.Digest, build.Name, build.Tag, build.Created, build.Updated, build.Deleted, build.Metadata)
}
}
Example 4: Retrieving the schema for the layers
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// LayersSchema represents the schema for the layers table.
type LayersSchema struct {
ID int `db:"id"`
ImageID int `db:"image_id"`
BuildID int `db:"build_id"`
Digest string `db:"digest"`
Size int64 `db:"size"`
MediaType string `db:"media_type"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
Metadata string `db:"metadata"`
}
// GetLayersSchema retrieves the schema for the layers table.
func GetLayersSchema(db *sqlx.DB) ([]LayersSchema, error) {
var layers []LayersSchema
err := db.Select(&layers, `SELECT * FROM layers`)
if err != nil {
return nil, err
}
return layers, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the layers table.
layers, err := GetLayersSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, layer := range layers {
fmt.Printf("ID: %d, ImageID: %d, BuildID: %d, Digest: %s, Size: %d, MediaType: %s, Created: %d, Updated: %d, Deleted: %d, Metadata: %s\n",
layer.ID, layer.ImageID, layer.BuildID, layer.Digest, layer.Size, layer.MediaType, layer.Created, layer.Updated, layer.Deleted, layer.Metadata)
}
}
Example 5: Retrieving the schema for the manifests
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// ManifestsSchema represents the schema for the manifests table.
type ManifestsSchema struct {
ID int `db:"id"`
ImageID int `db:"image_id"`
Digest string `db:"digest"`
MediaType string `db:"media_type"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
Metadata string `db:"metadata"`
}
// GetManifestsSchema retrieves the schema for the manifests table.
func GetManifestsSchema(db *sqlx.DB) ([]ManifestsSchema, error) {
var manifests []ManifestsSchema
err := db.Select(&manifests, `SELECT * FROM manifests`)
if err != nil {
return nil, err
}
return manifests, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the manifests table.
manifests, err := GetManifestsSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, manifest := range manifests {
fmt.Printf("ID: %d, ImageID: %d, Digest: %s, MediaType: %s, Created: %d, Updated: %d, Deleted: %d, Metadata: %s\n",
manifest.ID, manifest.ImageID, manifest.Digest, manifest.MediaType, manifest.Created, manifest.Updated, manifest.Deleted, manifest.Metadata)
}
}
Example 6: Retrieving the schema for the tags
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// TagsSchema represents the schema for the tags table.
type TagsSchema struct {
ID int `db:"id"`
ImageID int `db:"image_id"`
Name string `db:"name"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
Metadata string `db:"metadata"`
}
// GetTagsSchema retrieves the schema for the tags table.
func GetTagsSchema(db *sqlx.DB) ([]TagsSchema, error) {
var tags []TagsSchema
err := db.Select(&tags, `SELECT * FROM tags`)
if err != nil {
return nil, err
}
return tags, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the tags table.
tags, err := GetTagsSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, tag := range tags {
fmt.Printf("ID: %d, ImageID: %d, Name: %s, Created: %d, Updated: %d, Deleted: %d, Metadata: %s\n",
tag.ID, tag.ImageID, tag.Name, tag.Created, tag.Updated, tag.Deleted, tag.Metadata)
}
}
Example 7: Retrieving the schema for the metadata
table.
package database
import (
"context"
"database/sql"
"github.com/jmoiron/sqlx"
)
// MetadataSchema represents the schema for the metadata table.
type MetadataSchema struct {
ID int `db:"id"`
ProjectID int `db:"project_id"`
ImageID int `db:"image_id"`
BuildID int `db:"build_id"`
LayerID int `db:"layer_id"`
ManifestID int `db:"manifest_id"`
TagID int `db:"tag_id"`
Key string `db:"key"`
Value string `db:"value"`
Created int64 `db:"created"`
Updated int64 `db:"updated"`
Deleted int64 `db:"deleted"`
}
// GetMetadataSchema retrieves the schema for the metadata table.
func GetMetadataSchema(db *sqlx.DB) ([]MetadataSchema, error) {
var metadata []MetadataSchema
err := db.Select(&metadata, `SELECT * FROM metadata`)
if err != nil {
return nil, err
}
return metadata, nil
}
// Example usage:
func main() {
// Connect to the database.
db, err := sqlx.Connect("postgres", "user=postgres password=postgres dbname=slim")
if err != nil {
panic(err)
}
defer db.Close()
// Retrieve the schema for the metadata table.
metadata, err := GetMetadataSchema(db)
if err != nil {
panic(err)
}
// Print the schema.
for _, metadatum := range metadata {
fmt.Printf("ID: %d, ProjectID: %d, ImageID: %d, BuildID: %d, LayerID: %d, ManifestID: %d, TagID: %d, Key: %s, Value: %s, Created: %d, Updated: %d, Deleted: %d\n",
metadatum.ID, metadatum.ProjectID, metadatum.ImageID, metadatum.BuildID, metadatum.LayerID, metadatum.ManifestID, metadatum.TagID, metadatum.Key, metadatum.Value, metadatum.Created, metadatum.Updated, metadatum.Deleted)
}
}
Database Schema Documentation
The database schema is designed to store information about projects, images, builds, layers, manifests, and tags. It also includes a metadata table to store additional information.
Projects table:
id
: Unique identifier for the project.name
: Name of the project.repo
: Repository name of the project.owner
: Owner of the project.created
: Timestamp when the project was created.updated
: Timestamp when the project was last updated.deleted
: Timestamp when the project was deleted.metadata
: JSON string containing metadata about the project.
Images table:
id
: Unique identifier for the image.project_id
: Foreign key to theprojects
table, referencing the project that the image belongs to.digest
: Image digest.name
: Name of the image.tag
: Tag of the image.created
: Timestamp when the image was created.updated
: Timestamp when the image was last updated.deleted
: Timestamp when the image was deleted.metadata
: JSON string containing metadata about the image.
Builds table:
id
: Unique identifier for the build.project_id
: Foreign key to theprojects
table, referencing the project that the build belongs to.digest
: Build digest.name
: Name of the build.tag
: Tag of the build.created
: Timestamp when the build was created.updated
: Timestamp when the build was last updated.deleted
: Timestamp when the build was deleted.metadata
: JSON string containing metadata about the build.
Layers table:
id
: Unique identifier for the layer.image_id
: Foreign key to theimages
table, referencing the image that the layer belongs to.build_id
: Foreign key to thebuilds
table, referencing the build that the layer belongs to.digest
: Layer digest.size
: Size of the layer in bytes.media_type
: Media type of the layer.- `created
Querying the Database
The github.com/slimtoolkit/slim
package provides a powerful and flexible interface for interacting with databases. This document outlines how to query the database using the slim
package.
Example
package main
import (
"context"
"fmt"
"github.com/slimtoolkit/slim"
)
func main() {
// Create a new database connection.
db, err := slim.NewDatabase("mysql", "user:password@tcp(host:port)/database")
if err != nil {
panic(err)
}
defer db.Close()
// Execute a query.
rows, err := db.Query(context.Background(), "SELECT * FROM users")
if err != nil {
panic(err)
}
defer rows.Close()
// Iterate over the results.
for rows.Next() {
var id int
var name string
var email string
// Scan the values into the variables.
err := rows.Scan(&id, &name, &email)
if err != nil {
panic(err)
}
// Print the results.
fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}
// Check for errors during iteration.
if err := rows.Err(); err != nil {
panic(err)
}
}
This example demonstrates how to connect to a database, execute a query, and iterate over the results. The slim
package supports a variety of database drivers, allowing you to interact with different database systems.
Database Drivers
The slim
package provides support for the following database drivers:
- MySQL:
github.com/go-sql-driver/mysql
- PostgreSQL:
github.com/lib/pq
- SQLite:
github.com/mattn/go-sqlite3
- MongoDB:
go.mongodb.org/mongo-driver/mongo
Query Parameters
To prevent SQL injection, it’s crucial to use parameterized queries. The slim
package supports parameterized queries using the slim.Query()
and slim.Exec()
functions.
// Example using Query() with parameters
rows, err := db.Query(context.Background(), "SELECT * FROM users WHERE id = ?", 1)
if err != nil {
panic(err)
}
// ...
// Example using Exec() with parameters
result, err := db.Exec(context.Background(), "INSERT INTO users (name, email) VALUES (?, ?)", "John Doe", "[email protected]")
if err != nil {
panic(err)
}
These examples show how to pass parameters to queries and prevent SQL injection vulnerabilities.
Transactions
The slim
package allows you to perform transactions using the slim.BeginTx()
function.
tx, err := db.BeginTx(context.Background())
if err != nil {
panic(err)
}
defer func() {
if r := recover(); r != nil {
_ = tx.Rollback()
panic(r)
} else if err := tx.Commit(); err != nil {
panic(err)
}
}()
// Execute multiple queries within the transaction.
_, err = tx.Exec(context.Background(), "INSERT INTO users (name, email) VALUES (?, ?)", "Jane Doe", "[email protected]")
if err != nil {
panic(err)
}
// ... more queries within the transaction
// Commit the transaction.
if err := tx.Commit(); err != nil {
panic(err)
}
This example demonstrates how to begin a transaction, execute multiple queries, and commit the transaction. You can also rollback the transaction if an error occurs.
Error Handling
The slim
package returns an error if a query fails. You should always handle errors gracefully to prevent application crashes.
// Example of error handling
result, err := db.Exec(context.Background(), "INSERT INTO users (name, email) VALUES (?, ?)", "John Doe", "[email protected]")
if err != nil {
// Handle the error, e.g., log it and return an error response.
fmt.Println("Error executing query:", err)
return err
}
This example demonstrates how to check for errors after executing a query and handle them accordingly.
Conclusion
The slim
package provides a powerful and flexible interface for querying databases in Go. By using the provided functions and best practices for SQL injection prevention, you can efficiently and securely interact with databases in your applications. Remember to always handle errors gracefully to ensure your application’s stability.