Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion internal/cmd/group_flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"time"
Expand All @@ -19,6 +20,15 @@ import (
"github.com/tursodatabase/turso-cli/internal/turso"
)

var validTableName = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)

func validateCSVTableName(name string) error {
if !validTableName.MatchString(name) {
return fmt.Errorf("invalid --csv-table-name %q: must start with a letter or underscore and contain only letters, digits, and underscores", name)
}
return nil
}

var groupBoolFlag bool

func addGroupBoolFlag(cmd *cobra.Command, description string) {
Expand Down Expand Up @@ -80,6 +90,11 @@ func parseDBSeedFlags(client *turso.Client, isAWS bool, cipher string) (*turso.D
if csvTableNameFlag != "" && fromCSVFlag == "" {
return nil, errors.New("--from-csv must be used with --csv-table-name")
}
if csvTableNameFlag != "" {
if err := validateCSVTableName(csvTableNameFlag); err != nil {
return nil, err
}
}

if fromDBFlag != "" {
return &turso.DBSeed{Type: "database", Name: fromDBFlag, Timestamp: timestamp}, nil
Expand Down Expand Up @@ -482,7 +497,7 @@ func importCSVIntoSQLite(tempDB *os.File, csvFile, csvTableName string, separato
cmd.Stderr = stdErr

if err := cmd.Run(); err != nil {
return fmt.Errorf("could not load csv into new database file: %w: %x", err, stdErr.Bytes())
return fmt.Errorf("could not load csv into new database file: %w: %s", err, stdErr.String())
}
return nil
}
31 changes: 31 additions & 0 deletions internal/cmd/group_flag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ func createTestDatabase(t *testing.T, sizeBytes int) string {
return dbPath
}

func TestValidateCSVTableName(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
errContains string
}{
{name: "simple name", input: "users", wantErr: false},
{name: "underscore prefix", input: "_table", wantErr: false},
{name: "with numbers", input: "table123", wantErr: false},
{name: "mixed valid", input: "my_table_2", wantErr: false},
{name: "hyphen rejected", input: "some-table", wantErr: true, errContains: "invalid --csv-table-name"},
{name: "starts with digit", input: "1table", wantErr: true, errContains: "invalid --csv-table-name"},
{name: "empty string", input: "", wantErr: true, errContains: "invalid --csv-table-name"},
{name: "space in name", input: "my table", wantErr: true, errContains: "invalid --csv-table-name"},
{name: "dot in name", input: "my.table", wantErr: true, errContains: "invalid --csv-table-name"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateCSVTableName(tt.input)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errContains)
} else {
require.NoError(t, err)
}
})
}
}

func TestRunQuickCheck(t *testing.T) {
if _, err := exec.LookPath("sqlite3"); err != nil {
t.Skip("sqlite3 not available, skipping test")
Expand Down
Loading