Skip to content

Commit 8bab075

Browse files
committed
csv-table-name: reject invalid SQLite identifiers up front
.import interpolates the table name directly into a CREATE TABLE statement, so passing something with a dash or a quote produced a cryptic sqlite syntax error that the user had to decode. Validate the name as a conventional unquoted SQLite identifier (letter or underscore, then letters / digits / underscores) before invoking sqlite3 so the message is actionable. Closes the validation half of #810. Signed-off-by: Charlie Tonneslan <cst0520@gmail.com>
1 parent 2e8610c commit 8bab075

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

internal/cmd/group_flag.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ func parseDBSeedFlags(client *turso.Client, isAWS bool, cipher string) (*turso.D
7474
if csvTableNameFlag != "" && fromCSVFlag == "" {
7575
return nil, errors.New("--from-csv must be used with --csv-table-name")
7676
}
77+
if csvTableNameFlag != "" && !isValidCSVTableName(csvTableNameFlag) {
78+
return nil, fmt.Errorf("invalid --csv-table-name %q: must start with a letter or underscore and contain only letters, digits, and underscores", csvTableNameFlag)
79+
}
7780

7881
if fromDBFlag != "" {
7982
return &turso.DBSeed{Type: "database", Name: fromDBFlag, Timestamp: timestamp}, nil
@@ -479,3 +482,28 @@ func importCSVIntoSQLite(tempDB *os.File, csvFile, csvTableName string, separato
479482
}
480483
return nil
481484
}
485+
486+
// isValidCSVTableName accepts the conventional SQLite unquoted
487+
// identifier shape: a letter or underscore followed by letters,
488+
// digits, or underscores. The .import command interpolates the name
489+
// directly into a SQL CREATE TABLE statement, so anything weirder
490+
// would either produce a confusing sqlite syntax error or accept a
491+
// name the user can't easily query afterwards. See #810.
492+
func isValidCSVTableName(name string) bool {
493+
if name == "" {
494+
return false
495+
}
496+
for i, r := range name {
497+
switch {
498+
case r == '_':
499+
continue
500+
case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z':
501+
continue
502+
case i > 0 && r >= '0' && r <= '9':
503+
continue
504+
default:
505+
return false
506+
}
507+
}
508+
return true
509+
}

0 commit comments

Comments
 (0)