diff --git a/README.md b/README.md index 7d1a2b6..7e0a263 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,24 @@ Note that if a subquery uses no arguments, it's more effective to add it as SQL q.Close() ``` +To select from sub-query pass an empty string to From and immediately call a SubQuery method: + +```go + q := sqlf.Select(""). + From(""). + SubQuery( + "(", ") counted_news", + sqlf.From("news"). + Select("id, section, header, score"). + Select("row_number() OVER (PARTITION BY section ORDER BY score DESC) AS rating_in_section"). + OrderBy("section, rating_in_section")). + Where("rating_in_section <= 5") + // ... + q.Close() +``` + +The query constructed by this example returns top 5 news in each section. + #### Unions Use `Union` method to combine results of two queries: diff --git a/dialect.go b/dialect.go index 6af8129..5c0583b 100644 --- a/dialect.go +++ b/dialect.go @@ -98,7 +98,7 @@ func (b Dialect) DeleteFrom(tableName string) *Stmt { } // writePg function copies s into buf and replaces ? placeholders with $1, $2... -func writePg(argNo int64, s []byte, buf *bytebufferpool.ByteBuffer) (int64, error) { +func writePg(argNo int, s []byte, buf *bytebufferpool.ByteBuffer) (int, error) { var err error start := 0 // Iterate by runes diff --git a/example_test.go b/example_test.go index 70c2c53..f99e5e1 100644 --- a/example_test.go +++ b/example_test.go @@ -203,6 +203,22 @@ func ExampleStmt_With() { // WITH regional_sales AS (SELECT region, SUM(amount) AS total_sales FROM orders GROUP BY region), top_regions AS (SELECT region FROM regional_sales WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)) SELECT region, product, SUM(quantity) AS product_units, SUM(amount) AS product_sales FROM orders WHERE region IN (SELECT region FROM top_regions) GROUP BY region, product } +func ExampleStmt_From() { + q := sqlf.Select("*"). + From(""). + SubQuery( + "(", ") counted_news", + sqlf.From("news"). + Select("id, section, header, score"). + Select("row_number() OVER (PARTITION BY section ORDER BY score DESC) AS rating_in_section"). + OrderBy("section, rating_in_section")). + Where("rating_in_section <= 5") + fmt.Println(q.String()) + q.Close() + // Output: + //SELECT * FROM (SELECT id, section, header, score, row_number() OVER (PARTITION BY section ORDER BY score DESC) AS rating_in_section FROM news ORDER BY section, rating_in_section) counted_news WHERE rating_in_section <= 5 +} + func ExampleStmt_SubQuery() { q := sqlf.From("orders o"). Select("date, region"). diff --git a/stmt.go b/stmt.go index 0992717..2f00bd4 100644 --- a/stmt.go +++ b/stmt.go @@ -532,7 +532,7 @@ func (q *Stmt) Clause(expr string, args ...interface{}) *Stmt { // String method builds and returns an SQL statement. func (q *Stmt) String() string { if q.sql == nil { - var argNo int64 = 1 + var argNo int = 1 // Build a query buf := getBuffer() q.sql = buf @@ -666,6 +666,10 @@ loop: case chunk.pos == pos: // Do nothing if a clause is already there and no expressions are to be added if expr == "" { + // See if arguments are to be updated + if argLen > 0 { + copy(q.args[len(q.args)-argTail-chunk.argLen:], args) + } return i } // Write a separator diff --git a/stmt_test.go b/stmt_test.go index 3cb250b..98d40e6 100644 --- a/stmt_test.go +++ b/stmt_test.go @@ -244,3 +244,15 @@ func TestUnion(t *testing.T) { defer q.Close() assert.Equal(t, "SELECT id, status FROM tasks WHERE status = ? UNION SELECT id, status FROM tasks WHERE status = ?", q.String()) } + +func TestLimit(t *testing.T) { + q := sqlf.From("items"). + Select("id"). + Where("id > ?", 42). + Limit(10). + Limit(11). + Limit(20) + defer q.Close() + assert.Equal(t, "SELECT id FROM items WHERE id > ? LIMIT ?", q.String()) + assert.Equal(t, []interface{}{42, 20}, q.Args()) +}