80 lines
1.8 KiB
Go
80 lines
1.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"regexp"
|
|
)
|
|
|
|
var tableNameRegex = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
|
|
|
|
type DataService struct {
|
|
db *sql.DB
|
|
schema string
|
|
}
|
|
|
|
func NewDataService(db *sql.DB, schema string) *DataService {
|
|
return &DataService{db: db, schema: schema}
|
|
}
|
|
|
|
func (s *DataService) Select(ctx context.Context, table string, offset, count int) ([]map[string]any, error) {
|
|
if !tableNameRegex.MatchString(table) {
|
|
return nil, fmt.Errorf("invalid table name")
|
|
}
|
|
if s.schema != "" && !tableNameRegex.MatchString(s.schema) {
|
|
return nil, fmt.Errorf("invalid schema name")
|
|
}
|
|
|
|
qualifiedTable := table
|
|
if s.schema != "" {
|
|
qualifiedTable = fmt.Sprintf("%s.%s", s.schema, table)
|
|
}
|
|
|
|
query := fmt.Sprintf("SELECT * FROM %s LIMIT $2 OFFSET $1", qualifiedTable)
|
|
rows, err := s.db.QueryContext(ctx, query, offset, count)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query table %s: %w", table, err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
return scanRows(rows)
|
|
}
|
|
|
|
func scanRows(rows *sql.Rows) ([]map[string]any, error) {
|
|
columns, err := rows.Columns()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read columns: %w", err)
|
|
}
|
|
|
|
results := make([]map[string]any, 0)
|
|
values := make([]any, len(columns))
|
|
valuePtrs := make([]any, len(columns))
|
|
for i := range values {
|
|
valuePtrs[i] = &values[i]
|
|
}
|
|
|
|
for rows.Next() {
|
|
if err := rows.Scan(valuePtrs...); err != nil {
|
|
return nil, fmt.Errorf("scan row: %w", err)
|
|
}
|
|
|
|
row := make(map[string]any, len(columns))
|
|
for i, col := range columns {
|
|
val := values[i]
|
|
if b, ok := val.([]byte); ok {
|
|
row[col] = string(b)
|
|
continue
|
|
}
|
|
row[col] = val
|
|
}
|
|
results = append(results, row)
|
|
}
|
|
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate rows: %w", err)
|
|
}
|
|
|
|
return results, nil
|
|
}
|