jsql-neo 5.4.2 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +202 -202
  2. package/README.md +8097 -7971
  3. package/bin/jsql +320 -320
  4. package/bin/jsql-server +90 -90
  5. package/bin/jsql-server-control.js +177 -177
  6. package/index.d.ts +385 -357
  7. package/index.js +124 -118
  8. package/lib/ast.js +214 -0
  9. package/lib/btree.js +413 -413
  10. package/lib/cache.js +328 -328
  11. package/lib/client.js +125 -125
  12. package/lib/database.js +1744 -1744
  13. package/lib/date-types.js +182 -182
  14. package/lib/errors.js +89 -89
  15. package/lib/jsql_format.js +497 -497
  16. package/lib/migrate.js +254 -254
  17. package/lib/mod.js +292 -292
  18. package/lib/mongo_server.js +607 -607
  19. package/lib/multiserver.js +140 -140
  20. package/lib/mysql_compat.js +413 -413
  21. package/lib/mysql_server.js +1237 -1237
  22. package/lib/native_client.js +732 -732
  23. package/lib/nedb_compat.js +506 -506
  24. package/lib/pg_server.js +864 -864
  25. package/lib/plugin.js +34 -34
  26. package/lib/query.js +815 -815
  27. package/lib/redis_server.js +571 -571
  28. package/lib/sql.js +3460 -2925
  29. package/lib/sqlite_compat.js +323 -323
  30. package/lib/sqlite_worker.js +264 -264
  31. package/lib/table.js +1304 -1304
  32. package/lib/tui.js +502 -502
  33. package/lib/wasm.js +1 -1
  34. package/lib/wasm_client.js +718 -718
  35. package/lib/web_ui.js +257 -257
  36. package/native/jsql-neo-native.node +0 -0
  37. package/nativesrc/jsql-neo-core/Cargo.lock +455 -455
  38. package/nativesrc/jsql-neo-core/Cargo.toml +24 -24
  39. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +455 -455
  40. package/nativesrc/jsql-neo-core/src/engine/memory.rs +168 -168
  41. package/nativesrc/jsql-neo-core/src/engine/mod.rs +62 -62
  42. package/nativesrc/jsql-neo-core/src/engine/table.rs +700 -700
  43. package/nativesrc/jsql-neo-core/src/lib.rs +2 -2
  44. package/nativesrc/jsql-neo-core/src/storage/mod.rs +2 -2
  45. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +1 -1
  46. package/nativesrc/jsql-neo-core/src/storage/wal.rs +85 -85
  47. package/nativesrc/jsql-neo-core/src/types.rs +93 -93
  48. package/nativesrc/jsql-neo-native/Cargo.lock +606 -606
  49. package/nativesrc/jsql-neo-native/Cargo.toml +16 -16
  50. package/nativesrc/jsql-neo-native/build.rs +3 -3
  51. package/nativesrc/jsql-neo-native/package.json +7 -7
  52. package/nativesrc/jsql-neo-native/src/lib.rs +269 -269
  53. package/nativesrc/jsql-neo-wasm/Cargo.lock +465 -465
  54. package/nativesrc/jsql-neo-wasm/Cargo.toml +15 -15
  55. package/nativesrc/jsql-neo-wasm/src/lib.rs +447 -447
  56. package/package.json +87 -86
  57. package/postinstall.js +158 -158
  58. package/test/btree.test.js +128 -128
  59. package/test/coverage.js +187 -187
  60. package/test/join.test.js +110 -110
  61. package/test/native.test.js +158 -158
  62. package/test/regress-5.1.0.js +271 -271
  63. package/test/smoke.js +58 -58
  64. package/test/sql-parser.test.js +273 -0
  65. package/test/wasm.test.js +110 -110
  66. package/wasm/browser.d.ts +89 -89
  67. package/wasm/browser.mjs +569 -569
  68. package/wasm/browser_bg.mjs +597 -597
  69. package/wasm/jsql_neo_wasm.d.ts +38 -38
  70. package/wasm/jsql_neo_wasm.js +520 -520
  71. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +26 -26
  72. package/wasm/package.json +11 -11
