Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,27 @@
// defer db.Close()
//
// var users []User
// err = db.Builder().Select("*").From("users").All(&users)
// err = db.Select("id", "name").From("users").All(&users)
//
// # Features
//
// CRUD Operations:
//
// // SELECT
// db.Builder().Select("*").From("users").Where("id = ?", 123).One(&user)
// // SELECT with Expression API
// db.Select().From("users").Where(relica.Eq("id", 123)).One(&user)
//
// // INSERT
// db.Builder().Insert("users", map[string]interface{}{
// "name": "Alice",
// "email": "alice@example.com",
// }).Execute()
// // INSERT via Model API (recommended)
// user := User{Name: "Alice", Email: "alice@example.com"}
// db.Model(&user).Insert()
//
// // UPDATE
// db.Builder().Update("users").
// db.Update("users").
// Set(map[string]interface{}{"status": "active"}).
// Where("id = ?", 123).
// Where(relica.Eq("id", 123)).
// Execute()
//
// // DELETE
// db.Builder().Delete("users").Where("id = ?", 123).Execute()
// db.Delete("users").Where(relica.Eq("id", 123)).Execute()
package relica

import (
Expand Down Expand Up @@ -302,9 +300,9 @@ func Open(driverName, dsn string, opts ...Option) (*DB, error) {
return &DB{db: coreDB}, nil
}

// NewDB creates a database connection (deprecated: use Open).
// NewDB creates a database connection.
//
// This function exists for backward compatibility. New code should use Open.
// Deprecated: Use Open instead. NewDB will be removed in a future version.
//
// Example:
//
Expand Down
2 changes: 1 addition & 1 deletion internal/core/aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func TestSelectQuery_Having_Expression(t *testing.T) {
require.NotNil(t, q)

// Verify HAVING clause with column expression
assert.Contains(t, q.sql, `HAVING "user_id">$1`)
assert.Contains(t, q.sql, `HAVING "user_id" > $1`)
assert.Equal(t, []interface{}{100}, q.params)
}

Expand Down
20 changes: 10 additions & 10 deletions internal/core/andwhere_orwhere_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestSelectQuery_AndWhere(t *testing.T) {
Where("status = ?", 1).
AndWhere(GreaterThan("age", 18))
},
expectedSQL: `SELECT * FROM "users" WHERE status = $1 AND "age">$2`,
expectedSQL: `SELECT * FROM "users" WHERE status = $1 AND "age" > $2`,
expectedLen: 2,
},
{
Expand Down Expand Up @@ -146,7 +146,7 @@ func TestSelectQuery_OrWhere(t *testing.T) {
Where("status = ?", 1).
OrWhere(Eq("role", "admin"))
},
expectedSQL: `SELECT * FROM "users" WHERE (status = $1) OR ("role"=$2)`,
expectedSQL: `SELECT * FROM "users" WHERE (status = $1) OR ("role" = $2)`,
expectedLen: 2,
},
{
Expand Down Expand Up @@ -247,7 +247,7 @@ func TestUpdateQuery_AndWhere(t *testing.T) {
Where("id > ?", 100).
AndWhere(Eq("active", true))
},
expectedSQL: `UPDATE "users" SET "status" = $1 WHERE id > $2 AND "active"=$3`,
expectedSQL: `UPDATE "users" SET "status" = $1 WHERE id > $2 AND "active" = $3`,
expectedLen: 3,
},
}
Expand Down Expand Up @@ -307,7 +307,7 @@ func TestUpdateQuery_OrWhere(t *testing.T) {
Where("banned = ?", true).
OrWhere(Eq("deleted", true))
},
expectedSQL: `UPDATE "users" SET "status" = $1 WHERE (banned = $2) OR ("deleted"=$3)`,
expectedSQL: `UPDATE "users" SET "status" = $1 WHERE (banned = $2) OR ("deleted" = $3)`,
expectedLen: 3,
},
}
Expand Down Expand Up @@ -363,7 +363,7 @@ func TestDeleteQuery_AndWhere(t *testing.T) {
Where("status = ?", 0).
AndWhere(LessThan("created_at", "2020-01-01"))
},
expectedSQL: `DELETE FROM "users" WHERE status = $1 AND "created_at"<$2`,
expectedSQL: `DELETE FROM "users" WHERE status = $1 AND "created_at" < $2`,
expectedLen: 2,
},
}
Expand Down Expand Up @@ -419,7 +419,7 @@ func TestDeleteQuery_OrWhere(t *testing.T) {
Where("banned = ?", true).
OrWhere(Eq("deleted", true))
},
expectedSQL: `DELETE FROM "users" WHERE (banned = $1) OR ("deleted"=$2)`,
expectedSQL: `DELETE FROM "users" WHERE (banned = $1) OR ("deleted" = $2)`,
expectedLen: 2,
},
}
Expand Down Expand Up @@ -549,9 +549,9 @@ func TestAndWhere_OrWhere_HashExp(t *testing.T) {
// After sorting: WHERE status=1 AND (age=18 AND city=NYC) OR (role=admin)
// With parentheses: (status=1 AND age=18 AND city=NYC) OR (role=admin)
assert.Contains(t, q.sql, `WHERE (`)
assert.Contains(t, q.sql, `"status"=$1`)
assert.Contains(t, q.sql, `"age"=$2`)
assert.Contains(t, q.sql, `"city"=$3`)
assert.Contains(t, q.sql, `OR ("role"=$4)`)
assert.Contains(t, q.sql, `"status" = $1`)
assert.Contains(t, q.sql, `"age" = $2`)
assert.Contains(t, q.sql, `"city" = $3`)
assert.Contains(t, q.sql, `OR ("role" = $4)`)
assert.Len(t, q.params, 4)
}
28 changes: 14 additions & 14 deletions internal/core/count_exists_tosql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ func TestSelectQuery_ToSQL_WithWhere(t *testing.T) {
{
name: "postgres: WHERE with positional placeholder",
dialect: "postgres",
wantSQL: `SELECT * FROM "users" WHERE "id"=$1`,
wantSQL: `SELECT * FROM "users" WHERE "id" = $1`,
wantParams: []interface{}{1},
},
{
name: "mysql: WHERE with positional placeholder",
dialect: "mysql",
wantSQL: "SELECT * FROM `users` WHERE `id`=?",
wantSQL: "SELECT * FROM `users` WHERE `id` = ?",
wantParams: []interface{}{1},
},
}
Expand Down Expand Up @@ -100,7 +100,7 @@ func TestSelectQuery_ToSQL_WithMultipleConditions(t *testing.T) {
ToSQL()

assert.Contains(t, sql, `SELECT "id", "name" FROM "users"`)
assert.Contains(t, sql, `WHERE "status"=$1 AND "age">$2`)
assert.Contains(t, sql, `WHERE "status" = $1 AND "age" > $2`)
assert.Contains(t, sql, `ORDER BY "name" ASC`)
assert.Contains(t, sql, `LIMIT 10`)
assert.Equal(t, []interface{}{1, 18}, params)
Expand Down Expand Up @@ -153,7 +153,7 @@ func TestUpdateQuery_ToSQL_Postgres(t *testing.T) {

assert.Contains(t, sql, `UPDATE "users" SET`)
assert.Contains(t, sql, `"status" = $1`)
assert.Contains(t, sql, `WHERE "id"=$2`)
assert.Contains(t, sql, `WHERE "id" = $2`)
assert.Equal(t, []interface{}{2, 1}, params)
}

Expand All @@ -168,7 +168,7 @@ func TestUpdateQuery_ToSQL_MySQL(t *testing.T) {

assert.Contains(t, sql, "UPDATE `users` SET")
assert.Contains(t, sql, "`name` = ?")
assert.Contains(t, sql, "WHERE `id`=?")
assert.Contains(t, sql, "WHERE `id` = ?")
assert.Equal(t, []interface{}{"Alice", 5}, params)
}

Expand Down Expand Up @@ -196,7 +196,7 @@ func TestUpdateQuery_ToSQL_SQLite(t *testing.T) {

assert.Contains(t, sql, `UPDATE "products" SET`)
assert.Contains(t, sql, `"price" = ?`)
assert.Contains(t, sql, `WHERE "id"=?`)
assert.Contains(t, sql, `WHERE "id" = ?`)
assert.Equal(t, []interface{}{99, 10}, params)
}

Expand All @@ -210,7 +210,7 @@ func TestDeleteQuery_ToSQL_Postgres(t *testing.T) {

sql, params := qb.Delete("users").Where(Eq("id", 1)).ToSQL()

assert.Equal(t, `DELETE FROM "users" WHERE "id"=$1`, sql)
assert.Equal(t, `DELETE FROM "users" WHERE "id" = $1`, sql)
assert.Equal(t, []interface{}{1}, params)
}

Expand All @@ -220,7 +220,7 @@ func TestDeleteQuery_ToSQL_MySQL(t *testing.T) {

sql, params := qb.Delete("sessions").Where(Eq("user_id", 99)).ToSQL()

assert.Equal(t, "DELETE FROM `sessions` WHERE `user_id`=?", sql)
assert.Equal(t, "DELETE FROM `sessions` WHERE `user_id` = ?", sql)
assert.Equal(t, []interface{}{99}, params)
}

Expand Down Expand Up @@ -254,8 +254,8 @@ func TestDeleteQuery_ToSQL_MultipleConditions(t *testing.T) {
ToSQL()

assert.Contains(t, sql, `DELETE FROM "events" WHERE`)
assert.Contains(t, sql, `"status"=$1`)
assert.Contains(t, sql, `"created_at"<$2`)
assert.Contains(t, sql, `"status" = $1`)
assert.Contains(t, sql, `"created_at" < $2`)
assert.Equal(t, []interface{}{"archived", "2020-01-01"}, params)
}

Expand Down Expand Up @@ -374,7 +374,7 @@ func TestSelectQuery_Exists_SQL_Postgres(t *testing.T) {
innerSQL, innerParams := innerQuery.buildSQL(db.dialect)
existsSQL := "SELECT EXISTS(" + innerSQL + ")"

assert.Equal(t, `SELECT EXISTS(SELECT 1 FROM "users" WHERE "email"=$1)`, existsSQL)
assert.Equal(t, `SELECT EXISTS(SELECT 1 FROM "users" WHERE "email" = $1)`, existsSQL)
assert.Equal(t, []interface{}{"alice@example.com"}, innerParams)
}

Expand All @@ -396,7 +396,7 @@ func TestSelectQuery_Exists_SQL_MySQL(t *testing.T) {
innerSQL, innerParams := innerQuery.buildSQL(db.dialect)
existsSQL := "SELECT EXISTS(" + innerSQL + ")"

assert.Equal(t, "SELECT EXISTS(SELECT 1 FROM `users` WHERE `id`=?)", existsSQL)
assert.Equal(t, "SELECT EXISTS(SELECT 1 FROM `users` WHERE `id` = ?)", existsSQL)
assert.Equal(t, []interface{}{7}, innerParams)
}

Expand Down Expand Up @@ -424,7 +424,7 @@ func TestSelectQuery_Exists_SQL_WithJoin(t *testing.T) {

assert.Contains(t, existsSQL, "SELECT EXISTS(")
assert.Contains(t, existsSQL, "INNER JOIN")
assert.Contains(t, existsSQL, `"status"=$1`)
assert.Contains(t, existsSQL, `"status" = $1`)
}

func TestSelectQuery_Exists_SQL_SQLite(t *testing.T) {
Expand All @@ -445,7 +445,7 @@ func TestSelectQuery_Exists_SQL_SQLite(t *testing.T) {
innerSQL, innerParams := innerQuery.buildSQL(db.dialect)
existsSQL := "SELECT EXISTS(" + innerSQL + ")"

assert.Equal(t, `SELECT EXISTS(SELECT 1 FROM "products" WHERE "sku"=?)`, existsSQL)
assert.Equal(t, `SELECT EXISTS(SELECT 1 FROM "products" WHERE "sku" = ?)`, existsSQL)
assert.Equal(t, []interface{}{"ABC-123"}, innerParams)
}

Expand Down
10 changes: 5 additions & 5 deletions internal/core/exists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ func TestExists_WithHashExp(t *testing.T) {
sql, args := exp.Build(dialect)
// HashExp keys are sorted: status, user_id
assert.Contains(t, sql, `EXISTS (`)
assert.Contains(t, sql, `"status"=?`)
assert.Contains(t, sql, `"user_id"=?`)
assert.Contains(t, sql, `"status" = ?`)
assert.Contains(t, sql, `"user_id" = ?`)
assert.Contains(t, sql, ` AND `)
assert.Equal(t, []interface{}{"active", 123}, args)
}
Expand All @@ -104,8 +104,8 @@ func TestExists_WithComplexExpression(t *testing.T) {

sql, args := exp.Build(dialect)
assert.Contains(t, sql, `EXISTS (`)
assert.Contains(t, sql, `"user_id"=?`)
assert.Contains(t, sql, `"amount">?`)
assert.Contains(t, sql, `"user_id" = ?`)
assert.Contains(t, sql, `"amount" > ?`)
assert.Contains(t, sql, `) AND (`)
assert.Equal(t, []interface{}{123, 100}, args)
}
Expand All @@ -121,7 +121,7 @@ func TestNotExists_WithComplexExpression(t *testing.T) {

sql, args := exp.Build(dialect)
assert.Contains(t, sql, `NOT EXISTS (`)
assert.Contains(t, sql, `"status"=?`)
assert.Contains(t, sql, `"status" = ?`)
assert.Contains(t, sql, `) OR (`)
assert.Equal(t, []interface{}{"pending", "failed"}, args)
}
Expand Down
10 changes: 5 additions & 5 deletions internal/core/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func buildHashExpValue(key string, value interface{}, dialect dialects.Dialect)
return in.Build(dialect)

default:
return col + "=?", []interface{}{value}
return col + " = ?", []interface{}{value}
}
}

Expand Down Expand Up @@ -222,11 +222,11 @@ func (e *CompareExp) Build(dialect dialects.Dialect) (string, []interface{}) {
// Handle Expression values
if expr, ok := e.Value.(Expression); ok {
sql, args := expr.Build(dialect)
return col + e.Operator + "(" + sql + ")", args
return col + " " + e.Operator + " (" + sql + ")", args
}

// Simple comparison
return col + e.Operator + "?", []interface{}{e.Value}
return col + " " + e.Operator + " ?", []interface{}{e.Value}
}

// InExp represents an IN or NOT IN expression.
Expand Down Expand Up @@ -298,9 +298,9 @@ func buildInExpSingleValue(col string, val interface{}, not bool, dialect dialec
}
// Non-NULL single value
if not {
return col + "<>?", []interface{}{val}, true
return col + " <> ?", []interface{}{val}, true
}
return col + "=?", []interface{}{val}, true
return col + " = ?", []interface{}{val}, true
}

// Build converts an IN expression into a SQL fragment.
Expand Down
Loading
Loading