mirror of https://github.com/VinGarcia/ksql.git
Add new API for querying chunks
parent
ed0327babe
commit
4220002694
32
ksql.go
32
ksql.go
|
@ -122,6 +122,16 @@ func (c DB) Query(
|
||||||
query string,
|
query string,
|
||||||
params ...interface{},
|
params ...interface{},
|
||||||
) error {
|
) error {
|
||||||
|
// Check if we the user wants to use the lazy chunked approach:
|
||||||
|
if chunksPtr, ok := records.(*Chunks); ok {
|
||||||
|
*chunksPtr = chunks{
|
||||||
|
db: c,
|
||||||
|
query: query,
|
||||||
|
params: params,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
slicePtr := reflect.ValueOf(records)
|
slicePtr := reflect.ValueOf(records)
|
||||||
slicePtrType := slicePtr.Type()
|
slicePtrType := slicePtr.Type()
|
||||||
if slicePtrType.Kind() != reflect.Ptr {
|
if slicePtrType.Kind() != reflect.Ptr {
|
||||||
|
@ -271,6 +281,28 @@ func (c DB) QueryOne(
|
||||||
return rows.Close()
|
return rows.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Chunks interface {
|
||||||
|
ForEach(ctx context.Context, chunkSize int, fn interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// chunks stores a query to be executed lazily afterwards.
|
||||||
|
// the chunks ForEach function can be called any number of times and will
|
||||||
|
// repeat the query each time it is called.
|
||||||
|
type chunks struct {
|
||||||
|
db Provider
|
||||||
|
query string
|
||||||
|
params []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c chunks) ForEach(ctx context.Context, chunkSize int, fn interface{}) error {
|
||||||
|
return c.db.QueryChunks(ctx, ChunkParser{
|
||||||
|
ChunkSize: chunkSize,
|
||||||
|
Query: c.query,
|
||||||
|
Params: c.params,
|
||||||
|
ForEachChunk: fn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// QueryChunks is meant to perform queries that returns
|
// QueryChunks is meant to perform queries that returns
|
||||||
// more results than would normally fit on memory,
|
// more results than would normally fit on memory,
|
||||||
// for others cases the Query and QueryOne functions are indicated.
|
// for others cases the Query and QueryOne functions are indicated.
|
||||||
|
|
|
@ -70,7 +70,22 @@ func RunTestsForAdapter(
|
||||||
InsertTest(t, driver, connStr, newDBAdapter)
|
InsertTest(t, driver, connStr, newDBAdapter)
|
||||||
DeleteTest(t, driver, connStr, newDBAdapter)
|
DeleteTest(t, driver, connStr, newDBAdapter)
|
||||||
UpdateTest(t, driver, connStr, newDBAdapter)
|
UpdateTest(t, driver, connStr, newDBAdapter)
|
||||||
QueryChunksTest(t, driver, connStr, newDBAdapter)
|
|
||||||
|
// We are keeping this callback to simplify how we are testing both ways of querying chunks.
|
||||||
|
// In the future we plan on deprecating and eventually deleting one of them:
|
||||||
|
QueryChunksTest(t, driver, connStr, newDBAdapter, func(db Provider, ctx context.Context, parser ChunkParser) error {
|
||||||
|
return db.QueryChunks(ctx, parser)
|
||||||
|
})
|
||||||
|
QueryChunksTest(t, driver, connStr, newDBAdapter, func(db Provider, ctx context.Context, parser ChunkParser) error {
|
||||||
|
var chunks Chunks
|
||||||
|
err := db.Query(ctx, &chunks, parser.Query, parser.Params...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks.ForEach(ctx, parser.ChunkSize, parser.ForEachChunk)
|
||||||
|
})
|
||||||
|
|
||||||
TransactionTest(t, driver, connStr, newDBAdapter)
|
TransactionTest(t, driver, connStr, newDBAdapter)
|
||||||
ScanRowsTest(t, driver, connStr, newDBAdapter)
|
ScanRowsTest(t, driver, connStr, newDBAdapter)
|
||||||
})
|
})
|
||||||
|
@ -1531,6 +1546,7 @@ func QueryChunksTest(
|
||||||
driver string,
|
driver string,
|
||||||
connStr string,
|
connStr string,
|
||||||
newDBAdapter func(t *testing.T) (DBAdapter, io.Closer),
|
newDBAdapter func(t *testing.T) (DBAdapter, io.Closer),
|
||||||
|
queryChunks func(Provider, context.Context, ChunkParser) error,
|
||||||
) {
|
) {
|
||||||
t.Run("QueryChunks", func(t *testing.T) {
|
t.Run("QueryChunks", func(t *testing.T) {
|
||||||
variations := []struct {
|
variations := []struct {
|
||||||
|
@ -1567,7 +1583,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
var length int
|
var length int
|
||||||
var u user
|
var u user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `FROM users WHERE name = ` + c.dialect.Placeholder(0),
|
Query: variation.queryPrefix + `FROM users WHERE name = ` + c.dialect.Placeholder(0),
|
||||||
Params: []interface{}{"User1"},
|
Params: []interface{}{"User1"},
|
||||||
|
|
||||||
|
@ -1605,7 +1621,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1647,7 +1663,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1690,7 +1706,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1713,7 +1729,6 @@ func QueryChunksTest(
|
||||||
assert.Equal(t, []int{2, 1}, lengths)
|
assert.Equal(t, []int{2, 1}, lengths)
|
||||||
})
|
})
|
||||||
|
|
||||||
// xxx
|
|
||||||
t.Run("should query joined tables correctly", func(t *testing.T) {
|
t.Run("should query joined tables correctly", func(t *testing.T) {
|
||||||
// This test only makes sense with no query prefix
|
// This test only makes sense with no query prefix
|
||||||
if variation.queryPrefix != "" {
|
if variation.queryPrefix != "" {
|
||||||
|
@ -1747,7 +1762,7 @@ func QueryChunksTest(
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
var posts []post
|
var posts []post
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: fmt.Sprint(
|
Query: fmt.Sprint(
|
||||||
`FROM users u JOIN posts p ON p.user_id = u.id`,
|
`FROM users u JOIN posts p ON p.user_id = u.id`,
|
||||||
` WHERE u.name like `, c.dialect.Placeholder(0),
|
` WHERE u.name like `, c.dialect.Placeholder(0),
|
||||||
|
@ -1803,7 +1818,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1843,7 +1858,7 @@ func QueryChunksTest(
|
||||||
returnVals := []error{nil, ErrAbortIteration}
|
returnVals := []error{nil, ErrAbortIteration}
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1885,7 +1900,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1925,7 +1940,7 @@ func QueryChunksTest(
|
||||||
returnVals := []error{nil, errors.New("fake error msg")}
|
returnVals := []error{nil, errors.New("fake error msg")}
|
||||||
var lengths []int
|
var lengths []int
|
||||||
var users []user
|
var users []user
|
||||||
err = c.QueryChunks(ctx, ChunkParser{
|
err = queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
Query: variation.queryPrefix + `from users where name like ` + c.dialect.Placeholder(0) + ` order by name asc;`,
|
||||||
Params: []interface{}{"User%"},
|
Params: []interface{}{"User%"},
|
||||||
|
|
||||||
|
@ -1982,7 +1997,7 @@ func QueryChunksTest(
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fn := range funcs {
|
for _, fn := range funcs {
|
||||||
err := c.QueryChunks(ctx, ChunkParser{
|
err := queryChunks(c, ctx, ChunkParser{
|
||||||
Query: variation.queryPrefix + `FROM users`,
|
Query: variation.queryPrefix + `FROM users`,
|
||||||
Params: []interface{}{},
|
Params: []interface{}{},
|
||||||
|
|
||||||
|
@ -1999,7 +2014,7 @@ func QueryChunksTest(
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
c := newTestDB(db, driver)
|
c := newTestDB(db, driver)
|
||||||
err := c.QueryChunks(ctx, ChunkParser{
|
err := queryChunks(c, ctx, ChunkParser{
|
||||||
Query: `SELECT * FROM not a valid query`,
|
Query: `SELECT * FROM not a valid query`,
|
||||||
Params: []interface{}{},
|
Params: []interface{}{},
|
||||||
|
|
||||||
|
@ -2018,7 +2033,7 @@ func QueryChunksTest(
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
c := newTestDB(db, driver)
|
c := newTestDB(db, driver)
|
||||||
|
|
||||||
err := c.QueryChunks(ctx, ChunkParser{
|
err := queryChunks(c, ctx, ChunkParser{
|
||||||
Query: `SELECT * FROM users u JOIN posts p ON u.id = p.user_id`,
|
Query: `SELECT * FROM users u JOIN posts p ON u.id = p.user_id`,
|
||||||
Params: []interface{}{},
|
Params: []interface{}{},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue