forked from nao1215/filesql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtable.go
More file actions
127 lines (112 loc) · 3.27 KB
/
table.go
File metadata and controls
127 lines (112 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package filesql
import (
"path/filepath"
"strings"
)
// table represents file contents as database table structure.
type table struct {
// Name is table name derived from file path.
name TableName
// header is table header.
header header
// records is table records.
records []Record
// columnInfo contains inferred type information for each column
columnInfo []columnInfo
}
// newTable create new table.
func newTable(
name string,
header header,
records []Record,
) *table {
// Infer column types from data
columnInfo := newColumnInfoList(header, records)
return &table{
name: NewTableName(name),
header: header,
records: records,
columnInfo: columnInfo,
}
}
// getName return table name.
func (t *table) getName() string {
return t.name.String()
}
// getHeader return table header.
func (t *table) getHeader() header {
return t.header
}
// getRecords return table records.
func (t *table) getRecords() []Record {
return t.records
}
// equal compare table.
func (t *table) equal(t2 *table) bool {
if t.getName() != t2.getName() {
return false
}
if !t.header.equal(t2.header) {
return false
}
if len(t.getRecords()) != len(t2.getRecords()) {
return false
}
for i, record := range t.getRecords() {
if !record.equal(t2.getRecords()[i]) {
return false
}
}
return true
}
// tableFromFilePath creates table name from file path
func tableFromFilePath(filePath string) string {
fileName := filepath.Base(filePath)
// Remove compression extensions first
for _, ext := range []string{extGZ, extBZ2, extXZ, extZSTD} {
if strings.HasSuffix(fileName, ext) {
fileName = strings.TrimSuffix(fileName, ext)
break
}
}
// Then remove the file type extension
return strings.TrimSuffix(fileName, filepath.Ext(fileName))
}
// sanitizeTableName removes invalid characters from table names and ensures SQL-safe identifiers.
// This function is automatically applied to all table names generated from file paths to prevent
// SQL syntax errors caused by special characters like hyphens, spaces, and other symbols.
//
// Transformations applied:
// - Replaces spaces, hyphens (-), and dots (.) with underscores (_)
// - Removes any non-alphanumeric characters except underscores
// - Adds "sheet_" prefix if the name starts with a number
// - Returns "sheet" as fallback for empty names
//
// Example:
//
// sanitizeTableName("with-hyphens") // returns "with_hyphens"
// sanitizeTableName("data.backup") // returns "data_backup"
// sanitizeTableName("test@#$%") // returns "test_"
func sanitizeTableName(name string) string {
// Replace spaces and invalid characters with underscores
result := strings.ReplaceAll(name, " ", "_")
result = strings.ReplaceAll(result, "-", "_")
result = strings.ReplaceAll(result, ".", "_")
// Remove any non-alphanumeric characters except underscore
var sanitized strings.Builder
for _, r := range result {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' {
sanitized.WriteRune(r)
}
}
finalResult := sanitized.String()
// Ensure it doesn't start with a number
if len(finalResult) > 0 && finalResult[0] >= '0' && finalResult[0] <= '9' {
finalResult = "sheet_" + finalResult
}
// Ensure it's not empty
if finalResult == "" {
finalResult = "sheet"
}
return finalResult
}