|
|
@@ -32,8 +32,8 @@ public class TdEngineService {
|
|
32
|
32
|
private final Map<String, Set<String>> stableColumnCache = new ConcurrentHashMap<>();
|
|
33
|
33
|
private static final int MAX_CACHE_SIZE = 1000;
|
|
34
|
34
|
|
|
35
|
|
- // 允许的列名字符(仅 ASCII 字母、数字、下划线)
|
|
36
|
|
- private static final String ALLOWED_COLUMNS = "^[a-zA-Z_][a-zA-Z0-9_]*$";
|
|
|
35
|
+ // 允许的列名字符(支持中文、ASCII字母、数字、下划线)
|
|
|
36
|
+ private static final String ALLOWED_COLUMNS = "^[a-zA-Z_一-龥][a-zA-Z0-9_一-龥]*$";
|
|
37
|
37
|
|
|
38
|
38
|
// 允许的表名/数据库名字符(允许数字开头和连字符,如 UUID 格式的表名)
|
|
39
|
39
|
private static final String ALLOWED_TABLE_NAME = "^[a-zA-Z0-9][a-zA-Z0-9_-]*$";
|
|
|
@@ -122,7 +122,7 @@ public class TdEngineService {
|
|
122
|
122
|
if (name == null || name.isEmpty()) {
|
|
123
|
123
|
return "`unknown`";
|
|
124
|
124
|
}
|
|
125
|
|
- return "`" + name.replace("`", "") + "`";
|
|
|
125
|
+ return "`" + name.replaceAll("`", "") + "`";
|
|
126
|
126
|
}
|
|
127
|
127
|
|
|
128
|
128
|
private boolean isValidFieldName(String name) {
|
|
|
@@ -133,6 +133,18 @@ public class TdEngineService {
|
|
133
|
133
|
return name != null && name.matches(ALLOWED_TABLE_NAME);
|
|
134
|
134
|
}
|
|
135
|
135
|
|
|
|
136
|
+ private boolean isNumeric(String str) {
|
|
|
137
|
+ if (str == null || str.isEmpty()) {
|
|
|
138
|
+ return false;
|
|
|
139
|
+ }
|
|
|
140
|
+ try {
|
|
|
141
|
+ Double.parseDouble(str);
|
|
|
142
|
+ return true;
|
|
|
143
|
+ } catch (NumberFormatException e) {
|
|
|
144
|
+ return false;
|
|
|
145
|
+ }
|
|
|
146
|
+ }
|
|
|
147
|
+
|
|
136
|
148
|
// === 缓存工具方法 ===
|
|
137
|
149
|
private String getStableKey(String dbName, String stableName) {
|
|
138
|
150
|
return dbName + "." + stableName;
|
|
|
@@ -196,7 +208,7 @@ public class TdEngineService {
|
|
196
|
208
|
|
|
197
|
209
|
// 创建超级表:固定 ts + surfacename,无 ext_data 列
|
|
198
|
210
|
String stableSql = String.format(
|
|
199
|
|
- "CREATE STABLE IF NOT EXISTS %s.%s (ts TIMESTAMP, surfacename VARCHAR(64)) TAGS (location BINARY(64))",
|
|
|
211
|
+ "CREATE STABLE IF NOT EXISTS %s.%s (ts TIMESTAMP, surfacename VARCHAR(64)) TAGS (location VARCHAR(255))",
|
|
200
|
212
|
wrapName(dbName),
|
|
201
|
213
|
wrapName(superTableName)
|
|
202
|
214
|
);
|
|
|
@@ -225,7 +237,7 @@ public class TdEngineService {
|
|
225
|
237
|
// ==========================================
|
|
226
|
238
|
// 批量插入(按列存储)
|
|
227
|
239
|
// ==========================================
|
|
228
|
|
- public void insertBatch(String dbName, String table, List<Map<String, Object>> dataList)
|
|
|
240
|
+ public void insertBatch(String dbName, String superTable,String table, List<Map<String, Object>> dataList)
|
|
229
|
241
|
throws SQLException {
|
|
230
|
242
|
|
|
231
|
243
|
if (dataList == null || dataList.isEmpty()) {
|
|
|
@@ -233,12 +245,12 @@ public class TdEngineService {
|
|
233
|
245
|
return;
|
|
234
|
246
|
}
|
|
235
|
247
|
|
|
236
|
|
- String superTableName = extractSuperTableName(table);
|
|
|
248
|
+// String superTableName = extractSuperTableName(table);
|
|
237
|
249
|
|
|
238
|
250
|
int batchSize = DEFAULT_BATCH_SIZE;
|
|
239
|
251
|
for (int i = 0; i < dataList.size(); i += batchSize) {
|
|
240
|
252
|
List<Map<String, Object>> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
|
|
241
|
|
- insertBatchInternal(dbName, superTableName, table, batch);
|
|
|
253
|
+ insertBatchInternal(dbName, superTable, table, batch);
|
|
242
|
254
|
}
|
|
243
|
255
|
|
|
244
|
256
|
log.info("批量写入成功: {} | 条数: {}", table, dataList.size());
|
|
|
@@ -261,10 +273,13 @@ public class TdEngineService {
|
|
261
|
273
|
for (Map.Entry<String, Object> entry : data.entrySet()) {
|
|
262
|
274
|
String key = entry.getKey();
|
|
263
|
275
|
if (isValidFieldName(key) && !isReservedColumn(key) && !columnTypes.containsKey(key)) {
|
|
264
|
|
- columnTypes.put(key, getValueType(entry.getValue()));
|
|
|
276
|
+ // 统一使用 VARCHAR,避免类型推断导致的问题
|
|
|
277
|
+ columnTypes.put(key, "VARCHAR");
|
|
265
|
278
|
}
|
|
266
|
279
|
}
|
|
267
|
280
|
}
|
|
|
281
|
+
|
|
|
282
|
+ log.debug("收集到的列: {} | 共 {} 列", columnTypes.keySet(), columnTypes.size());
|
|
268
|
283
|
return columnTypes;
|
|
269
|
284
|
}
|
|
270
|
285
|
|
|
|
@@ -305,7 +320,7 @@ public class TdEngineService {
|
|
305
|
320
|
if (!hasData) {
|
|
306
|
321
|
return null;
|
|
307
|
322
|
}
|
|
308
|
|
-
|
|
|
323
|
+ log.info("生成的 INSERT SQL | 列类型: {} | SQL 前100字符: {}", columnTypes, sql.toString().substring(0, Math.min(100, sql.toString().length())));
|
|
309
|
324
|
sql.setLength(sql.length() - 1);
|
|
310
|
325
|
return sql.toString();
|
|
311
|
326
|
}
|
|
|
@@ -329,7 +344,9 @@ public class TdEngineService {
|
|
329
|
344
|
return;
|
|
330
|
345
|
}
|
|
331
|
346
|
|
|
|
347
|
+ log.info("收集到的列类型: {}", columnTypes);
|
|
332
|
348
|
Set<String> existingColumns = getStableColumns(dbName, superTableName);
|
|
|
349
|
+ log.info("超级表已有列: {}", existingColumns);
|
|
333
|
350
|
// 计算实际新增列数(扣除已有列的交集)
|
|
334
|
351
|
int newColumns = 0;
|
|
335
|
352
|
for (String col : columnTypes.keySet()) {
|
|
|
@@ -542,8 +559,8 @@ public class TdEngineService {
|
|
542
|
559
|
}
|
|
543
|
560
|
|
|
544
|
561
|
try (Connection conn = getConnection()) {
|
|
545
|
|
- // 检查超级表是否存在 — 使用 PreparedStatement 防止 SQL 注入
|
|
546
|
|
- String checkStableSql = "SELECT * FROM information_schema.ins_tables WHERE table_name = ? AND db_name = ?";
|
|
|
562
|
+ // 检查超级表是否存在(stable_name 字段标识超级表)
|
|
|
563
|
+ String checkStableSql = "SELECT * FROM information_schema.ins_tables WHERE stable_name = ? AND db_name = ?";
|
|
547
|
564
|
try (PreparedStatement pStmt = conn.prepareStatement(checkStableSql)) {
|
|
548
|
565
|
pStmt.setString(1, superTableName);
|
|
549
|
566
|
pStmt.setString(2, dbName);
|
|
|
@@ -557,9 +574,9 @@ public class TdEngineService {
|
|
557
|
574
|
}
|
|
558
|
575
|
}
|
|
559
|
576
|
|
|
560
|
|
- // 检查子表是否存在 — 使用 PreparedStatement 防止 SQL 注入
|
|
|
577
|
+ // 检查子表是否存在
|
|
561
|
578
|
String checkTableSql = "SELECT * FROM information_schema.ins_tables " +
|
|
562
|
|
- "WHERE table_name = ? AND db_name = ? AND table_type = 'CHILD_TABLE'";
|
|
|
579
|
+ "WHERE table_name = ? AND db_name = ? AND type = 'CHILD_TABLE'";
|
|
563
|
580
|
try (PreparedStatement pStmt = conn.prepareStatement(checkTableSql)) {
|
|
564
|
581
|
pStmt.setString(1, table);
|
|
565
|
582
|
pStmt.setString(2, dbName);
|