package/lib/date-types.js CHANGED
@@ -1,183 +1,183 @@
1
- // © Vexify 2026 All Rights Reserved.
2
- /**
3
- * JSQL Date Types — DATE / DATETIME / TIMESTAMP 类型处理
4
- */
5
-
6
- const { createError } = require('./errors');
7
-
8
- /**
9
- * 验证并规范化日期类型值
10
- * @param {*} value - 输入值
11
- * @param {string} type - 'date' | 'datetime' | 'timestamp'
12
- * @param {string} fieldName - 字段名(用于错误提示)
13
- * @returns {*} 规范化后的值
14
- */
15
- function validateDateType(value, type, fieldName) {
16
- if (value === null || value === undefined) return value;
17
-
18
- switch (type) {
19
- case 'date':
20
- return validateDate(value, fieldName);
21
- case 'datetime':
22
- return validateDateTime(value, fieldName);
23
- case 'timestamp':
24
- return validateTimestamp(value, fieldName);
25
- case 'time':
26
- return validateTime(value, fieldName);
27
- default:
28
- return value;
29
- }
30
- }
31
-
32
- // ============================================================
33
- // DATE: YYYY-MM-DD
34
- // ============================================================
35
-
36
- function validateDate(value, fieldName) {
37
- if (value instanceof Date) {
38
- return value.toISOString().slice(0, 10);
39
- }
40
- if (typeof value === 'number') {
41
- // Unix timestamp → date
42
- return new Date(value * 1000).toISOString().slice(0, 10);
43
- }
44
- if (typeof value === 'string') {
45
- const d = parseDateString(value);
46
- if (d) return d.toISOString().slice(0, 10);
47
- }
48
- throw createError('ER_INVALID_DATE', fieldName, String(value));
49
- }
50
-
51
- function parseDateString(str) {
52
- // YYYY-MM-DD
53
- let m = str.match(/^(\d{4})-(\d{2})-(\d{2})$/);
54
- if (m) {
55
- const d = new Date(parseInt(m[1]), parseInt(m[2]) - 1, parseInt(m[3]));
56
- if (d.getFullYear() === parseInt(m[1])) return d;
57
- }
58
- // YYYY/MM/DD
59
- m = str.match(/^(\d{4})\/(\d{2})\/(\d{2})$/);
60
- if (m) {
61
- const d = new Date(parseInt(m[1]), parseInt(m[2]) - 1, parseInt(m[3]));
62
- if (d.getFullYear() === parseInt(m[1])) return d;
63
- }
64
- // MM/DD/YYYY
65
- m = str.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
66
- if (m) {
67
- const d = new Date(parseInt(m[3]), parseInt(m[1]) - 1, parseInt(m[2]));
68
- if (d.getFullYear() === parseInt(m[3])) return d;
69
- }
70
- // Try native parse
71
- const d = new Date(str);
72
- if (!isNaN(d.getTime())) return d;
73
- return null;
74
- }
75
-
76
- // ============================================================
77
- // DATETIME: YYYY-MM-DD HH:MM:SS
78
- // ============================================================
79
-
80
- function validateDateTime(value, fieldName) {
81
- if (value instanceof Date) {
82
- return value.toISOString().replace('T', ' ').slice(0, 19);
83
- }
84
- if (typeof value === 'number') {
85
- return new Date(value * 1000).toISOString().replace('T', ' ').slice(0, 19);
86
- }
87
- if (typeof value === 'string') {
88
- // ISO format: 2024-01-15T10:30:00.000Z
89
- const isoMatch = value.match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})/);
90
- if (isoMatch) {
91
- return isoMatch[1] + ' ' + isoMatch[2];
92
- }
93
- // MySQL format: YYYY-MM-DD HH:MM:SS
94
- const myMatch = value.match(/^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})$/);
95
- if (myMatch) {
96
- const d = new Date(value.replace(' ', 'T') + 'Z');
97
- if (!isNaN(d.getTime())) return value;
98
- }
99
- const d = new Date(value);
100
- if (!isNaN(d.getTime())) {
101
- return d.toISOString().replace('T', ' ').slice(0, 19);
102
- }
103
- }
104
- throw createError('ER_INVALID_DATE', fieldName, String(value));
105
- }
106
-
107
- // ============================================================
108
- // TIMESTAMP: Unix 时间戳(秒)
109
- // ============================================================
110
-
111
- function validateTimestamp(value, fieldName) {
112
- if (value instanceof Date) {
113
- return Math.floor(value.getTime() / 1000);
114
- }
115
- if (typeof value === 'number') {
116
- // 自动检测毫秒时间戳
117
- if (value > 1e12) return Math.floor(value / 1000);
118
- return value;
119
- }
120
- if (typeof value === 'string') {
121
- const d = new Date(value);
122
- if (!isNaN(d.getTime())) {
123
- return Math.floor(d.getTime() / 1000);
124
- }
125
- const n = parseInt(value, 10);
126
- if (!isNaN(n)) {
127
- if (n > 1e12) return Math.floor(n / 1000);
128
- return n;
129
- }
130
- }
131
- throw createError('ER_INVALID_DATE', fieldName, String(value));
132
- }
133
-
134
- // ============================================================
135
- // TIME: HH:MM:SS
136
- // ============================================================
137
-
138
- function validateTime(value, fieldName) {
139
- if (typeof value === 'string') {
140
- const m = value.match(/^(\d{2}):(\d{2}):(\d{2})$/);
141
- if (m) {
142
- const h = parseInt(m[1]), min = parseInt(m[2]), sec = parseInt(m[3]);
143
- if (h >= 0 && h < 24 && min >= 0 && min < 60 && sec >= 0 && sec < 60) {
144
- return value;
145
- }
146
- }
147
- // 负时间: -HH:MM:SS
148
- const neg = value.match(/^-(\d{2}):(\d{2}):(\d{2})$/);
149
- if (neg) return value;
150
- }
151
- throw createError('ER_INVALID_DATE', fieldName, String(value));
152
- }
153
-
154
- // ============================================================
155
- // 类型转换辅助
156
- // ============================================================
157
-
158
- function now(type = 'datetime') {
159
- const d = new Date();
160
- switch (type) {
161
- case 'date': return d.toISOString().slice(0, 10);
162
- case 'timestamp': return Math.floor(d.getTime() / 1000);
163
- case 'time': return d.toISOString().slice(11, 19);
164
- default: return d.toISOString().replace('T', ' ').slice(0, 19);
165
- }
166
- }
167
-
168
- function formatDate(value, format = 'YYYY-MM-DD') {
169
- if (!value) return value;
170
- const d = value instanceof Date ? value : new Date(value);
171
- if (isNaN(d.getTime())) return String(value);
172
-
173
- const pad = n => String(n).padStart(2, '0');
174
- return format
175
- .replace('YYYY', d.getFullYear())
176
- .replace('MM', pad(d.getMonth() + 1))
177
- .replace('DD', pad(d.getDate()))
178
- .replace('HH', pad(d.getHours()))
179
- .replace('mm', pad(d.getMinutes()))
180
- .replace('ss', pad(d.getSeconds()));
181
- }
182
-
1
+ // © Vexify 2026 All Rights Reserved.
2
+ /**
3
+ * JSQL Date Types — DATE / DATETIME / TIMESTAMP 类型处理
4
+ */
5
+
6
+ const { createError } = require('./errors');
7
+
8
+ /**
9
+ * 验证并规范化日期类型值
10
+ * @param {*} value - 输入值
11
+ * @param {string} type - 'date' | 'datetime' | 'timestamp'
12
+ * @param {string} fieldName - 字段名(用于错误提示)
13
+ * @returns {*} 规范化后的值
14
+ */
15
+ function validateDateType(value, type, fieldName) {
16
+ if (value === null || value === undefined) return value;
17
+
18
+ switch (type) {
19
+ case 'date':
20
+ return validateDate(value, fieldName);
21
+ case 'datetime':
22
+ return validateDateTime(value, fieldName);
23
+ case 'timestamp':
24
+ return validateTimestamp(value, fieldName);
25
+ case 'time':
26
+ return validateTime(value, fieldName);
27
+ default:
28
+ return value;
29
+ }
30
+ }
31
+
32
+ // ============================================================
33
+ // DATE: YYYY-MM-DD
34
+ // ============================================================
35
+
36
+ function validateDate(value, fieldName) {
37
+ if (value instanceof Date) {
38
+ return value.toISOString().slice(0, 10);
39
+ }
40
+ if (typeof value === 'number') {
41
+ // Unix timestamp → date
42
+ return new Date(value * 1000).toISOString().slice(0, 10);
43
+ }
44
+ if (typeof value === 'string') {
45
+ const d = parseDateString(value);
46
+ if (d) return d.toISOString().slice(0, 10);
47
+ }
48
+ throw createError('ER_INVALID_DATE', fieldName, String(value));
49
+ }
50
+
51
+ function parseDateString(str) {
52
+ // YYYY-MM-DD
53
+ let m = str.match(/^(\d{4})-(\d{2})-(\d{2})$/);
54
+ if (m) {
55
+ const d = new Date(parseInt(m[1]), parseInt(m[2]) - 1, parseInt(m[3]));
56
+ if (d.getFullYear() === parseInt(m[1])) return d;
57
+ }
58
+ // YYYY/MM/DD
59
+ m = str.match(/^(\d{4})\/(\d{2})\/(\d{2})$/);
60
+ if (m) {
61
+ const d = new Date(parseInt(m[1]), parseInt(m[2]) - 1, parseInt(m[3]));
62
+ if (d.getFullYear() === parseInt(m[1])) return d;
63
+ }
64
+ // MM/DD/YYYY
65
+ m = str.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
66
+ if (m) {
67
+ const d = new Date(parseInt(m[3]), parseInt(m[1]) - 1, parseInt(m[2]));
68
+ if (d.getFullYear() === parseInt(m[3])) return d;
69
+ }
70
+ // Try native parse
71
+ const d = new Date(str);
72
+ if (!isNaN(d.getTime())) return d;
73
+ return null;
74
+ }
75
+
76
+ // ============================================================
77
+ // DATETIME: YYYY-MM-DD HH:MM:SS
78
+ // ============================================================
79
+
80
+ function validateDateTime(value, fieldName) {
81
+ if (value instanceof Date) {
82
+ return value.toISOString().replace('T', ' ').slice(0, 19);
83
+ }
84
+ if (typeof value === 'number') {
85
+ return new Date(value * 1000).toISOString().replace('T', ' ').slice(0, 19);
86
+ }
87
+ if (typeof value === 'string') {
88
+ // ISO format: 2024-01-15T10:30:00.000Z
89
+ const isoMatch = value.match(/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})/);
90
+ if (isoMatch) {
91
+ return isoMatch[1] + ' ' + isoMatch[2];
92
+ }
93
+ // MySQL format: YYYY-MM-DD HH:MM:SS
94
+ const myMatch = value.match(/^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})$/);
95
+ if (myMatch) {
96
+ const d = new Date(value.replace(' ', 'T') + 'Z');
97
+ if (!isNaN(d.getTime())) return value;
98
+ }
99
+ const d = new Date(value);
100
+ if (!isNaN(d.getTime())) {
101
+ return d.toISOString().replace('T', ' ').slice(0, 19);
102
+ }
103
+ }
104
+ throw createError('ER_INVALID_DATE', fieldName, String(value));
105
+ }
106
+
107
+ // ============================================================
108
+ // TIMESTAMP: Unix 时间戳(秒)
109
+ // ============================================================
110
+
111
+ function validateTimestamp(value, fieldName) {
112
+ if (value instanceof Date) {
113
+ return Math.floor(value.getTime() / 1000);
114
+ }
115
+ if (typeof value === 'number') {
116
+ // 自动检测毫秒时间戳
117
+ if (value > 1e12) return Math.floor(value / 1000);
118
+ return value;
119
+ }
120
+ if (typeof value === 'string') {
121
+ const d = new Date(value);
122
+ if (!isNaN(d.getTime())) {
123
+ return Math.floor(d.getTime() / 1000);
124
+ }
125
+ const n = parseInt(value, 10);
126
+ if (!isNaN(n)) {
127
+ if (n > 1e12) return Math.floor(n / 1000);
128
+ return n;
129
+ }
130
+ }
131
+ throw createError('ER_INVALID_DATE', fieldName, String(value));
132
+ }
133
+
134
+ // ============================================================
135
+ // TIME: HH:MM:SS
136
+ // ============================================================
137
+
138
+ function validateTime(value, fieldName) {
139
+ if (typeof value === 'string') {
140
+ const m = value.match(/^(\d{2}):(\d{2}):(\d{2})$/);
141
+ if (m) {
142
+ const h = parseInt(m[1]), min = parseInt(m[2]), sec = parseInt(m[3]);
143
+ if (h >= 0 && h < 24 && min >= 0 && min < 60 && sec >= 0 && sec < 60) {
144
+ return value;
145
+ }
146
+ }
147
+ // 负时间: -HH:MM:SS
148
+ const neg = value.match(/^-(\d{2}):(\d{2}):(\d{2})$/);
149
+ if (neg) return value;
150
+ }
151
+ throw createError('ER_INVALID_DATE', fieldName, String(value));
152
+ }
153
+
154
+ // ============================================================
155
+ // 类型转换辅助
156
+ // ============================================================
157
+
158
+ function now(type = 'datetime') {
159
+ const d = new Date();
160
+ switch (type) {
161
+ case 'date': return d.toISOString().slice(0, 10);
162
+ case 'timestamp': return Math.floor(d.getTime() / 1000);
163
+ case 'time': return d.toISOString().slice(11, 19);
164
+ default: return d.toISOString().replace('T', ' ').slice(0, 19);
165
+ }
166
+ }
167
+
168
+ function formatDate(value, format = 'YYYY-MM-DD') {
169
+ if (!value) return value;
170
+ const d = value instanceof Date ? value : new Date(value);
171
+ if (isNaN(d.getTime())) return String(value);
172
+
173
+ const pad = n => String(n).padStart(2, '0');
174
+ return format
175
+ .replace('YYYY', d.getFullYear())
176
+ .replace('MM', pad(d.getMonth() + 1))
177
+ .replace('DD', pad(d.getDate()))
178
+ .replace('HH', pad(d.getHours()))
179
+ .replace('mm', pad(d.getMinutes()))
180
+ .replace('ss', pad(d.getSeconds()));
181
+ }
182
+
183
183
  module.exports = { validateDateType, now, formatDate };
