Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
gopkg.in/ulule/loukoum.v3
A simple SQL Query Builder.
Loukoum is a simple SQL Query Builder, only PostgreSQL is supported at the moment.
If you have to generate complex queries, which rely on various contexts, loukoum is the right tool for you.
Afraid to slip a tiny SQL injection manipulating fmt
to append conditions? Fear no more, loukoum is here to protect you against yourself.
Just a few examples when and where loukoum can become handy:
ON CONFLICT
clause for resource's ownerUsing Go Modules
go get github.com/ulule/loukoum/v3@v3.3.0
Loukoum helps you generate SQL queries from composable parts.
However, keep in mind it's not an ORM or a Mapper so you have to use a SQL connector (database/sql, sqlx, makroud, etc.) to execute queries.
Insert a new Comment
and retrieve its id
.
import lk "github.com/ulule/loukoum/v3"
// Comment model
type Comment struct {
ID int64
Email string `db:"email"`
Status string `db:"status"`
Message string `db:"message"`
UserID int64 `db:"user_id"`
CreatedAt pq.NullTime `db:"created_at"`
DeletedAt pq.NullTime `db:"deleted_at"`
}
// CreateComment creates a comment.
func CreateComment(db *sqlx.DB, comment Comment) (Comment, error) {
builder := lk.Insert("comments").
Set(
lk.Pair("email", comment.Email),
lk.Pair("status", "waiting"),
lk.Pair("message", comment.Message),
lk.Pair("created_at", lk.Raw("NOW()")),
).
Returning("id")
// query: INSERT INTO comments (created_at, email, message, status, user_id)
// VALUES (NOW(), :arg_1, :arg_2, :arg_3, :arg_4) RETURNING id
// args: map[string]interface{}{
// "arg_1": string(comment.Email),
// "arg_2": string(comment.Message),
// "arg_3": string("waiting"),
// "arg_4": string(comment.UserID),
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return comment, err
}
defer stmt.Close()
err = stmt.Get(&comment, args)
if err != nil {
return comment, err
}
return comment, nil
}
import lk "github.com/ulule/loukoum/v3"
// UpsertComment inserts or updates a comment based on the email attribute.
func UpsertComment(db *sqlx.DB, comment Comment) (Comment, error) {
builder := lk.Insert("comments").
Set(
lk.Pair("email", comment.Email),
lk.Pair("status", "waiting"),
lk.Pair("message", comment.Message),
lk.Pair("user_id", comment.UserID),
lk.Pair("created_at", lk.Raw("NOW()")),
).
OnConflict("email", lk.DoUpdate(
lk.Pair("message", comment.Message),
lk.Pair("user_id", comment.UserID),
lk.Pair("status", "waiting"),
lk.Pair("created_at", lk.Raw("NOW()")),
lk.Pair("deleted_at", nil),
)).
Returning("id, created_at")
// query: INSERT INTO comments (created_at, email, message, status, user_id)
// VALUES (NOW(), :arg_1, :arg_2, :arg_3, :arg_4)
// ON CONFLICT (email) DO UPDATE SET created_at = NOW(), deleted_at = NULL, message = :arg_5,
// status = :arg_6, user_id = :arg_7 RETURNING id, created_at
// args: map[string]interface{}{
// "arg_1": string(comment.Email),
// "arg_2": string(comment.Message),
// "arg_3": string("waiting"),
// "arg_4": string(comment.UserID),
// "arg_5": string(comment.Message),
// "arg_6": string("waiting"),
// "arg_7": string(comment.UserID),
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return comment, err
}
defer stmt.Close()
err = stmt.Get(&comment, args)
if err != nil {
return comment, err
}
return comment, nil
}
Publish a News
by updating its status and publication date.
// News model
type News struct {
ID int64
Status string `db:"status"`
PublishedAt pq.NullTime `db:"published_at"`
DeletedAt pq.NullTime `db:"deleted_at"`
}
// PublishNews publishes a news.
func PublishNews(db *sqlx.DB, news News) (News, error) {
builder := lk.Update("news").
Set(
lk.Pair("published_at", lk.Raw("NOW()")),
lk.Pair("status", "published"),
).
Where(lk.Condition("id").Equal(news.ID)).
And(lk.Condition("deleted_at").IsNull(true)).
Returning("published_at")
// query: UPDATE news SET published_at = NOW(), status = :arg_1 WHERE ((id = :arg_2) AND (deleted_at IS NULL))
// RETURNING published_at
// args: map[string]interface{}{
// "arg_1": string("published"),
// "arg_2": int64(news.ID),
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return news, err
}
defer stmt.Close()
err = stmt.Get(&news, args)
if err != nil {
return news, err
}
return news, nil
}
Retrieve non-deleted users.
import lk "github.com/ulule/loukoum/v3"
// User model
type User struct {
ID int64
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
Email string
IsStaff bool `db:"is_staff"`
DeletedAt pq.NullTime `db:"deleted_at"`
}
// FindUsers retrieves non-deleted users
func FindUsers(db *sqlx.DB) ([]User, error) {
builder := lk.Select("id", "first_name", "last_name", "email").
From("users").
Where(lk.Condition("deleted_at").IsNull(true))
// query: SELECT id, first_name, last_name, email FROM users WHERE (deleted_at IS NULL)
// args: map[string]interface{}{
//
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return nil, err
}
defer stmt.Close()
users := []User{}
err = stmt.Select(&users, args)
if err != nil {
return nil, err
}
return users, nil
}
Retrieve comments only sent by staff users, the staff users query will be a subquery as we don't want to use any JOIN operations.
// FindStaffComments retrieves comments by staff users.
func FindStaffComments(db *sqlx.DB, comment Comment) ([]Comment, error) {
builder := lk.Select("id", "email", "status", "user_id", "message", "created_at").
From("comments").
Where(lk.Condition("deleted_at").IsNull(true)).
Where(
lk.Condition("user_id").In(
lk.Select("id").
From("users").
Where(lk.Condition("is_staff").Equal(true)),
),
)
// query: SELECT id, email, status, user_id, message, created_at
// FROM comments WHERE ((deleted_at IS NULL) AND
// (user_id IN (SELECT id FROM users WHERE (is_staff = :arg_1))))
// args: map[string]interface{}{
// "arg_1": bool(true),
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return nil, err
}
defer stmt.Close()
comments := []Comment{}
err = stmt.Select(&comments, args)
if err != nil {
return nil, err
}
return comments, nil
}
Retrieve non-deleted comments sent by a user with embedded user in results.
First, we need to update the Comment
struct to embed User
.
// Comment model
type Comment struct {
ID int64
Email string `db:"email"`
Status string `db:"status"`
Message string `db:"message"`
UserID int64 `db:"user_id"`
User *User `db:"users"`
CreatedAt pq.NullTime `db:"created_at"`
DeletedAt pq.NullTime `db:"deleted_at"`
}
Let's create a FindComments
method to retrieve these comments.
In this scenario we will use an INNER JOIN
but loukoum also supports LEFT JOIN
and RIGHT JOIN
.
// FindComments retrieves comments by users.
func FindComments(db *sqlx.DB, comment Comment) ([]Comment, error) {
builder := lk.
Select(
"comments.id", "comments.email", "comments.status",
"comments.user_id", "comments.message", "comments.created_at",
).
From("comments").
Join(lk.Table("users"), lk.On("comments.user_id", "users.id")).
Where(lk.Condition("comments.deleted_at").IsNull(true))
// query: SELECT comments.id, comments.email, comments.status, comments.user_id, comments.message,
// comments.created_at FROM comments INNER JOIN users ON comments.user_id = users.id
// WHERE (comments.deleted_at IS NULL)
// args: map[string]interface{}{
//
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return nil, err
}
defer stmt.Close()
comments := []Comment{}
err = stmt.Select(&comments, args)
if err != nil {
return nil, err
}
return comments, nil
}
Delete a user based on ID.
// DeleteUser deletes a user.
func DeleteUser(db *sqlx.DB, user User) error {
builder := lk.Delete("users").
Where(lk.Condition("id").Equal(user.ID))
// query: DELETE FROM users WHERE (id = :arg_1)
// args: map[string]interface{}{
// "arg_1": int64(user.ID),
// }
query, args := builder.NamedQuery()
stmt, err := db.PrepareNamed(query)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(args)
return err
}
See examples directory for more information.
NOTE: For
database/sql
, see standard.
github.com/ulule/loukoum/...
by github.com/ulule/loukoum/v3/...
Prepare()
to NamedQuery()
for builder.Builder interface.This is Free Software, released under the MIT License
.
Loukoum artworks are released under the Creative Commons BY-SA License
.
Don't hesitate ;)
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.