From 3e46605581c885600b31d81dbb0e3f02cc414844 Mon Sep 17 00:00:00 2001 From: Alexander Loginov Date: Wed, 17 May 2023 22:48:37 +0300 Subject: [PATCH 1/4] Index, With, Flatten, Assume --- select.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/select.go b/select.go index d55ce4c7..2af39faa 100644 --- a/select.go +++ b/select.go @@ -16,10 +16,14 @@ type selectData struct { Options []string Columns []Sqlizer From Sqlizer + Index Sqlizer + WithParts []Sqlizer + Flatten []Sqlizer Joins []Sqlizer WhereParts []Sqlizer GroupBys []string HavingParts []Sqlizer + AssumeOrderBy string OrderByParts []Sqlizer Limit string Offset string @@ -100,6 +104,30 @@ func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) { } } + if d.Index != nil { + sql.WriteString(" VIEW ") + args, err = appendToSql([]Sqlizer{d.Index}, sql, "", args) + if err != nil { + return + } + } + + if len(d.WithParts) > 0 { + sql.WriteString(" WITH ") + args, err = appendToSql(d.WithParts, sql, " AND ", args) + if err != nil { + return + } + } + + if len(d.Flatten) > 0 { + sql.WriteString(" ") + args, err = appendToSql(d.Flatten, sql, " ", args) + if err != nil { + return + } + } + if len(d.Joins) > 0 { sql.WriteString(" ") args, err = appendToSql(d.Joins, sql, " ", args) @@ -129,6 +157,12 @@ func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) { } } + if len(d.AssumeOrderBy) > 0 && len(d.OrderByParts) > 0 { + sql.WriteString(" ") + sql.WriteString(d.AssumeOrderBy) + sql.WriteString(" ") + } + if len(d.OrderByParts) > 0 { sql.WriteString(" ORDER BY ") args, err = appendToSql(d.OrderByParts, sql, ", ", args) @@ -272,7 +306,8 @@ func (b SelectBuilder) RemoveColumns() SelectBuilder { // Column adds a result column to the query. // Unlike Columns, Column accepts args which will be bound to placeholders in // the columns string, for example: -// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3) +// +// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3) func (b SelectBuilder) Column(column interface{}, args ...interface{}) SelectBuilder { return builder.Append(b, "Columns", newPart(column, args...)).(SelectBuilder) } @@ -289,6 +324,48 @@ func (b SelectBuilder) FromSelect(from SelectBuilder, alias string) SelectBuilde return builder.Set(b, "From", Alias(from, alias)).(SelectBuilder) } +// Index sets the VIEW clause of the query. +func (b SelectBuilder) Index(view string) SelectBuilder { + return builder.Set(b, "Index", newPart(view)).(SelectBuilder) +} + +// With adds an expression to the WITH clause of the query. +// +// See Where. +func (b SelectBuilder) With(pred interface{}, rest ...interface{}) SelectBuilder { + return builder.Append(b, "WithParts", newWherePart(pred, rest...)).(SelectBuilder) +} + +// FlattenClause adds a flatten clause to the query. +func (b SelectBuilder) FlattenClause(pred interface{}, args ...interface{}) SelectBuilder { + return builder.Append(b, "Flatten", newPart(pred, args...)).(SelectBuilder) +} + +// Flatten adds a FLATTEN BY clause to the query. +func (b SelectBuilder) Flatten(flatten string, rest ...interface{}) SelectBuilder { + return b.FlattenClause("FLATTEN BY "+flatten, rest...) +} + +// FlattenList adds a FLATTEN BY clause to the query. +func (b SelectBuilder) FlattenList(flatten string, rest ...interface{}) SelectBuilder { + return b.FlattenClause("FLATTEN LIST BY "+flatten, rest...) +} + +// FlattenDict adds a FLATTEN BY clause to the query. +func (b SelectBuilder) FlattenDict(flatten string, rest ...interface{}) SelectBuilder { + return b.FlattenClause("FLATTEN DICT BY "+flatten, rest...) +} + +// FlattenOptional adds a FLATTEN BY clause to the query. +func (b SelectBuilder) FlattenOptional(flatten string, rest ...interface{}) SelectBuilder { + return b.FlattenClause("FLATTEN OPTIONAL BY "+flatten, rest...) +} + +// FlattenOptional adds a FLATTEN BY clause to the query. +func (b SelectBuilder) FlattenColumns(flatten string, rest ...interface{}) SelectBuilder { + return b.FlattenClause("FLATTEN COLUMNS", rest...) +} + // JoinClause adds a join clause to the query. func (b SelectBuilder) JoinClause(pred interface{}, args ...interface{}) SelectBuilder { return builder.Append(b, "Joins", newPart(pred, args...)).(SelectBuilder) @@ -358,6 +435,11 @@ func (b SelectBuilder) Having(pred interface{}, rest ...interface{}) SelectBuild return builder.Append(b, "HavingParts", newWherePart(pred, rest...)).(SelectBuilder) } +// AssumeOrderBy adds a ASSUME clause to the query. +func (b SelectBuilder) AssumeOrderBy() SelectBuilder { + return builder.Set(b, "AssumeOrderBy", "ASSUME").(SelectBuilder) +} + // OrderByClause adds ORDER BY clause to the query. func (b SelectBuilder) OrderByClause(pred interface{}, args ...interface{}) SelectBuilder { return builder.Append(b, "OrderByParts", newPart(pred, args...)).(SelectBuilder) From ee9a8dd1482b53a5ec6e015b66acc5cb6e234818 Mon Sep 17 00:00:00 2001 From: Alexander Loginov Date: Thu, 18 May 2023 00:54:13 +0300 Subject: [PATCH 2/4] create, drop --- create.go | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++ drop.go | 154 +++++++++++++++++++++++++++++++++ statement.go | 24 ++++++ 3 files changed, 417 insertions(+) create mode 100644 create.go create mode 100644 drop.go diff --git a/create.go b/create.go new file mode 100644 index 00000000..c929ab6c --- /dev/null +++ b/create.go @@ -0,0 +1,239 @@ +package squirrel + +import ( + "bytes" + "database/sql" + "errors" + "fmt" + "io" + "sort" + "strings" + + "github.com/lann/builder" +) + +type createStmt struct { + PlaceholderFormat PlaceholderFormat + RunWith BaseRunner + Prefixes []Sqlizer + StatementKeyword string + Table string + Columns []string + Types []string + PrimaryKey []Sqlizer + Suffixes []Sqlizer +} + +func (d *createStmt) Exec() (sql.Result, error) { + if d.RunWith == nil { + return nil, RunnerNotSet + } + return ExecWith(d.RunWith, d) +} + +func (d *createStmt) Query() (*sql.Rows, error) { + if d.RunWith == nil { + return nil, RunnerNotSet + } + return QueryWith(d.RunWith, d) +} + +func (d *createStmt) ToSql() (sqlStr string, args []interface{}, err error) { + if len(d.Table) == 0 { + err = errors.New("create statements must specify a table") + return + } + if len(d.Columns) == 0 { + err = errors.New("create statements must have at least one set of columns") + return + } + + sql := &bytes.Buffer{} + + if len(d.Prefixes) > 0 { + args, err = appendToSql(d.Prefixes, sql, " ", args) + if err != nil { + return + } + + sql.WriteString(" ") + } + + if d.StatementKeyword == "" { + sql.WriteString("CREATE ") + } else { + sql.WriteString(d.StatementKeyword) + sql.WriteString(" ") + } + + sql.WriteString("TABLE ") + sql.WriteString(d.Table) + sql.WriteString(" ") + + sql.WriteString("( ") + err = d.appendValuesToSQL(sql) + if len(d.PrimaryKey) > 0 { + sql.WriteString(", PRIMARY KEY ") + sql.WriteString("(") + args, err = appendToSql(d.PrimaryKey, sql, ", ", args) + sql.WriteString(") ") + if err != nil { + return + } + } + sql.WriteString(") ") + + if err != nil { + return + } + + if len(d.Suffixes) > 0 { + sql.WriteString(" ") + args, err = appendToSql(d.Suffixes, sql, " ", args) + if err != nil { + return + } + } + + sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String()) + return +} + +func (d *createStmt) appendValuesToSQL(w io.Writer) error { + if len(d.Columns) == 0 { + return errors.New("columns for create statements are not set") + } + if len(d.Types) == 0 { + return errors.New("types for create statements are not set") + } + if len(d.Types) != len(d.Columns) { + return errors.New("types size are not equal to columns for create statements are not set") + } + valueStrings := make([]string, len(d.Columns)) + for i, col := range d.Columns { + valueStrings[i] = fmt.Sprintf("%s %s", col, d.Types[i]) + } + io.WriteString(w, strings.Join(valueStrings, ", ")) + + return nil +} + +// Builder + +// CreateBuilder builds SQL CREATE statements. +type CreateBuilder builder.Builder + +func init() { + builder.Register(CreateBuilder{}, createStmt{}) +} + +// Format methods + +// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the +// query. +func (b CreateBuilder) PlaceholderFormat(f PlaceholderFormat) CreateBuilder { + return builder.Set(b, "PlaceholderFormat", f).(CreateBuilder) +} + +// Runner methods + +// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec. +func (b CreateBuilder) RunWith(runner BaseRunner) CreateBuilder { + return setRunWith(b, runner).(CreateBuilder) +} + +// Exec builds and Execs the query with the Runner set by RunWith. +func (b CreateBuilder) Exec() (sql.Result, error) { + data := builder.GetStruct(b).(createStmt) + return data.Exec() +} + +// Query builds and Querys the query with the Runner set by RunWith. +func (b CreateBuilder) Query() (*sql.Rows, error) { + data := builder.GetStruct(b).(createStmt) + return data.Query() +} + +// SQL methods + +// ToSql builds the query into a SQL string and bound args. +func (b CreateBuilder) ToSql() (string, []interface{}, error) { + data := builder.GetStruct(b).(createStmt) + return data.ToSql() +} + +// MustSql builds the query into a SQL string and bound args. +// It panics if there are any errors. +func (b CreateBuilder) MustSql() (string, []interface{}) { + sql, args, err := b.ToSql() + if err != nil { + panic(err) + } + return sql, args +} + +// Prefix adds an expression to the beginning of the query +func (b CreateBuilder) Prefix(sql string, args ...interface{}) CreateBuilder { + return b.PrefixExpr(Expr(sql, args...)) +} + +// PrefixExpr adds an expression to the very beginning of the query +func (b CreateBuilder) PrefixExpr(expr Sqlizer) CreateBuilder { + return builder.Append(b, "Prefixes", expr).(CreateBuilder) +} + +// Table sets the TABLE clause of the query. +func (b CreateBuilder) Table(table string) CreateBuilder { + return builder.Set(b, "Table", table).(CreateBuilder) +} + +// Columns adds create columns to the query. +func (b CreateBuilder) Columns(columns ...string) CreateBuilder { + return builder.Extend(b, "Columns", columns).(CreateBuilder) +} + +// Types adds create types to the query. +func (b CreateBuilder) Types(types ...string) CreateBuilder { + return builder.Extend(b, "Types", types).(CreateBuilder) +} + +// PrimaryKey adds an primary key to the query +func (b CreateBuilder) PrimaryKey(sql string, args ...interface{}) CreateBuilder { + return b.PrimaryKeyExpr(Expr(sql, args...)) +} + +// PrimaryKeyExpr adds an expression to the query +func (b CreateBuilder) PrimaryKeyExpr(expr Sqlizer) CreateBuilder { + return builder.Append(b, "PrimaryKey", expr).(CreateBuilder) +} + +// Suffix adds an expression to the end of the query +func (b CreateBuilder) Suffix(sql string, args ...interface{}) CreateBuilder { + return b.SuffixExpr(Expr(sql, args...)) +} + +// SuffixExpr adds an expression to the end of the query +func (b CreateBuilder) SuffixExpr(expr Sqlizer) CreateBuilder { + return builder.Append(b, "Suffixes", expr).(CreateBuilder) +} + +// SetMap set columns and values for insert builder from a map of column name and value +// note that it will reset all previous columns and values was set if any +func (b CreateBuilder) SetMap(clauses map[string]string) CreateBuilder { + // Keep the columns in a consistent order by sorting the column key string. + cols := make([]string, 0, len(clauses)) + for col := range clauses { + cols = append(cols, col+" "+clauses[col]) + } + sort.Strings(cols) + + vals := make([]string, 0, len(clauses)) + for _, col := range cols { + vals = append(vals, clauses[col]) + } + + b = builder.Set(b, "Columns", cols).(CreateBuilder) + b = builder.Set(b, "Types", vals).(CreateBuilder) + + return b +} diff --git a/drop.go b/drop.go new file mode 100644 index 00000000..248a7825 --- /dev/null +++ b/drop.go @@ -0,0 +1,154 @@ +package squirrel + +import ( + "bytes" + "database/sql" + "errors" + + "github.com/lann/builder" +) + +type dropStmt struct { + PlaceholderFormat PlaceholderFormat + RunWith BaseRunner + Prefixes []Sqlizer + StatementKeyword string + Table string + Suffixes []Sqlizer +} + +func (d *dropStmt) Exec() (sql.Result, error) { + if d.RunWith == nil { + return nil, RunnerNotSet + } + return ExecWith(d.RunWith, d) +} + +func (d *dropStmt) Query() (*sql.Rows, error) { + if d.RunWith == nil { + return nil, RunnerNotSet + } + return QueryWith(d.RunWith, d) +} + +func (d *dropStmt) ToSql() (sqlStr string, args []interface{}, err error) { + if len(d.Table) == 0 { + err = errors.New("drop statements must specify a table") + return + } + + sql := &bytes.Buffer{} + + if len(d.Prefixes) > 0 { + args, err = appendToSql(d.Prefixes, sql, " ", args) + if err != nil { + return + } + + sql.WriteString(" ") + } + + if d.StatementKeyword == "" { + sql.WriteString("DROP ") + } else { + sql.WriteString(d.StatementKeyword) + sql.WriteString(" ") + } + + sql.WriteString("TABLE ") + sql.WriteString(d.Table) + sql.WriteString(" ") + + if err != nil { + return + } + + if len(d.Suffixes) > 0 { + sql.WriteString(" ") + args, err = appendToSql(d.Suffixes, sql, " ", args) + if err != nil { + return + } + } + sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String()) + return +} + +// Builder + +// DropBuilder builds SQL DROP statements. +type DropBuilder builder.Builder + +func init() { + builder.Register(DropBuilder{}, dropStmt{}) +} + +// Format methods + +// PlaceholderFormat sets PlaceholderFormat (e.g. Question or Dollar) for the +// query. +func (b DropBuilder) PlaceholderFormat(f PlaceholderFormat) DropBuilder { + return builder.Set(b, "PlaceholderFormat", f).(DropBuilder) +} + +// Runner methods + +// RunWith sets a Runner (like database/sql.DB) to be used with e.g. Exec. +func (b DropBuilder) RunWith(runner BaseRunner) DropBuilder { + return setRunWith(b, runner).(DropBuilder) +} + +// Exec builds and Execs the query with the Runner set by RunWith. +func (b DropBuilder) Exec() (sql.Result, error) { + data := builder.GetStruct(b).(dropStmt) + return data.Exec() +} + +// Query builds and Querys the query with the Runner set by RunWith. +func (b DropBuilder) Query() (*sql.Rows, error) { + data := builder.GetStruct(b).(dropStmt) + return data.Query() +} + +// SQL methods + +// ToSql builds the query into a SQL string and bound args. +func (b DropBuilder) ToSql() (string, []interface{}, error) { + data := builder.GetStruct(b).(dropStmt) + return data.ToSql() +} + +// MustSql builds the query into a SQL string and bound args. +// It panics if there are any errors. +func (b DropBuilder) MustSql() (string, []interface{}) { + sql, args, err := b.ToSql() + if err != nil { + panic(err) + } + return sql, args +} + +// Prefix adds an expression to the beginning of the query +func (b DropBuilder) Prefix(sql string, args ...interface{}) DropBuilder { + return b.PrefixExpr(Expr(sql, args...)) +} + +// PrefixExpr adds an expression to the very beginning of the query +func (b DropBuilder) PrefixExpr(expr Sqlizer) DropBuilder { + return builder.Append(b, "Prefixes", expr).(DropBuilder) +} + +// Table sets the TABLE clause of the query. +func (b DropBuilder) Table(table string) DropBuilder { + return builder.Set(b, "Table", table).(DropBuilder) +} + +// Suffix adds an expression to the end of the query +func (b DropBuilder) Suffix(sql string, args ...interface{}) DropBuilder { + return b.SuffixExpr(Expr(sql, args...)) +} + +// SuffixExpr adds an expression to the end of the query +func (b DropBuilder) SuffixExpr(expr Sqlizer) DropBuilder { + return builder.Append(b, "Suffixes", expr).(DropBuilder) +} diff --git a/statement.go b/statement.go index 9420c67f..b06d14c2 100644 --- a/statement.go +++ b/statement.go @@ -31,6 +31,16 @@ func (b StatementBuilderType) Delete(from string) DeleteBuilder { return DeleteBuilder(b).From(from) } +// Create returns a CreateBuilder for this StatementBuilderType. +func (b StatementBuilderType) Create(table string) CreateBuilder { + return CreateBuilder(b).Table(table) +} + +// Drop returns a DropBuilder for this StatementBuilderType. +func (b StatementBuilderType) Drop(table string) DropBuilder { + return DropBuilder(b).Table(table) +} + // PlaceholderFormat sets the PlaceholderFormat field for any child builders. func (b StatementBuilderType) PlaceholderFormat(f PlaceholderFormat) StatementBuilderType { return builder.Set(b, "PlaceholderFormat", f).(StatementBuilderType) @@ -87,6 +97,20 @@ func Delete(from string) DeleteBuilder { return StatementBuilder.Delete(from) } +// Create returns a new CreateBuilder with the given table name. +// +// See CreateBuilder.Table. +func Create(table string) CreateBuilder { + return StatementBuilder.Create(table) +} + +// Drop returns a new DropBuilder with the given table name. +// +// See DropBuilder.Table. +func Drop(table string) DropBuilder { + return StatementBuilder.Drop(table) +} + // Case returns a new CaseBuilder // "what" represents case value func Case(what ...interface{}) CaseBuilder { From e2f746863a471a8f56b3b80086cc42262101ed0a Mon Sep 17 00:00:00 2001 From: Alexander Loginov Date: Thu, 18 May 2023 02:12:50 +0300 Subject: [PATCH 3/4] drop, create tests --- create.go | 10 ++---- create_test.go | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++ drop.go | 1 - drop_test.go | 74 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 create_test.go create mode 100644 drop_test.go diff --git a/create.go b/create.go index c929ab6c..948c49d8 100644 --- a/create.go +++ b/create.go @@ -58,35 +58,31 @@ func (d *createStmt) ToSql() (sqlStr string, args []interface{}, err error) { sql.WriteString(" ") } - if d.StatementKeyword == "" { sql.WriteString("CREATE ") } else { sql.WriteString(d.StatementKeyword) sql.WriteString(" ") } - sql.WriteString("TABLE ") sql.WriteString(d.Table) sql.WriteString(" ") - sql.WriteString("( ") err = d.appendValuesToSQL(sql) if len(d.PrimaryKey) > 0 { sql.WriteString(", PRIMARY KEY ") sql.WriteString("(") args, err = appendToSql(d.PrimaryKey, sql, ", ", args) - sql.WriteString(") ") + sql.WriteString(")") if err != nil { return } } - sql.WriteString(") ") + sql.WriteString(" )") if err != nil { return } - if len(d.Suffixes) > 0 { sql.WriteString(" ") args, err = appendToSql(d.Suffixes, sql, " ", args) @@ -94,7 +90,6 @@ func (d *createStmt) ToSql() (sqlStr string, args []interface{}, err error) { return } } - sqlStr, err = d.PlaceholderFormat.ReplacePlaceholders(sql.String()) return } @@ -112,6 +107,7 @@ func (d *createStmt) appendValuesToSQL(w io.Writer) error { valueStrings := make([]string, len(d.Columns)) for i, col := range d.Columns { valueStrings[i] = fmt.Sprintf("%s %s", col, d.Types[i]) + valueStrings[i] = strings.TrimSuffix(valueStrings[i], " ") } io.WriteString(w, strings.Join(valueStrings, ", ")) diff --git a/create_test.go b/create_test.go new file mode 100644 index 00000000..028c0325 --- /dev/null +++ b/create_test.go @@ -0,0 +1,98 @@ +package squirrel + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateBuilderToSql(t *testing.T) { + b := Create("test"). + Prefix("WITH prefix AS ?", 0). + Columns("id", "a", "b"). + Types("INT", "TEXT", "DATE"). + PrimaryKey("id"). + Suffix("RETURNING ?", 4) + + sql, args, err := b.ToSql() + assert.NoError(t, err) + + expectedSql := + "WITH prefix AS ? " + + "CREATE TABLE test ( id INT, a TEXT, b DATE, PRIMARY KEY (id) ) " + + "RETURNING ?" + assert.Equal(t, expectedSql, sql) + + expectedArgs := []interface{}{0, 4} + assert.Equal(t, expectedArgs, args) +} + +func TestCreateBuilderSetMapToSql(t *testing.T) { + b := Create("test"). + SetMap(map[string]string{ + "a": "INT", + "b": "TEXT", + "c": "DATE", + "d": "TIMESTAMP", + }) + + sql, _, err := b.ToSql() + assert.NoError(t, err) + + expectedSql := "CREATE TABLE test ( a INT, b TEXT, c DATE, d TIMESTAMP )" + assert.Equal(t, expectedSql, sql) +} + +func TestCreateBuilderToSqlErr(t *testing.T) { + _, _, err := Create("").ToSql() + assert.Error(t, err) +} + +func TestCreateBuilderMustSql(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("TestCreateBuilderMustSql should have panicked!") + } + }() + Create("").MustSql() +} + +func TestCreateBuilderPlaceholders(t *testing.T) { + b := Create("test").Columns("id", "a", "b"). + Types("INT", "TEXT", "DATE").Suffix("SUFFIX x = ? AND y = ?") + + sql, _, _ := b.PlaceholderFormat(Question).ToSql() + assert.Equal(t, "CREATE TABLE test ( id INT, a TEXT, b DATE ) SUFFIX x = ? AND y = ?", sql) + + sql, _, _ = b.PlaceholderFormat(Dollar).ToSql() + assert.Equal(t, "CREATE TABLE test ( id INT, a TEXT, b DATE ) SUFFIX x = $1 AND y = $2", sql) +} + +func TestCreateBuilderRunners(t *testing.T) { + db := &DBStub{} + b := Create("test").Columns("id", "a", "b"). + Types("INT", "TEXT", "DATE").Suffix("SUFFIX x = ?").RunWith(db) + + expectedSql := "CREATE TABLE test ( id INT, a TEXT, b DATE ) SUFFIX x = ?" + + b.Exec() + assert.Equal(t, expectedSql, db.LastExecSql) +} + +func TestCreateBuilderNoRunner(t *testing.T) { + b := Create("test") + + _, err := b.Exec() + assert.Equal(t, RunnerNotSet, err) +} + +func TestCreateWithQuery(t *testing.T) { + db := &DBStub{} + b := Create("test").Columns("id", "a", "b"). + Types("INT", "TEXT", "DATE").Suffix("RETURNING path").RunWith(db) + + expectedSql := "CREATE TABLE test ( id INT, a TEXT, b DATE ) RETURNING path" + b.Query() + + assert.Equal(t, expectedSql, db.LastQuerySql) +} diff --git a/drop.go b/drop.go index 248a7825..989e307e 100644 --- a/drop.go +++ b/drop.go @@ -57,7 +57,6 @@ func (d *dropStmt) ToSql() (sqlStr string, args []interface{}, err error) { sql.WriteString("TABLE ") sql.WriteString(d.Table) - sql.WriteString(" ") if err != nil { return diff --git a/drop_test.go b/drop_test.go new file mode 100644 index 00000000..796718ad --- /dev/null +++ b/drop_test.go @@ -0,0 +1,74 @@ +package squirrel + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDropBuilderToSql(t *testing.T) { + b := Drop("a"). + Prefix("WITH prefix AS ?", 0). + Suffix("RETURNING ?", 4) + + sql, args, err := b.ToSql() + assert.NoError(t, err) + + expectedSql := + "WITH prefix AS ? DROP TABLE a RETURNING ?" + assert.Equal(t, expectedSql, sql) + + expectedArgs := []interface{}{0, 4} + assert.Equal(t, expectedArgs, args) +} + +func TestDropBuilderToSqlErr(t *testing.T) { + _, _, err := Drop("").ToSql() + assert.Error(t, err) +} + +func TestDropBuilderMustSql(t *testing.T) { + defer func() { + if r := recover(); r == nil { + t.Errorf("TestDropBuilderMustSql should have panicked!") + } + }() + Drop("").MustSql() +} + +func TestDropBuilderPlaceholders(t *testing.T) { + b := Drop("test").Suffix("SUFFIX x = ? AND y = ?", 1, 2) + + sql, _, _ := b.PlaceholderFormat(Question).ToSql() + assert.Equal(t, "DROP TABLE test SUFFIX x = ? AND y = ?", sql) + + sql, _, _ = b.PlaceholderFormat(Dollar).ToSql() + assert.Equal(t, "DROP TABLE test SUFFIX x = $1 AND y = $2", sql) +} + +func TestDropBuilderRunners(t *testing.T) { + db := &DBStub{} + b := Drop("test").Suffix("x = ?", 1).RunWith(db) + + expectedSql := "DROP TABLE test x = ?" + + b.Exec() + assert.Equal(t, expectedSql, db.LastExecSql) +} + +func TestDropBuilderNoRunner(t *testing.T) { + b := Drop("test") + + _, err := b.Exec() + assert.Equal(t, RunnerNotSet, err) +} + +func TestDropWithQuery(t *testing.T) { + db := &DBStub{} + b := Drop("test").Suffix("RETURNING path").RunWith(db) + + expectedSql := "DROP TABLE test RETURNING path" + b.Query() + + assert.Equal(t, expectedSql, db.LastQuerySql) +} From 35e4c4b68eed79ecae301021bb76ce571342d360 Mon Sep 17 00:00:00 2001 From: Alexander Loginov Date: Thu, 1 Jun 2023 00:42:03 +0300 Subject: [PATCH 4/4] added new functionality to tests --- select.go | 1 - select_test.go | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/select.go b/select.go index 2af39faa..00faa679 100644 --- a/select.go +++ b/select.go @@ -160,7 +160,6 @@ func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) { if len(d.AssumeOrderBy) > 0 && len(d.OrderByParts) > 0 { sql.WriteString(" ") sql.WriteString(d.AssumeOrderBy) - sql.WriteString(" ") } if len(d.OrderByParts) > 0 { diff --git a/select_test.go b/select_test.go index 80161bf5..e5a0f7e9 100644 --- a/select_test.go +++ b/select_test.go @@ -21,6 +21,9 @@ func TestSelectBuilderToSql(t *testing.T) { Column(Alias(Eq{"b": []int{101, 102, 103}}, "b_alias")). Column(Alias(subQ, "subq")). From("e"). + Index("nameIndex"). + With(Eq{"FORCE_INFER_SCHEMA": 42}). + FlattenOptional("name"). JoinClause("CROSS JOIN j1"). Join("j2"). LeftJoin("j3"). @@ -34,6 +37,7 @@ func TestSelectBuilderToSql(t *testing.T) { Where(Or{Expr("j = ?", 10), And{Eq{"k": 11}, Expr("true")}}). GroupBy("l"). Having("m = n"). + AssumeOrderBy(). OrderByClause("? DESC", 1). OrderBy("o ASC", "p DESC"). Limit(12). @@ -48,14 +52,15 @@ func TestSelectBuilderToSql(t *testing.T) { "SELECT DISTINCT a, b, c, IF(d IN (?,?,?), 1, 0) as stat_column, a > ?, " + "(b IN (?,?,?)) AS b_alias, " + "(SELECT aa, bb FROM dd) AS subq " + - "FROM e " + + "FROM e VIEW nameIndex " + + "WITH FORCE_INFER_SCHEMA = ? FLATTEN OPTIONAL BY name " + "CROSS JOIN j1 JOIN j2 LEFT JOIN j3 RIGHT JOIN j4 INNER JOIN j5 CROSS JOIN j6 " + "WHERE f = ? AND g = ? AND h = ? AND i IN (?,?,?) AND (j = ? OR (k = ? AND true)) " + - "GROUP BY l HAVING m = n ORDER BY ? DESC, o ASC, p DESC LIMIT 12 OFFSET 13 " + + "GROUP BY l HAVING m = n ASSUME ORDER BY ? DESC, o ASC, p DESC LIMIT 12 OFFSET 13 " + "FETCH FIRST ? ROWS ONLY" assert.Equal(t, expectedSql, sql) - expectedArgs := []interface{}{0, 1, 2, 3, 100, 101, 102, 103, 4, 5, 6, 7, 8, 9, 10, 11, 1, 14} + expectedArgs := []interface{}{0, 1, 2, 3, 100, 101, 102, 103, 42, 4, 5, 6, 7, 8, 9, 10, 11, 1, 14} assert.Equal(t, expectedArgs, args) }