package/lib/errors.js CHANGED
@@ -1,90 +1,90 @@
1
- // © Vexify 2026 All Rights Reserved.
2
- /**
3
- * JSQL Error Codes — MySQL 风格错误码体系
4
- */
5
-
6
- class JSQL_Error extends Error {
7
- constructor(code, message, details = {}) {
8
- super(message);
9
- this.name = 'JSQL_Error';
10
- this.code = code;
11
- this.details = details;
12
- this.timestamp = new Date().toISOString();
13
- }
14
-
15
- toJSON() {
16
- return {
17
- error: true,
18
- code: this.code,
19
- message: this.message,
20
- details: this.details,
21
- timestamp: this.timestamp
22
- };
23
- }
24
- }
25
-
26
- // MySQL 兼容错误码
27
- const ErrorCodes = {
28
- // 表/库操作
29
- ER_TABLE_EXISTS: { code: 1050, msg: "Table '%s' already exists" },
30
- ER_NO_SUCH_TABLE: { code: 1146, msg: "Table '%s' doesn't exist" },
31
- ER_DB_CREATE_EXISTS: { code: 1007, msg: "Can't create database '%s'; database exists" },
32
-
33
- // 列操作
34
- ER_DUP_FIELDNAME: { code: 1060, msg: "Duplicate column name '%s'" },
35
- ER_BAD_FIELD_ERROR: { code: 1054, msg: "Unknown column '%s' in '%s'" },
36
- ER_CANT_DROP_FIELD: { code: 1091, msg: "Can't DROP '%s'; check that column exists" },
37
-
38
- // 约束
39
- ER_DUP_ENTRY: { code: 1062, msg: "Duplicate entry '%s' for key '%s'" },
40
- ER_NO_DEFAULT_FOR_FIELD: { code: 1364, msg: "Field '%s' doesn't have a default value" },
41
- ER_BAD_NULL_ERROR: { code: 1048, msg: "Column '%s' cannot be null" },
42
- ER_CHECK_CONSTRAINT: { code: 3819, msg: "Check constraint '%s' is violated" },
43
- ER_NO_REFERENCED_ROW: { code: 1216, msg: "Cannot add or update: foreign key constraint fails" },
44
- ER_ROW_IS_REFERENCED: { code: 1217, msg: "Cannot delete or update: a foreign key constraint fails" },
45
-
46
- // 数据类型
47
- ER_TRUNCATED_WRONG_VALUE: { code: 1292, msg: "Truncated incorrect %s value: '%s'" },
48
- ER_INVALID_DATE: { code: 1292, msg: "Incorrect date value: '%s'" },
49
- ER_DATA_TOO_LONG: { code: 1406, msg: "Data too long for column '%s'" },
50
- ER_OUT_OF_RANGE: { code: 1264, msg: "Out of range value for column '%s'" },
51
-
52
- // 事务
53
- ER_TRANSACTION_ACTIVE: { code: 1568, msg: "Transaction already in progress" },
54
- ER_NO_TRANSACTION: { code: 1569, msg: "No transaction in progress" },
55
-
56
- // 其他
57
- ER_NOT_SUPPORTED: { code: 1235, msg: "This version doesn't yet support '%s'" },
58
- ER_PARSE_ERROR: { code: 1064, msg: "You have an error in your syntax: %s" },
59
- ER_LOCK_WAIT_TIMEOUT: { code: 1205, msg: "Lock wait timeout exceeded; try restarting transaction" },
60
- ER_FILE_NOT_FOUND: { code: 1017, msg: "Can't find file: '%s'" },
61
- ER_VIEW_EXISTS: { code: 1050, msg: "View '%s' already exists" },
62
- ER_TRIGGER_EXISTS: { code: 1359, msg: "Trigger '%s' already exists" },
63
- ER_TRIGGER_NOT_FOUND: { code: 1360, msg: "Trigger '%s' does not exist" },
64
- ER_PLUGIN_ERR: { code: 1125, msg: "Plugin error: %s" },
65
- ER_PLUGIN_ABORT: { code: 1125, msg: "Operation aborted by plugin: %s" },
66
- ER_TABLE_EXISTS_ERROR: { code: 1050, msg: "Table '%s' already exists" },
67
- ER_BAD_TABLE_NAME: { code: 1058, msg: "Invalid table name '%s'" },
68
- ER_BAD_REGEX: { code: 1258, msg: "Unsafe regular expression rejected: %s" },
69
- ER_DBATTACH_EXISTS: { code: 1007, msg: "Database '%s' already attached" },
70
- ER_DBATTACH_NOT_FOUND: { code: 1049, msg: "Attached database '%s' not found" },
71
- };
72
-
73
- /**
74
- * 创建错误
75
- */
76
- function createError(codeKey, ...args) {
77
- const template = ErrorCodes[codeKey];
78
- if (!template) {
79
- return new JSQL_Error('UNKNOWN', `Unknown error: ${codeKey}`, { codeKey, args });
80
- }
81
-
82
- let msg = template.msg;
83
- for (let i = 0; i < args.length; i++) {
84
- msg = msg.replace('%s', String(args[i]));
85
- }
86
-
87
- return new JSQL_Error(template.code, msg, { args });
88
- }
89
-
1
+ // © Vexify 2026 All Rights Reserved.
2
+ /**
3
+ * JSQL Error Codes — MySQL 风格错误码体系
4
+ */
5
+
6
+ class JSQL_Error extends Error {
7
+ constructor(code, message, details = {}) {
8
+ super(message);
9
+ this.name = 'JSQL_Error';
10
+ this.code = code;
11
+ this.details = details;
12
+ this.timestamp = new Date().toISOString();
13
+ }
14
+
15
+ toJSON() {
16
+ return {
17
+ error: true,
18
+ code: this.code,
19
+ message: this.message,
20
+ details: this.details,
21
+ timestamp: this.timestamp
22
+ };
23
+ }
24
+ }
25
+
26
+ // MySQL 兼容错误码
27
+ const ErrorCodes = {
28
+ // 表/库操作
29
+ ER_TABLE_EXISTS: { code: 1050, msg: "Table '%s' already exists" },
30
+ ER_NO_SUCH_TABLE: { code: 1146, msg: "Table '%s' doesn't exist" },
31
+ ER_DB_CREATE_EXISTS: { code: 1007, msg: "Can't create database '%s'; database exists" },
32
+
33
+ // 列操作
34
+ ER_DUP_FIELDNAME: { code: 1060, msg: "Duplicate column name '%s'" },
35
+ ER_BAD_FIELD_ERROR: { code: 1054, msg: "Unknown column '%s' in '%s'" },
36
+ ER_CANT_DROP_FIELD: { code: 1091, msg: "Can't DROP '%s'; check that column exists" },
37
+
38
+ // 约束
39
+ ER_DUP_ENTRY: { code: 1062, msg: "Duplicate entry '%s' for key '%s'" },
40
+ ER_NO_DEFAULT_FOR_FIELD: { code: 1364, msg: "Field '%s' doesn't have a default value" },
41
+ ER_BAD_NULL_ERROR: { code: 1048, msg: "Column '%s' cannot be null" },
42
+ ER_CHECK_CONSTRAINT: { code: 3819, msg: "Check constraint '%s' is violated" },
43
+ ER_NO_REFERENCED_ROW: { code: 1216, msg: "Cannot add or update: foreign key constraint fails" },
44
+ ER_ROW_IS_REFERENCED: { code: 1217, msg: "Cannot delete or update: a foreign key constraint fails" },
45
+
46
+ // 数据类型
47
+ ER_TRUNCATED_WRONG_VALUE: { code: 1292, msg: "Truncated incorrect %s value: '%s'" },
48
+ ER_INVALID_DATE: { code: 1292, msg: "Incorrect date value: '%s'" },
49
+ ER_DATA_TOO_LONG: { code: 1406, msg: "Data too long for column '%s'" },
50
+ ER_OUT_OF_RANGE: { code: 1264, msg: "Out of range value for column '%s'" },
51
+
52
+ // 事务
53
+ ER_TRANSACTION_ACTIVE: { code: 1568, msg: "Transaction already in progress" },
54
+ ER_NO_TRANSACTION: { code: 1569, msg: "No transaction in progress" },
55
+
56
+ // 其他
57
+ ER_NOT_SUPPORTED: { code: 1235, msg: "This version doesn't yet support '%s'" },
58
+ ER_PARSE_ERROR: { code: 1064, msg: "You have an error in your syntax: %s" },
59
+ ER_LOCK_WAIT_TIMEOUT: { code: 1205, msg: "Lock wait timeout exceeded; try restarting transaction" },
60
+ ER_FILE_NOT_FOUND: { code: 1017, msg: "Can't find file: '%s'" },
61
+ ER_VIEW_EXISTS: { code: 1050, msg: "View '%s' already exists" },
62
+ ER_TRIGGER_EXISTS: { code: 1359, msg: "Trigger '%s' already exists" },
63
+ ER_TRIGGER_NOT_FOUND: { code: 1360, msg: "Trigger '%s' does not exist" },
64
+ ER_PLUGIN_ERR: { code: 1125, msg: "Plugin error: %s" },
65
+ ER_PLUGIN_ABORT: { code: 1125, msg: "Operation aborted by plugin: %s" },
66
+ ER_TABLE_EXISTS_ERROR: { code: 1050, msg: "Table '%s' already exists" },
67
+ ER_BAD_TABLE_NAME: { code: 1058, msg: "Invalid table name '%s'" },
68
+ ER_BAD_REGEX: { code: 1258, msg: "Unsafe regular expression rejected: %s" },
69
+ ER_DBATTACH_EXISTS: { code: 1007, msg: "Database '%s' already attached" },
70
+ ER_DBATTACH_NOT_FOUND: { code: 1049, msg: "Attached database '%s' not found" },
71
+ };
72
+
73
+ /**
74
+ * 创建错误
75
+ */
76
+ function createError(codeKey, ...args) {
77
+ const template = ErrorCodes[codeKey];
78
+ if (!template) {
79
+ return new JSQL_Error('UNKNOWN', `Unknown error: ${codeKey}`, { codeKey, args });
80
+ }
81
+
82
+ let msg = template.msg;
83
+ for (let i = 0; i < args.length; i++) {
84
+ msg = msg.replace('%s', String(args[i]));
85
+ }
86
+
87
+ return new JSQL_Error(template.code, msg, { args });
88
+ }
89
+
90
90
  module.exports = { JSQL_Error, ErrorCodes, createError };