mirror of https://github.com/aldy505/bob.git
fix: replacePlaceholder now do things properly
test: added more tests
This commit is contained in:
parent
5c112c73f6
commit
03e6bdb7fc
|
@ -8,13 +8,13 @@ Angular's [commit message guidelines](https://github.com/angular/angular/blob/ma
|
||||||
|
|
||||||
Appears under "Features" header, pencil subheader:
|
Appears under "Features" header, pencil subheader:
|
||||||
|
|
||||||
```
|
```xml
|
||||||
feat(pencil): add 'graphiteWidth' option
|
feat(pencil): add 'graphiteWidth' option
|
||||||
```
|
```
|
||||||
|
|
||||||
Appears under "Bug Fixes" header, graphite subheader, with a link to issue #28:
|
Appears under "Bug Fixes" header, graphite subheader, with a link to issue #28:
|
||||||
|
|
||||||
```
|
```xml
|
||||||
fix(graphite): stop graphite breaking when width < 0.1
|
fix(graphite): stop graphite breaking when width < 0.1
|
||||||
|
|
||||||
Closes #28
|
Closes #28
|
||||||
|
@ -22,7 +22,7 @@ Closes #28
|
||||||
|
|
||||||
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
|
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
|
||||||
|
|
||||||
```
|
```xml
|
||||||
perf(pencil): remove graphiteWidth option
|
perf(pencil): remove graphiteWidth option
|
||||||
|
|
||||||
BREAKING CHANGE: The graphiteWidth option has been removed. The default graphite width of 10mm is always used for performance reason.
|
BREAKING CHANGE: The graphiteWidth option has been removed. The default graphite width of 10mm is always used for performance reason.
|
||||||
|
@ -30,7 +30,7 @@ BREAKING CHANGE: The graphiteWidth option has been removed. The default graphite
|
||||||
|
|
||||||
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
|
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
|
||||||
|
|
||||||
```
|
```xml
|
||||||
revert: feat(pencil): add 'graphiteWidth' option
|
revert: feat(pencil): add 'graphiteWidth' option
|
||||||
|
|
||||||
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||||
|
@ -40,7 +40,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||||
|
|
||||||
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||||
|
|
||||||
```
|
```xml
|
||||||
<type>(<scope>): <subject>
|
<type>(<scope>): <subject>
|
||||||
<BLANK LINE>
|
<BLANK LINE>
|
||||||
<body>
|
<body>
|
||||||
|
@ -71,9 +71,9 @@ The scope could be anything specifying place of the commit change. For example `
|
||||||
|
|
||||||
The subject contains succinct description of the change:
|
The subject contains succinct description of the change:
|
||||||
|
|
||||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||||
* don't capitalize first letter
|
* don't capitalize first letter
|
||||||
* no dot (.) at the end
|
* no dot (.) at the end
|
||||||
|
|
||||||
### Body
|
### Body
|
||||||
|
|
||||||
|
@ -87,14 +87,6 @@ reference GitHub issues that this commit **Closes**.
|
||||||
|
|
||||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||||
|
|
||||||
A detailed explanation can be found in this [document](#commit-message-format).
|
A detailed explanation can be found in this [document][commit-message-format].
|
||||||
|
|
||||||
[npm-image]: https://badge.fury.io/js/conventional-changelog-angular.svg
|
|
||||||
[npm-url]: https://npmjs.org/package/conventional-changelog-angular
|
|
||||||
[travis-image]: https://travis-ci.org/conventional-changelog/conventional-changelog-angular.svg?branch=master
|
|
||||||
[travis-url]: https://travis-ci.org/conventional-changelog/conventional-changelog-angular
|
|
||||||
[daviddm-image]: https://david-dm.org/conventional-changelog/conventional-changelog-angular.svg?theme=shields.io
|
|
||||||
[daviddm-url]: https://david-dm.org/conventional-changelog/conventional-changelog-angular
|
|
||||||
[coveralls-image]: https://coveralls.io/repos/conventional-changelog/conventional-changelog-angular/badge.svg
|
|
||||||
[coveralls-url]: https://coveralls.io/r/conventional-changelog/conventional-changelog-angular
|
|
||||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
|
@ -19,7 +19,7 @@ func main() {
|
||||||
Types("varchar(36)", "varchar(255)", "varchar(255)", "text", "date").
|
Types("varchar(36)", "varchar(255)", "varchar(255)", "text", "date").
|
||||||
Primary("id").
|
Primary("id").
|
||||||
Unique("email")
|
Unique("email")
|
||||||
ToSql()
|
ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,13 @@ package bob
|
||||||
|
|
||||||
import "io"
|
import "io"
|
||||||
|
|
||||||
func appendToSql(parts []BobBuilder, w io.Writer, sep string, args []interface{}) ([]interface{}, error) {
|
// appendToSQL - Documentation coming soon
|
||||||
|
func appendToSQL(parts []BobBuilder, w io.Writer, sep string, args []interface{}) ([]interface{}, error) {
|
||||||
for i, p := range parts {
|
for i, p := range parts {
|
||||||
partSql, partArgs, err := p.ToSql()
|
partSQL, partArgs, err := p.ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if len(partSql) == 0 {
|
} else if len(partSQL) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ func appendToSql(parts []BobBuilder, w io.Writer, sep string, args []interface{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.WriteString(w, partSql)
|
_, err = io.WriteString(w, partSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
11
bob.go
11
bob.go
|
@ -2,34 +2,43 @@ package bob
|
||||||
|
|
||||||
import "github.com/lann/builder"
|
import "github.com/lann/builder"
|
||||||
|
|
||||||
|
// BobBuilderType is the type for BobBuilder
|
||||||
type BobBuilderType builder.Builder
|
type BobBuilderType builder.Builder
|
||||||
|
|
||||||
|
// BobBuilder interface wraps the ToSQL method
|
||||||
type BobBuilder interface {
|
type BobBuilder interface {
|
||||||
ToSql() (string, []interface{}, error)
|
ToSQL() (string, []interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTable creates a table with CreateBuilder interface
|
||||||
func (b BobBuilderType) CreateTable(table string) CreateBuilder {
|
func (b BobBuilderType) CreateTable(table string) CreateBuilder {
|
||||||
return CreateBuilder(b).Name(table)
|
return CreateBuilder(b).Name(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasTable checks if a table exists with HasBuilder interface
|
||||||
func (b BobBuilderType) HasTable(table string) HasBuilder {
|
func (b BobBuilderType) HasTable(table string) HasBuilder {
|
||||||
return HasBuilder(b).HasTable(table)
|
return HasBuilder(b).HasTable(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasColumn checks if a column exists with HasBuilder interface
|
||||||
func (b BobBuilderType) HasColumn(column string) HasBuilder {
|
func (b BobBuilderType) HasColumn(column string) HasBuilder {
|
||||||
return HasBuilder(b).HasColumn(column)
|
return HasBuilder(b).HasColumn(column)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BobStmtBuilder is the parent builder for BobBuilderType
|
||||||
var BobStmtBuilder = BobBuilderType(builder.EmptyBuilder)
|
var BobStmtBuilder = BobBuilderType(builder.EmptyBuilder)
|
||||||
|
|
||||||
|
// CreateTable creates a table with CreateBuilder interface
|
||||||
func CreateTable(table string) CreateBuilder {
|
func CreateTable(table string) CreateBuilder {
|
||||||
return BobStmtBuilder.CreateTable(table)
|
return BobStmtBuilder.CreateTable(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasTable checks if a table exists with HasBuilder interface
|
||||||
func HasTable(table string) HasBuilder {
|
func HasTable(table string) HasBuilder {
|
||||||
return BobStmtBuilder.HasTable(table)
|
return BobStmtBuilder.HasTable(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasColumn checks if a column exists with HasBuilder interface
|
||||||
func HasColumn(col string) HasBuilder {
|
func HasColumn(col string) HasBuilder {
|
||||||
return BobStmtBuilder.HasColumn(col)
|
return BobStmtBuilder.HasColumn(col)
|
||||||
}
|
}
|
||||||
|
|
14
create.go
14
create.go
|
@ -25,36 +25,44 @@ func init() {
|
||||||
builder.Register(CreateBuilder{}, createData{})
|
builder.Register(CreateBuilder{}, createData{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name sets the table name
|
||||||
func (b CreateBuilder) Name(name string) CreateBuilder {
|
func (b CreateBuilder) Name(name string) CreateBuilder {
|
||||||
return builder.Set(b, "TableName", name).(CreateBuilder)
|
return builder.Set(b, "TableName", name).(CreateBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSchema specifies the schema to be used when using the schema-building commands.
|
||||||
func (b CreateBuilder) WithSchema(name string) CreateBuilder {
|
func (b CreateBuilder) WithSchema(name string) CreateBuilder {
|
||||||
return builder.Set(b, "Schema", name).(CreateBuilder)
|
return builder.Set(b, "Schema", name).(CreateBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Columns sets the column names
|
||||||
func (b CreateBuilder) Columns(cols ...string) CreateBuilder {
|
func (b CreateBuilder) Columns(cols ...string) CreateBuilder {
|
||||||
return builder.Set(b, "Columns", cols).(CreateBuilder)
|
return builder.Set(b, "Columns", cols).(CreateBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Types set a type for certain column
|
||||||
func (b CreateBuilder) Types(types ...string) CreateBuilder {
|
func (b CreateBuilder) Types(types ...string) CreateBuilder {
|
||||||
return builder.Set(b, "Types", types).(CreateBuilder)
|
return builder.Set(b, "Types", types).(CreateBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Primary will set that column as the primary key for a table.
|
||||||
func (b CreateBuilder) Primary(column string) CreateBuilder {
|
func (b CreateBuilder) Primary(column string) CreateBuilder {
|
||||||
return builder.Set(b, "Primary", column).(CreateBuilder)
|
return builder.Set(b, "Primary", column).(CreateBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unique adds an unique index to a table over the given columns.
|
||||||
func (b CreateBuilder) Unique(column string) CreateBuilder {
|
func (b CreateBuilder) Unique(column string) CreateBuilder {
|
||||||
return builder.Set(b, "Unique", column).(CreateBuilder)
|
return builder.Set(b, "Unique", column).(CreateBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b CreateBuilder) ToSql() (string, []interface{}, error) {
|
// ToSQL returns 3 variables filled out with the correct values based on bindings, etc.
|
||||||
|
func (b CreateBuilder) ToSQL() (string, []interface{}, error) {
|
||||||
data := builder.GetStruct(b).(createData)
|
data := builder.GetStruct(b).(createData)
|
||||||
return data.ToSql()
|
return data.ToSQL()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *createData) ToSql() (sqlStr string, args []interface{}, err error) {
|
// ToSQL returns 3 variables filled out with the correct values based on bindings, etc.
|
||||||
|
func (d *createData) ToSQL() (sqlStr string, args []interface{}, err error) {
|
||||||
if len(d.TableName) == 0 || d.TableName == "" {
|
if len(d.TableName) == 0 || d.TableName == "" {
|
||||||
err = errors.New("create statements must specify a table")
|
err = errors.New("create statements must specify a table")
|
||||||
return
|
return
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
t.Run("should return correct sql string with basic columns and types", func(t *testing.T) {
|
t.Run("should return correct sql string with basic columns and types", func(t *testing.T) {
|
||||||
sql, _, err := bob.CreateTable("users").Columns("name", "password", "date").Types("varchar(255)", "text", "date").ToSql()
|
sql, _, err := bob.CreateTable("users").Columns("name", "password", "date").Types("varchar(255)", "text", "date").ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ func TestCreate(t *testing.T) {
|
||||||
Types("uuid", "varchar(255)", "varchar(255)", "text", "date").
|
Types("uuid", "varchar(255)", "varchar(255)", "text", "date").
|
||||||
Primary("id").
|
Primary("id").
|
||||||
Unique("email").
|
Unique("email").
|
||||||
ToSql()
|
ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ func TestCreate(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should be able to have a schema name", func(t *testing.T) {
|
t.Run("should be able to have a schema name", func(t *testing.T) {
|
||||||
sql, _, err := bob.CreateTable("users").WithSchema("private").Columns("name", "password", "date").Types("varchar(255)", "text", "date").ToSql()
|
sql, _, err := bob.CreateTable("users").WithSchema("private").Columns("name", "password", "date").Types("varchar(255)", "text", "date").ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -49,28 +49,28 @@ func TestCreate(t *testing.T) {
|
||||||
_, _, err := bob.CreateTable("users").
|
_, _, err := bob.CreateTable("users").
|
||||||
Columns("id", "name", "email", "password", "date").
|
Columns("id", "name", "email", "password", "date").
|
||||||
Types("uuid", "varchar(255)", "varchar(255)", "date").
|
Types("uuid", "varchar(255)", "varchar(255)", "date").
|
||||||
ToSql()
|
ToSQL()
|
||||||
if err.Error() != "columns and types should have equal length" {
|
if err.Error() != "columns and types should have equal length" {
|
||||||
t.Fatal("should throw an error, it didn't:", err.Error())
|
t.Fatal("should throw an error, it didn't:", err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should emit error on empty table name", func(t *testing.T) {
|
t.Run("should emit error on empty table name", func(t *testing.T) {
|
||||||
_, _, err := bob.CreateTable("").Columns("name").Types("text").ToSql()
|
_, _, err := bob.CreateTable("").Columns("name").Types("text").ToSQL()
|
||||||
if err.Error() != "create statements must specify a table" {
|
if err.Error() != "create statements must specify a table" {
|
||||||
t.Fatal("should throw an error, it didn't:", err.Error())
|
t.Fatal("should throw an error, it didn't:", err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should emit error for primary key not in columns", func(t *testing.T) {
|
t.Run("should emit error for primary key not in columns", func(t *testing.T) {
|
||||||
_, _, err := bob.CreateTable("users").Columns("name").Types("text").Primary("id").ToSql()
|
_, _, err := bob.CreateTable("users").Columns("name").Types("text").Primary("id").ToSQL()
|
||||||
if err.Error() != "supplied primary column name doesn't exists on columns" {
|
if err.Error() != "supplied primary column name doesn't exists on columns" {
|
||||||
t.Fatal("should throw an error, it didn't:", err.Error())
|
t.Fatal("should throw an error, it didn't:", err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should emit error for unique key not in columns", func(t *testing.T) {
|
t.Run("should emit error for unique key not in columns", func(t *testing.T) {
|
||||||
_, _, err := bob.CreateTable("users").Columns("name").Types("text").Unique("id").ToSql()
|
_, _, err := bob.CreateTable("users").Columns("name").Types("text").Unique("id").ToSQL()
|
||||||
if err.Error() != "supplied unique column name doesn't exists on columns" {
|
if err.Error() != "supplied unique column name doesn't exists on columns" {
|
||||||
t.Fatal("should throw an error, it didn't:", err.Error())
|
t.Fatal("should throw an error, it didn't:", err.Error())
|
||||||
}
|
}
|
||||||
|
|
12
has.go
12
has.go
|
@ -23,28 +23,34 @@ func init() {
|
||||||
builder.Register(HasBuilder{}, hasData{})
|
builder.Register(HasBuilder{}, hasData{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasTable checks for a table's existence by tableName, resolving with a boolean to signal if the table exists.
|
||||||
func (h HasBuilder) HasTable(table string) HasBuilder {
|
func (h HasBuilder) HasTable(table string) HasBuilder {
|
||||||
return builder.Set(h, "Name", table).(HasBuilder)
|
return builder.Set(h, "Name", table).(HasBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasColumn checks if a column exists in the current table, resolves the promise with a boolean, true if the column exists, false otherwise.
|
||||||
func (h HasBuilder) HasColumn(column string) HasBuilder {
|
func (h HasBuilder) HasColumn(column string) HasBuilder {
|
||||||
return builder.Set(h, "Column", column).(HasBuilder)
|
return builder.Set(h, "Column", column).(HasBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSchema specifies the schema to be used when using the schema-building commands.
|
||||||
func (h HasBuilder) WithSchema(schema string) HasBuilder {
|
func (h HasBuilder) WithSchema(schema string) HasBuilder {
|
||||||
return builder.Set(h, "Schema", schema).(HasBuilder)
|
return builder.Set(h, "Schema", schema).(HasBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PlaceholderFormat changes the default placeholder (?) to desired placeholder.
|
||||||
func (h HasBuilder) PlaceholderFormat(f string) HasBuilder {
|
func (h HasBuilder) PlaceholderFormat(f string) HasBuilder {
|
||||||
return builder.Set(h, "Placeholder", f).(HasBuilder)
|
return builder.Set(h, "Placeholder", f).(HasBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HasBuilder) ToSql() (string, []interface{}, error) {
|
// ToSQL returns 3 variables filled out with the correct values based on bindings, etc.
|
||||||
|
func (h HasBuilder) ToSQL() (string, []interface{}, error) {
|
||||||
data := builder.GetStruct(h).(hasData)
|
data := builder.GetStruct(h).(hasData)
|
||||||
return data.ToSql()
|
return data.ToSQL()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *hasData) ToSql() (sqlStr string, args []interface{}, err error) {
|
// ToSQL returns 3 variables filled out with the correct values based on bindings, etc.
|
||||||
|
func (d *hasData) ToSQL() (sqlStr string, args []interface{}, err error) {
|
||||||
sql := &bytes.Buffer{}
|
sql := &bytes.Buffer{}
|
||||||
if d.Name == "" {
|
if d.Name == "" {
|
||||||
err = errors.New("has statement should have a table name")
|
err = errors.New("has statement should have a table name")
|
||||||
|
|
59
has_test.go
59
has_test.go
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
func TestHas(t *testing.T) {
|
func TestHas(t *testing.T) {
|
||||||
t.Run("should be able to create a hasTable query", func(t *testing.T) {
|
t.Run("should be able to create a hasTable query", func(t *testing.T) {
|
||||||
sql, args, err := bob.HasTable("users").ToSql()
|
sql, args, err := bob.HasTable("users").ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ func TestHas(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should be able to create a hasColumn query", func(t *testing.T) {
|
t.Run("should be able to create a hasColumn query", func(t *testing.T) {
|
||||||
sql, args, err := bob.HasTable("users").HasColumn("name").ToSql()
|
sql, args, err := bob.HasTable("users").HasColumn("name").ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
@ -40,4 +40,59 @@ func TestHas(t *testing.T) {
|
||||||
t.Fatal("args is not equal with argsResult:", args)
|
t.Fatal("args is not equal with argsResult:", args)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should be able to create a hasColumn query (but reversed)", func(t *testing.T) {
|
||||||
|
sql, args, err := bob.HasColumn("name").HasTable("users").ToSQL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
result := "SELECT * FROM information_schema.columns WHERE table_name = ? AND column_name = ? AND table_schema = current_schema();"
|
||||||
|
if sql != result {
|
||||||
|
t.Fatal("sql is not equal with result:", sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) != 2 {
|
||||||
|
t.Fatal("args is not equal with argsResult:", args)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should be able to create a hasTable query with schema", func(t *testing.T) {
|
||||||
|
sql, args, err := bob.HasTable("users").WithSchema("private").ToSQL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
result := "SELECT * FROM information_schema.tables WHERE table_name = ? AND table_schema = ?;"
|
||||||
|
if sql != result {
|
||||||
|
t.Fatal("sql is not equal with result:", sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) != 2 {
|
||||||
|
t.Fatal("args is not equal with argsResult:", args)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should be able to have a different placeholder", func(t *testing.T) {
|
||||||
|
sql, args, err := bob.HasTable("users").HasColumn("name").PlaceholderFormat(bob.Dollar).ToSQL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
result := "SELECT * FROM information_schema.columns WHERE table_name = $1 AND column_name = $2 AND table_schema = current_schema();"
|
||||||
|
if sql != result {
|
||||||
|
t.Fatal("sql is not equal with result:", sql)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) != 2 {
|
||||||
|
t.Fatal("args is not equal with argsResult:", args)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should expect an error for no table name", func(t *testing.T) {
|
||||||
|
_, _, err := bob.HasTable("").ToSQL()
|
||||||
|
if err.Error() != "has statement should have a table name" {
|
||||||
|
t.Fatal("error is different:", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,40 @@
|
||||||
package bob
|
package bob
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strconv"
|
||||||
const (
|
"strings"
|
||||||
Question = "?"
|
|
||||||
Dollar = "$"
|
|
||||||
Colon = ":"
|
|
||||||
AtP = "@p"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Question is the format used in MySQL
|
||||||
|
Question = "?"
|
||||||
|
// Dollar is the format used in PostgreSQL
|
||||||
|
Dollar = "$"
|
||||||
|
// Colon is the format used in Oracle Database, but here I implemented it wrong.
|
||||||
|
// I will either fix it or remove it in the future.
|
||||||
|
Colon = ":"
|
||||||
|
// AtP comes in the documentation of Squirrel but I don't know what database uses it.
|
||||||
|
AtP = "@p"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlaceholderFormat is an interface for placeholder formattings.
|
||||||
type PlaceholderFormat interface {
|
type PlaceholderFormat interface {
|
||||||
ReplacePlaceholders(sql string) (string, error)
|
ReplacePlaceholders(sql string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - test this one
|
// ReplacePlaceholder converts default placeholder format to a specific format.
|
||||||
func ReplacePlaceholder(sql string, format string) string {
|
func ReplacePlaceholder(sql string, format string) string {
|
||||||
if format == "" {
|
if format == "" {
|
||||||
format = Question
|
format = Question
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if format == Dollar || format == Colon {
|
||||||
|
separate := strings.SplitAfter(sql, "?")
|
||||||
|
for i := 0; i < len(separate); i++ {
|
||||||
|
separate[i] = strings.Replace(separate[i], "?", format+strconv.Itoa(i+1), 1)
|
||||||
|
}
|
||||||
|
return strings.Join(separate, "")
|
||||||
|
}
|
||||||
|
|
||||||
return strings.ReplaceAll(sql, "?", format)
|
return strings.ReplaceAll(sql, "?", format)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package bob_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aldy505/bob"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReplacePlaceholder(t *testing.T) {
|
||||||
|
t.Run("should be able to replace placeholder to dollar", func(t *testing.T) {
|
||||||
|
sql := "INSERT INTO table_name (`col1`, `col2`, `col3`) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?);"
|
||||||
|
result := bob.ReplacePlaceholder(sql, bob.Dollar)
|
||||||
|
should := "INSERT INTO table_name (`col1`, `col2`, `col3`) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);"
|
||||||
|
|
||||||
|
if result != should {
|
||||||
|
t.Fatal("result string doesn't match:", result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should be able to replace placeholder to colon", func(t *testing.T) {
|
||||||
|
sql := "INSERT INTO table_name (`col1`, `col2`, `col3`) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?);"
|
||||||
|
result := bob.ReplacePlaceholder(sql, bob.Colon)
|
||||||
|
should := "INSERT INTO table_name (`col1`, `col2`, `col3`) VALUES (:1, :2, :3), (:4, :5, :6), (:7, :8, :9);"
|
||||||
|
|
||||||
|
if result != should {
|
||||||
|
t.Fatal("result string doesn't match:", result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should be able to replace placeholder to @p", func(t *testing.T) {
|
||||||
|
sql := "INSERT INTO table_name (`col1`, `col2`, `col3`) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?);"
|
||||||
|
result := bob.ReplacePlaceholder(sql, bob.AtP)
|
||||||
|
should := "INSERT INTO table_name (`col1`, `col2`, `col3`) VALUES (@p, @p, @p), (@p, @p, @p), (@p, @p, @p);"
|
||||||
|
|
||||||
|
if result != should {
|
||||||
|
t.Fatal("result string doesn't match:", result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package util
|
package util
|
||||||
|
|
||||||
|
// IsIn checks if an array have a value
|
||||||
func IsIn(arr []string, value string) bool {
|
func IsIn(arr []string, value string) bool {
|
||||||
for _, item := range arr {
|
for _, item := range arr {
|
||||||
if item == value {
|
if item == value {
|
||||||
|
@ -9,6 +10,7 @@ func IsIn(arr []string, value string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindPosition search for value position on an array
|
||||||
func FindPosition(arr []string, value string) int {
|
func FindPosition(arr []string, value string) int {
|
||||||
for i, item := range arr {
|
for i, item := range arr {
|
||||||
if item == value {
|
if item == value {
|
||||||
|
|
Loading…
Reference in New Issue