From 9c204a9554171ae24d1f497e9e8e79b8f6fb63eb Mon Sep 17 00:00:00 2001 From: KentoNion Date: Sun, 2 Nov 2025 19:55:58 +0300 Subject: [PATCH] 02.11.2025 --- batcher/batcher.go | 64 ++++++++++++++++++++++++++-------------- btchrr.go | 2 +- dbadapter/sql_adapter.go | 7 ++--- models/models.go | 5 +++- 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/batcher/batcher.go b/batcher/batcher.go index 48c3dfd..9518c84 100644 --- a/batcher/batcher.go +++ b/batcher/batcher.go @@ -1,9 +1,9 @@ package batcher import ( - "strings" "errors" "strconv" + "strings" "btchrr/models" ) @@ -80,25 +80,33 @@ func (b *Batcher) BuildBatchQuery(singleQuery string, batchSize int, batchedItem switch placeholder { case "?": - for i :=0; i < numOfBatches; i++ { - batchQuery, err := b.buildSqliteQuery(singleQuery, batchedItems[i]) + for i := 0; i < numOfBatches; i++ { + batchQuery, err := b.buildSqliteQuery(singleQuery) if err != nil { return []models.BatchedQuery{}, err } + batchQuery.Items = batchedItems[i] batchedQueries = append(batchedQueries, batchQuery) } return case "$": - for i :=0; i < numOfBatches; i++ { - batchQuery, err := b.buildPostgresQuery(singleQuery, batchedItems[i]) + for i := 0; i < numOfBatches; i++ { + batchQuery, err := b.buildPostgresQuery(singleQuery) if err != nil { return []models.BatchedQuery{}, err } + batchQuery.Items = batchedItems[i] batchedQueries = append(batchedQueries, batchQuery) } return + case ":": + batchedQueries, err = b.buildQueryWithNamedPlaceholders(singleQuery) + if err != nil { + return []models.BatchedQuery{}, err + } + return default: - return b.buildQueryWithNamedPlaceholders(singleQuery, placeholder, batchedItems) + return []models.BatchedQuery{}, errors.New("unknown placeholder") } } @@ -112,11 +120,12 @@ func (b *Batcher) detectPlaceholders(query string) (string, error) { return "", models.ErrCannotDetectPlaceholder } -func (b *Batcher) buildSqliteQuery(singleQuery string, batchedItems []any) (batchedQueries models.BatchedQuery, err error) { +// buildSqliteQuery - builds a batch query with sqlite placeholders +func (b *Batcher) buildSqliteQuery(singleQuery string) (batchedQuery models.BatchedQuery, err error) { // INSERT INTO users VALUES (?, ?); idx := strings.Index(strings.ToLower(singleQuery), "values") if idx == -1 { - return "", errors.New("no values found in query") + return models.BatchedQuery{}, errors.New("no values found in query") } prefix := singleQuery[:idx+6] // +6 - values @@ -124,23 +133,23 @@ func (b *Batcher) buildSqliteQuery(singleQuery string, batchedItems []any) (batc numOfInserts := strings.Count(valuesPart, "?") if numOfInserts == 0 { - return "", errors.New("no placeholders found in query") + return models.BatchedQuery{}, errors.New("no placeholders found in query") } - singleItemValues := "(" + strings.Repeat("?, ", numOfInserts - 1) + "?)" + singleItemValues := "(" + strings.Repeat("?, ", numOfInserts-1) + "?)" //INSERT INTO users (name, age) VALUES (?, ?), (?, ?), (?, ?); - newValues := strings.Repeat(singleItemValues + ", ", b.batchSize - 1) + singleItemValues - newQuery := prefix + " " + newValues + ";" + newValues := strings.Repeat(singleItemValues+", ", b.batchSize-1) + singleItemValues + batchedQuery.Query = prefix + " " + newValues + ";" - - return models.BatchedQuery(newQuery), nil + return } -func (b *Batcher) buildPostgresQuery(singleQuery string, batchedItems []any) (batchedQueries models.BatchedQuery, err error) { +// buildPostgresQuery - builds a batch query with postgres placeholders +func (b *Batcher) buildPostgresQuery(singleQuery string) (batchedQuery models.BatchedQuery, err error) { // INSERT INTO users VALUES ($1, $2); idx := strings.Index(strings.ToLower(singleQuery), "values") if idx == -1 { - return "", errors.New("no values found in query") + return models.BatchedQuery{}, errors.New("no values found in query") } prefix := singleQuery[:idx+6] // +6 - values @@ -148,13 +157,13 @@ func (b *Batcher) buildPostgresQuery(singleQuery string, batchedItems []any) (ba numOfInserts := strings.Count(valuesPart, "$") if numOfInserts == 0 { - return "", errors.New("no placeholders found in query") + return models.BatchedQuery{}, errors.New("no placeholders found in query") } //INSERT INTO users VALUES ($1, $2), ($3, $4), ($5, $6); placeholderNum := 1 query := "" - for i := 0; i < b.batchSize ; i++ { + for i := 0; i < b.batchSize; i++ { if i > 0 { query += ", " } @@ -166,11 +175,22 @@ func (b *Batcher) buildPostgresQuery(singleQuery string, batchedItems []any) (ba query += ")" } - query = prefix + " " + query + ";" + batchedQuery.Query = prefix + " " + query + ";" - return models.BatchedQuery(query), nil + return } -func (b *Batcher) buildQueryWithNamedPlaceholders(singleQuery string, placeholder string, batchedItems [][]any) (batchedQueries []models.BatchedQuery, err error) { - return []models.BatchedQuery{}, errors.New("not implemented") +// buildQueryWithNamedPlaceholders - builds a batch query with named placeholders +func (b *Batcher) buildQueryWithNamedPlaceholders(singleQuery string) (batchedQuery models.BatchedQuery, err error) { + // INSERT INTO users (name, age) VALUES (:name, :age); + idx := strings.Index(strings.ToLower(singleQuery), "values") + if idx == -1 { + return models.BatchedQuery{}, errors.New("no values found in query") + } + + prefix := singleQuery[:idx+6] // +6 - values + valuesPart := singleQuery[idx+6:] + + + return } diff --git a/btchrr.go b/btchrr.go index fd9b7f9..55dce68 100644 --- a/btchrr.go +++ b/btchrr.go @@ -52,7 +52,7 @@ func NewBtchrr(batchSize int, db *sql.DB) (*Btchrr, error) { } // Exec - accepts a query for single item, items and executes it in batches -func (b *Btchrr) Exec(ctx context.Context, query string, items []any) (sql.Result, error) { +func (b *Btchrr) ExecWitchContext(ctx context.Context, query string, items []any) (sql.Result, error) { err := b.executor.CheckQuery(query) if err != nil { return nil, err diff --git a/dbadapter/sql_adapter.go b/dbadapter/sql_adapter.go index e59747a..bca37d4 100644 --- a/dbadapter/sql_adapter.go +++ b/dbadapter/sql_adapter.go @@ -16,16 +16,15 @@ func NewSQLAdapter(db *sql.DB) *DBAdapter { } func (a *DBAdapter) Exec(ctx context.Context, batchedQuery models.BatchedQuery) (sql.Result, error) { - res, err := a.db.ExecContext(ctx, string(batchedQuery)) - + res, err := a.db.ExecContext(ctx, batchedQuery.Query, batchedQuery.Items...) return res, err } // CheckQuery - checks if the query is valid func (a *DBAdapter) CheckQuery(query string) error { - stmt, err := a.db.Prepare(query) + stmt, err := a.db.Prepare(query) if err != nil { - return err + return err } stmt.Close() return nil diff --git a/models/models.go b/models/models.go index 85a3a23..8a0c7d2 100644 --- a/models/models.go +++ b/models/models.go @@ -9,4 +9,7 @@ var ( ) // BatchedQuery - single batch query -type BatchedQuery string +type BatchedQuery struct { + Query string + Items []any +}