@zhin.js/database 1.0.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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/lib/base/database.d.ts +71 -0
  4. package/lib/base/database.d.ts.map +1 -0
  5. package/lib/base/database.js +101 -0
  6. package/lib/base/database.js.map +1 -0
  7. package/lib/base/dialect.d.ts +34 -0
  8. package/lib/base/dialect.d.ts.map +1 -0
  9. package/lib/base/dialect.js +21 -0
  10. package/lib/base/dialect.js.map +1 -0
  11. package/lib/base/index.d.ts +6 -0
  12. package/lib/base/index.d.ts.map +1 -0
  13. package/lib/base/index.js +6 -0
  14. package/lib/base/index.js.map +1 -0
  15. package/lib/base/model.d.ts +53 -0
  16. package/lib/base/model.d.ts.map +1 -0
  17. package/lib/base/model.js +65 -0
  18. package/lib/base/model.js.map +1 -0
  19. package/lib/base/query-classes.d.ts +68 -0
  20. package/lib/base/query-classes.d.ts.map +1 -0
  21. package/lib/base/query-classes.js +178 -0
  22. package/lib/base/query-classes.js.map +1 -0
  23. package/lib/base/thenable.d.ts +15 -0
  24. package/lib/base/thenable.d.ts.map +1 -0
  25. package/lib/base/thenable.js +33 -0
  26. package/lib/base/thenable.js.map +1 -0
  27. package/lib/dialects/memory.d.ts +52 -0
  28. package/lib/dialects/memory.d.ts.map +1 -0
  29. package/lib/dialects/memory.js +772 -0
  30. package/lib/dialects/memory.js.map +1 -0
  31. package/lib/dialects/mongodb.d.ts +85 -0
  32. package/lib/dialects/mongodb.d.ts.map +1 -0
  33. package/lib/dialects/mongodb.js +461 -0
  34. package/lib/dialects/mongodb.js.map +1 -0
  35. package/lib/dialects/mysql.d.ts +33 -0
  36. package/lib/dialects/mysql.d.ts.map +1 -0
  37. package/lib/dialects/mysql.js +132 -0
  38. package/lib/dialects/mysql.js.map +1 -0
  39. package/lib/dialects/pg.d.ts +33 -0
  40. package/lib/dialects/pg.d.ts.map +1 -0
  41. package/lib/dialects/pg.js +132 -0
  42. package/lib/dialects/pg.js.map +1 -0
  43. package/lib/dialects/redis.d.ts +87 -0
  44. package/lib/dialects/redis.d.ts.map +1 -0
  45. package/lib/dialects/redis.js +500 -0
  46. package/lib/dialects/redis.js.map +1 -0
  47. package/lib/dialects/sqlite.d.ts +46 -0
  48. package/lib/dialects/sqlite.d.ts.map +1 -0
  49. package/lib/dialects/sqlite.js +201 -0
  50. package/lib/dialects/sqlite.js.map +1 -0
  51. package/lib/index.d.ts +18 -0
  52. package/lib/index.d.ts.map +1 -0
  53. package/lib/index.js +18 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/registry.d.ts +37 -0
  56. package/lib/registry.d.ts.map +1 -0
  57. package/lib/registry.js +21 -0
  58. package/lib/registry.js.map +1 -0
  59. package/lib/type/document/database.d.ts +60 -0
  60. package/lib/type/document/database.d.ts.map +1 -0
  61. package/lib/type/document/database.js +242 -0
  62. package/lib/type/document/database.js.map +1 -0
  63. package/lib/type/document/model.d.ts +42 -0
  64. package/lib/type/document/model.d.ts.map +1 -0
  65. package/lib/type/document/model.js +65 -0
  66. package/lib/type/document/model.js.map +1 -0
  67. package/lib/type/keyvalue/database.d.ts +64 -0
  68. package/lib/type/keyvalue/database.d.ts.map +1 -0
  69. package/lib/type/keyvalue/database.js +214 -0
  70. package/lib/type/keyvalue/database.js.map +1 -0
  71. package/lib/type/keyvalue/model.d.ts +107 -0
  72. package/lib/type/keyvalue/model.d.ts.map +1 -0
  73. package/lib/type/keyvalue/model.js +261 -0
  74. package/lib/type/keyvalue/model.js.map +1 -0
  75. package/lib/type/related/database.d.ts +32 -0
  76. package/lib/type/related/database.d.ts.map +1 -0
  77. package/lib/type/related/database.js +334 -0
  78. package/lib/type/related/database.js.map +1 -0
  79. package/lib/type/related/model.d.ts +43 -0
  80. package/lib/type/related/model.d.ts.map +1 -0
  81. package/lib/type/related/model.js +108 -0
  82. package/lib/type/related/model.js.map +1 -0
  83. package/lib/types.d.ts +251 -0
  84. package/lib/types.d.ts.map +1 -0
  85. package/lib/types.js +28 -0
  86. package/lib/types.js.map +1 -0
  87. package/package.json +54 -0
  88. package/src/base/database.ts +128 -0
  89. package/src/base/dialect.ts +76 -0
  90. package/src/base/index.ts +5 -0
  91. package/src/base/model.ts +89 -0
  92. package/src/base/query-classes.ts +217 -0
  93. package/src/base/thenable.ts +54 -0
  94. package/src/dialects/memory.ts +880 -0
  95. package/src/dialects/mongodb.ts +533 -0
  96. package/src/dialects/mysql.ts +157 -0
  97. package/src/dialects/pg.ts +157 -0
  98. package/src/dialects/redis.ts +598 -0
  99. package/src/dialects/sqlite.ts +233 -0
  100. package/src/index.ts +20 -0
  101. package/src/registry.ts +59 -0
  102. package/src/type/document/database.ts +283 -0
  103. package/src/type/document/model.ts +86 -0
  104. package/src/type/keyvalue/database.ts +261 -0
  105. package/src/type/keyvalue/model.ts +339 -0
  106. package/src/type/related/database.ts +392 -0
  107. package/src/type/related/model.ts +117 -0
  108. package/src/types.ts +403 -0
  109. package/tsconfig.json +24 -0
@@ -0,0 +1,880 @@
1
+ import {Dialect} from '../base';
2
+ import {MemoryConfig} from "../types";
3
+ import {RelatedDatabase} from "../type/related/database";
4
+ import {Registry} from "../registry";
5
+
6
+ interface MemoryTable {
7
+ name: string;
8
+ schema: Record<string, any>;
9
+ data: Record<string, any>[];
10
+ indexes: Map<string, MemoryIndex>;
11
+ }
12
+
13
+ interface MemoryIndex {
14
+ index: Map<any, number[]>;
15
+ columns: string[];
16
+ unique: boolean;
17
+ }
18
+
19
+ export class MemoryDialect extends Dialect<MemoryConfig,string> {
20
+ private connected = false;
21
+ private tables: Map<string, MemoryTable> = new Map();
22
+ private autoIncrementCounters: Map<string, Map<string, number>> = new Map();
23
+
24
+ constructor(config: MemoryConfig = {}) {
25
+ super('memory', config);
26
+ }
27
+
28
+ // Connection management
29
+ isConnected(): boolean {
30
+ return this.connected;
31
+ }
32
+
33
+ async connect(): Promise<void> {
34
+ this.connected = true;
35
+ this.tables.clear();
36
+ this.autoIncrementCounters.clear();
37
+ }
38
+
39
+ async disconnect(): Promise<void> {
40
+ this.connected = false;
41
+ this.tables.clear();
42
+ this.autoIncrementCounters.clear();
43
+ }
44
+
45
+ async healthCheck(): Promise<boolean> {
46
+ return this.isConnected();
47
+ }
48
+
49
+ async query<U = any>(sql: string, params?: any[]): Promise<U> {
50
+ if (!this.connected) {
51
+ throw new Error('Database not connected');
52
+ }
53
+
54
+ // 简单的 SQL 解析和执行
55
+ const trimmedSql = sql.trim().toLowerCase();
56
+
57
+ if (trimmedSql.startsWith('create table')) {
58
+ return this.executeCreateTable(sql, params) as U;
59
+ } else if (trimmedSql.startsWith('insert into')) {
60
+ return this.executeInsert(sql, params) as U;
61
+ } else if (trimmedSql.startsWith('select')) {
62
+ return this.executeSelect(sql, params) as U;
63
+ } else if (trimmedSql.startsWith('update')) {
64
+ return this.executeUpdate(sql, params) as U;
65
+ } else if (trimmedSql.startsWith('delete')) {
66
+ return this.executeDelete(sql, params) as U;
67
+ } else if (trimmedSql.startsWith('alter table')) {
68
+ return this.executeAlterTable(sql, params) as U;
69
+ } else if (trimmedSql.startsWith('drop table')) {
70
+ return this.executeDropTable(sql, params) as U;
71
+ } else if (trimmedSql.startsWith('drop index')) {
72
+ return this.executeDropIndex(sql, params) as U;
73
+ } else if (trimmedSql.startsWith('create index')) {
74
+ return this.executeCreateIndex(sql, params) as U;
75
+ }
76
+
77
+ throw new Error(`Unsupported SQL: ${sql}`);
78
+ }
79
+
80
+ async dispose(): Promise<void> {
81
+ await this.disconnect();
82
+ }
83
+
84
+ // SQL generation methods
85
+ mapColumnType(type: string): string {
86
+ // 内存数据库不需要严格的类型映射,但保持一致性
87
+ const typeMap: Record<string, string> = {
88
+ 'text': 'TEXT',
89
+ 'integer': 'INTEGER',
90
+ 'float': 'REAL',
91
+ 'boolean': 'BOOLEAN',
92
+ 'date': 'DATE',
93
+ 'json': 'JSON'
94
+ };
95
+ return typeMap[type.toLowerCase()] || 'TEXT';
96
+ }
97
+
98
+ quoteIdentifier(identifier: string): string {
99
+ return `"${identifier}"`;
100
+ }
101
+
102
+ getParameterPlaceholder(index: number): string {
103
+ return '?';
104
+ }
105
+
106
+ getStatementTerminator(): string {
107
+ return ';';
108
+ }
109
+
110
+ formatBoolean(value: boolean): string {
111
+ return value ? 'true' : 'false';
112
+ }
113
+
114
+ formatDate(value: Date): string {
115
+ return `'${value.toISOString()}'`;
116
+ }
117
+
118
+ formatJson(value: any): string {
119
+ return `'${JSON.stringify(value)}'`;
120
+ }
121
+
122
+ escapeString(value: string): string {
123
+ return value.replace(/'/g, "''");
124
+ }
125
+
126
+ formatDefaultValue(value: any): string {
127
+ if (typeof value === 'string') {
128
+ return `'${this.escapeString(value)}'`;
129
+ } else if (typeof value === 'number' || typeof value === 'boolean') {
130
+ return value.toString();
131
+ } else if (value instanceof Date) {
132
+ return this.formatDate(value);
133
+ } else if (value === null) {
134
+ return 'NULL';
135
+ } else if (typeof value === 'object') {
136
+ return this.formatJson(value);
137
+ } else {
138
+ throw new Error(`Unsupported default value type: ${typeof value}`);
139
+ }
140
+ }
141
+
142
+ formatLimit(limit: number): string {
143
+ return `LIMIT ${limit}`;
144
+ }
145
+
146
+ formatOffset(offset: number): string {
147
+ return `OFFSET ${offset}`;
148
+ }
149
+
150
+ formatLimitOffset(limit: number, offset: number): string {
151
+ return `LIMIT ${limit} OFFSET ${offset}`;
152
+ }
153
+
154
+ formatCreateTable(tableName: string, columns: string[]): string {
155
+ return `CREATE TABLE ${this.quoteIdentifier(tableName)} (${columns.join(', ')})`;
156
+ }
157
+
158
+ formatAlterTable(tableName: string, alterations: string[]): string {
159
+ return `ALTER TABLE ${this.quoteIdentifier(tableName)} ${alterations.join(', ')}`;
160
+ }
161
+
162
+ formatDropTable(tableName: string, ifExists?: boolean): string {
163
+ const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
164
+ return `DROP TABLE ${ifExistsClause}${this.quoteIdentifier(tableName)}`;
165
+ }
166
+
167
+ formatDropIndex(indexName: string, tableName: string, ifExists?: boolean): string {
168
+ const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
169
+ return `DROP INDEX ${ifExistsClause}${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(tableName)}`;
170
+ }
171
+
172
+ // 私有方法:SQL 执行实现
173
+ private executeCreateTable(sql: string, params?: any[]): any {
174
+ // 解析 CREATE TABLE 语句
175
+ const match = sql.match(/CREATE TABLE (?:IF NOT EXISTS )?"?(\w+)"?\s*\((.*)\)/i);
176
+ if (!match) {
177
+ throw new Error(`Invalid CREATE TABLE syntax: ${sql}`);
178
+ }
179
+
180
+ const tableName = match[1];
181
+ const columnsStr = match[2];
182
+
183
+ // 解析列定义
184
+ const schema: Record<string, any> = {};
185
+ const columnDefs = columnsStr.split(',').map(col => col.trim());
186
+
187
+ for (const colDef of columnDefs) {
188
+ const parts = colDef.split(/\s+/);
189
+ const columnName = parts[0].replace(/"/g, '');
190
+ const columnType = parts[1];
191
+
192
+ schema[columnName] = {
193
+ type: columnType,
194
+ primary: colDef.toLowerCase().includes('primary key'),
195
+ nullable: !colDef.toLowerCase().includes('not null'),
196
+ unique: colDef.toLowerCase().includes('unique'),
197
+ autoIncrement: colDef.toLowerCase().includes('auto_increment') || colDef.toLowerCase().includes('autoincrement')
198
+ };
199
+ }
200
+
201
+ // 创建表
202
+ const table: MemoryTable = {
203
+ name: tableName,
204
+ schema,
205
+ data: [],
206
+ indexes: new Map()
207
+ };
208
+
209
+ this.tables.set(tableName, table);
210
+ this.autoIncrementCounters.set(tableName, new Map());
211
+
212
+ return { affectedRows: 0 };
213
+ }
214
+
215
+ private executeInsert(sql: string, params: any[] = []): any {
216
+ // 解析 INSERT 语句
217
+ const match = sql.match(/INSERT INTO "?(\w+)"?\s*\((.*?)\)\s*VALUES\s*\((.*?)\)/i);
218
+ if (!match) {
219
+ throw new Error(`Invalid INSERT syntax: ${sql}`);
220
+ }
221
+
222
+ const tableName = match[1];
223
+ const columnsStr = match[2];
224
+ const valuesStr = match[3];
225
+
226
+ const table = this.tables.get(tableName);
227
+ if (!table) {
228
+ throw new Error(`Table ${tableName} does not exist`);
229
+ }
230
+
231
+ const columns = columnsStr.split(',').map(col => col.trim().replace(/"/g, ''));
232
+ const values = params || this.parseValues(valuesStr);
233
+
234
+ // 创建新记录
235
+ const record: Record<string, any> = {};
236
+
237
+ // 处理自增字段
238
+ for (const [columnName, columnDef] of Object.entries(table.schema)) {
239
+ if (columnDef.autoIncrement && columnDef.primary) {
240
+ const counter = this.autoIncrementCounters.get(tableName)!;
241
+ const currentId = counter.get(columnName) || 0;
242
+ const nextId = currentId + 1;
243
+ counter.set(columnName, nextId);
244
+ record[columnName] = nextId;
245
+ }
246
+ }
247
+
248
+ // 设置提供的值
249
+ columns.forEach((col, index) => {
250
+ if (index < values.length) {
251
+ record[col] = values[index];
252
+ }
253
+ });
254
+
255
+ // 设置默认值
256
+ for (const [columnName, columnDef] of Object.entries(table.schema)) {
257
+ if (!(columnName in record) && columnDef.default !== undefined) {
258
+ record[columnName] = columnDef.default;
259
+ }
260
+ }
261
+
262
+ table.data.push(record);
263
+
264
+ return record;
265
+ }
266
+
267
+ private executeSelect(sql: string, params: any[] = []): any {
268
+ // 简单的 SELECT 解析
269
+ const selectMatch = sql.match(/SELECT\s+(.*?)\s+FROM\s+"?(\w+)"?/i);
270
+ if (!selectMatch) {
271
+ throw new Error(`Invalid SELECT syntax: ${sql}`);
272
+ }
273
+
274
+ const fieldsStr = selectMatch[1].trim();
275
+ const tableName = selectMatch[2];
276
+
277
+ const table = this.tables.get(tableName);
278
+ if (!table) {
279
+ throw new Error(`Table ${tableName} does not exist`);
280
+ }
281
+
282
+ let results = [...table.data];
283
+
284
+ // 处理 WHERE 条件
285
+ const whereMatch = sql.match(/WHERE\s+(.*?)(?:\s+ORDER\s+BY|\s+LIMIT|\s+OFFSET|$)/i);
286
+ if (whereMatch) {
287
+ const whereClause = whereMatch[1].trim();
288
+ results = this.applyWhereCondition(results, whereClause, params);
289
+ }
290
+
291
+ // 处理 ORDER BY
292
+ const orderMatch = sql.match(/ORDER\s+BY\s+(.*?)(?:\s+LIMIT|\s+OFFSET|$)/i);
293
+ if (orderMatch) {
294
+ const orderClause = orderMatch[1].trim();
295
+ results = this.applyOrderBy(results, orderClause);
296
+ }
297
+
298
+ // 处理 LIMIT
299
+ const limitMatch = sql.match(/LIMIT\s+(\d+)(?:\s+OFFSET\s+(\d+))?/i);
300
+ if (limitMatch) {
301
+ const limit = parseInt(limitMatch[1]);
302
+ const offset = limitMatch[2] ? parseInt(limitMatch[2]) : 0;
303
+ results = results.slice(offset, offset + limit);
304
+ }
305
+
306
+ // 处理字段选择
307
+ if (fieldsStr !== '*') {
308
+ const fields = fieldsStr.split(',').map(f => f.trim().replace(/"/g, ''));
309
+ results = results.map(row => {
310
+ const newRow: Record<string, any> = {};
311
+ fields.forEach(field => {
312
+ if (field in row) {
313
+ newRow[field] = row[field];
314
+ }
315
+ });
316
+ return newRow;
317
+ });
318
+ }
319
+
320
+ return results;
321
+ }
322
+
323
+ private executeUpdate(sql: string, params: any[] = []): any {
324
+ // 解析 UPDATE 语句
325
+ const match = sql.match(/UPDATE\s+"?(\w+)"?\s+SET\s+(.*?)(?:\s+WHERE\s+(.*?))?$/i);
326
+ if (!match) {
327
+ throw new Error(`Invalid UPDATE syntax: ${sql}`);
328
+ }
329
+
330
+ const tableName = match[1];
331
+ const setClause = match[2];
332
+ const whereClause = match[3];
333
+
334
+ const table = this.tables.get(tableName);
335
+ if (!table) {
336
+ throw new Error(`Table ${tableName} does not exist`);
337
+ }
338
+
339
+ let results = [...table.data];
340
+
341
+ // 应用 WHERE 条件
342
+ if (whereClause) {
343
+ results = this.applyWhereCondition(results, whereClause, params.slice(1));
344
+ }
345
+
346
+ // 解析 SET 子句
347
+ const setPairs = setClause.split(',').map(pair => pair.trim());
348
+ const updates: Record<string, any> = {};
349
+
350
+ setPairs.forEach((pair, index) => {
351
+ const [field, placeholder] = pair.split('=').map(p => p.trim());
352
+ const fieldName = field.replace(/"/g, '');
353
+ updates[fieldName] = params[index];
354
+ });
355
+
356
+ // 应用更新
357
+ let affectedRows = 0;
358
+ table.data.forEach(row => {
359
+ if (results.includes(row)) {
360
+ Object.assign(row, updates);
361
+ affectedRows++;
362
+ }
363
+ });
364
+
365
+ return { affectedRows };
366
+ }
367
+
368
+ private executeDelete(sql: string, params: any[] = []): any {
369
+ // 解析 DELETE 语句
370
+ const match = sql.match(/DELETE\s+FROM\s+"?(\w+)"?(?:\s+WHERE\s+(.*?))?$/i);
371
+ if (!match) {
372
+ throw new Error(`Invalid DELETE syntax: ${sql}`);
373
+ }
374
+
375
+ const tableName = match[1];
376
+ const whereClause = match[2];
377
+
378
+ const table = this.tables.get(tableName);
379
+ if (!table) {
380
+ throw new Error(`Table ${tableName} does not exist`);
381
+ }
382
+
383
+ let toDelete = [...table.data];
384
+
385
+ // 应用 WHERE 条件
386
+ if (whereClause) {
387
+ toDelete = this.applyWhereCondition(toDelete, whereClause, params);
388
+ }
389
+
390
+ // 删除记录
391
+ const affectedRows = toDelete.length;
392
+ table.data = table.data.filter(row => !toDelete.includes(row));
393
+
394
+ return { affectedRows };
395
+ }
396
+
397
+ private executeAlterTable(sql: string, params?: any[]): any {
398
+ // 简单的 ALTER TABLE 实现
399
+ const match = sql.match(/ALTER\s+TABLE\s+"?(\w+)"?\s+(.*)/i);
400
+ if (!match) {
401
+ throw new Error(`Invalid ALTER TABLE syntax: ${sql}`);
402
+ }
403
+
404
+ const tableName = match[1];
405
+ const alteration = match[2];
406
+
407
+ const table = this.tables.get(tableName);
408
+ if (!table) {
409
+ throw new Error(`Table ${tableName} does not exist`);
410
+ }
411
+
412
+ if (alteration.toLowerCase().startsWith('add column')) {
413
+ // 添加列的简单实现
414
+ const columnMatch = alteration.match(/ADD\s+COLUMN\s+"?(\w+)"?\s+(\w+)/i);
415
+ if (columnMatch) {
416
+ const columnName = columnMatch[1];
417
+ const columnType = columnMatch[2];
418
+ table.schema[columnName] = { type: columnType, nullable: true };
419
+
420
+ // 为现有记录添加默认值
421
+ table.data.forEach(row => {
422
+ row[columnName] = null;
423
+ });
424
+ }
425
+ }
426
+
427
+ return { affectedRows: 0 };
428
+ }
429
+
430
+ private executeDropTable(sql: string, params?: any[]): any {
431
+ const match = sql.match(/DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?"?(\w+)"?/i);
432
+ if (!match) {
433
+ throw new Error(`Invalid DROP TABLE syntax: ${sql}`);
434
+ }
435
+
436
+ const tableName = match[1];
437
+ const ifExists = sql.toLowerCase().includes('if exists');
438
+
439
+ const table = this.tables.get(tableName);
440
+ if (!table) {
441
+ if (ifExists) {
442
+ return { affectedRows: 0 };
443
+ } else {
444
+ throw new Error(`Table ${tableName} does not exist`);
445
+ }
446
+ }
447
+
448
+ this.tables.delete(tableName);
449
+ this.autoIncrementCounters.delete(tableName);
450
+
451
+ return { affectedRows: 0 };
452
+ }
453
+
454
+ private executeDropIndex(sql: string, params?: any[]): any {
455
+ const match = sql.match(/DROP\s+INDEX\s+(?:IF\s+EXISTS\s+)?"?(\w+)"?\s+ON\s+"?(\w+)"?/i);
456
+ if (!match) {
457
+ throw new Error(`Invalid DROP INDEX syntax: ${sql}`);
458
+ }
459
+
460
+ const indexName = match[1];
461
+ const tableName = match[2];
462
+ const ifExists = sql.toLowerCase().includes('if exists');
463
+
464
+ const table = this.tables.get(tableName);
465
+ if (!table) {
466
+ if (ifExists) {
467
+ return { affectedRows: 0 };
468
+ } else {
469
+ throw new Error(`Table ${tableName} does not exist`);
470
+ }
471
+ }
472
+
473
+ const indexExists = table.indexes.has(indexName);
474
+ if (!indexExists) {
475
+ if (ifExists) {
476
+ return { affectedRows: 0 };
477
+ } else {
478
+ throw new Error(`Index ${indexName} does not exist on table ${tableName}`);
479
+ }
480
+ }
481
+
482
+ table.indexes.delete(indexName);
483
+ return { affectedRows: 0 };
484
+ }
485
+
486
+ private executeCreateIndex(sql: string, params?: any[]): any {
487
+ const match = sql.match(/CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?"?(\w+)"?\s+ON\s+"?(\w+)"?\s*\(([^)]+)\)/i);
488
+ if (!match) {
489
+ throw new Error(`Invalid CREATE INDEX syntax: ${sql}`);
490
+ }
491
+
492
+ const indexName = match[1];
493
+ const tableName = match[2];
494
+ const columnsStr = match[3];
495
+ const unique = sql.toLowerCase().includes('unique');
496
+ const ifNotExists = sql.toLowerCase().includes('if not exists');
497
+
498
+ const table = this.tables.get(tableName);
499
+ if (!table) {
500
+ throw new Error(`Table ${tableName} does not exist`);
501
+ }
502
+
503
+ if (table.indexes.has(indexName)) {
504
+ if (ifNotExists) {
505
+ return { affectedRows: 0 };
506
+ } else {
507
+ throw new Error(`Index ${indexName} already exists on table ${tableName}`);
508
+ }
509
+ }
510
+
511
+ // 解析列名
512
+ const columns = columnsStr.split(',').map(col => col.trim().replace(/"/g, ''));
513
+
514
+ // 验证列是否存在
515
+ for (const column of columns) {
516
+ if (!(column in table.schema)) {
517
+ throw new Error(`Column ${column} does not exist in table ${tableName}`);
518
+ }
519
+ }
520
+
521
+ // 创建索引
522
+ const indexMap = new Map<any, number[]>();
523
+
524
+ // 为现有数据建立索引
525
+ table.data.forEach((row, rowIndex) => {
526
+ const key = columns.map(col => row[col]).join('|');
527
+ if (!indexMap.has(key)) {
528
+ indexMap.set(key, []);
529
+ }
530
+ indexMap.get(key)!.push(rowIndex);
531
+ });
532
+
533
+ table.indexes.set(indexName, { index: indexMap, columns, unique });
534
+ return { affectedRows: 0 };
535
+ }
536
+
537
+ private parseValues(valuesStr: string): any[] {
538
+ const values: any[] = [];
539
+ let current = '';
540
+ let inString = false;
541
+ let stringChar = '';
542
+ let parenDepth = 0;
543
+
544
+ for (let i = 0; i < valuesStr.length; i++) {
545
+ const char = valuesStr[i];
546
+
547
+ if (!inString && (char === "'" || char === '"')) {
548
+ inString = true;
549
+ stringChar = char;
550
+ current += char;
551
+ } else if (inString && char === stringChar) {
552
+ // 检查是否是转义字符
553
+ if (i > 0 && valuesStr[i - 1] === '\\') {
554
+ current += char;
555
+ } else {
556
+ inString = false;
557
+ current += char;
558
+ }
559
+ } else if (!inString && char === '(') {
560
+ parenDepth++;
561
+ current += char;
562
+ } else if (!inString && char === ')') {
563
+ parenDepth--;
564
+ current += char;
565
+ } else if (!inString && char === ',' && parenDepth === 0) {
566
+ values.push(this.parseValue(current.trim()));
567
+ current = '';
568
+ } else {
569
+ current += char;
570
+ }
571
+ }
572
+
573
+ if (current.trim()) {
574
+ values.push(this.parseValue(current.trim()));
575
+ }
576
+
577
+ return values;
578
+ }
579
+
580
+ private parseValue(value: string): any {
581
+ value = value.trim();
582
+
583
+ // 参数占位符
584
+ if (value === '?') return undefined;
585
+
586
+ // NULL 值
587
+ if (value.toUpperCase() === 'NULL') return null;
588
+
589
+ // 布尔值
590
+ if (value.toLowerCase() === 'true') return true;
591
+ if (value.toLowerCase() === 'false') return false;
592
+
593
+ // 字符串(单引号或双引号)
594
+ if ((value.startsWith("'") && value.endsWith("'")) ||
595
+ (value.startsWith('"') && value.endsWith('"'))) {
596
+ return this.unescapeString(value.slice(1, -1));
597
+ }
598
+
599
+ // 数字
600
+ if (!isNaN(Number(value)) && value !== '') {
601
+ return Number(value);
602
+ }
603
+
604
+ // JSON 对象
605
+ if (value.startsWith('{') && value.endsWith('}')) {
606
+ try {
607
+ return JSON.parse(value);
608
+ } catch {
609
+ return value;
610
+ }
611
+ }
612
+
613
+ // JSON 数组
614
+ if (value.startsWith('[') && value.endsWith(']')) {
615
+ try {
616
+ return JSON.parse(value);
617
+ } catch {
618
+ return value;
619
+ }
620
+ }
621
+
622
+ // 日期
623
+ if (this.isDateString(value)) {
624
+ return new Date(value);
625
+ }
626
+
627
+ return value;
628
+ }
629
+
630
+ private unescapeString(str: string): string {
631
+ return str
632
+ .replace(/\\'/g, "'")
633
+ .replace(/\\"/g, '"')
634
+ .replace(/\\\\/g, '\\')
635
+ .replace(/\\n/g, '\n')
636
+ .replace(/\\r/g, '\r')
637
+ .replace(/\\t/g, '\t');
638
+ }
639
+
640
+ private isDateString(str: string): boolean {
641
+ // 简单的日期字符串检测
642
+ const dateRegex = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/;
643
+ return dateRegex.test(str) && !isNaN(Date.parse(str));
644
+ }
645
+
646
+ private applyWhereCondition(data: any[], whereClause: string, params: any[]): any[] {
647
+ if (!whereClause.trim()) return data;
648
+
649
+ try {
650
+ const condition = this.parseWhereClause(whereClause, params);
651
+ return data.filter(row => this.evaluateCondition(row, condition));
652
+ } catch (error) {
653
+ console.warn('WHERE clause parsing error:', error);
654
+ return data;
655
+ }
656
+ }
657
+
658
+ private parseWhereClause(whereClause: string, params: any[]): any {
659
+ // 处理括号优先级
660
+ const tokens = this.tokenizeWhereClause(whereClause);
661
+ return this.parseLogicalExpression(tokens, params);
662
+ }
663
+
664
+ private tokenizeWhereClause(whereClause: string): string[] {
665
+ const tokens: string[] = [];
666
+ let current = '';
667
+ let inString = false;
668
+ let stringChar = '';
669
+ let parenDepth = 0;
670
+
671
+ for (let i = 0; i < whereClause.length; i++) {
672
+ const char = whereClause[i];
673
+
674
+ if (!inString && (char === "'" || char === '"')) {
675
+ inString = true;
676
+ stringChar = char;
677
+ current += char;
678
+ } else if (inString && char === stringChar) {
679
+ if (i > 0 && whereClause[i - 1] === '\\') {
680
+ current += char;
681
+ } else {
682
+ inString = false;
683
+ current += char;
684
+ }
685
+ } else if (!inString && char === '(') {
686
+ if (current.trim()) {
687
+ tokens.push(current.trim());
688
+ current = '';
689
+ }
690
+ tokens.push('(');
691
+ parenDepth++;
692
+ } else if (!inString && char === ')') {
693
+ if (current.trim()) {
694
+ tokens.push(current.trim());
695
+ current = '';
696
+ }
697
+ tokens.push(')');
698
+ parenDepth--;
699
+ } else if (!inString && /\s/.test(char)) {
700
+ if (current.trim()) {
701
+ tokens.push(current.trim());
702
+ current = '';
703
+ }
704
+ } else if (!inString && /[=<>!]/.test(char)) {
705
+ if (current.trim()) {
706
+ tokens.push(current.trim());
707
+ current = '';
708
+ }
709
+ current += char;
710
+ } else {
711
+ current += char;
712
+ }
713
+ }
714
+
715
+ if (current.trim()) {
716
+ tokens.push(current.trim());
717
+ }
718
+
719
+ return tokens;
720
+ }
721
+
722
+ private parseLogicalExpression(tokens: string[], params: any[]): any {
723
+ // 处理 AND 和 OR 操作符
724
+ let result = this.parseComparisonExpression(tokens, params);
725
+
726
+ while (tokens.length > 0) {
727
+ const operator = tokens[0].toUpperCase();
728
+ if (operator === 'AND' || operator === 'OR') {
729
+ tokens.shift(); // 移除操作符
730
+ const right = this.parseComparisonExpression(tokens, params);
731
+ result = { type: 'logical', operator: operator.toLowerCase(), left: result, right };
732
+ } else {
733
+ break;
734
+ }
735
+ }
736
+
737
+ return result;
738
+ }
739
+
740
+ private parseComparisonExpression(tokens: string[], params: any[]): any {
741
+ // 处理括号
742
+ if (tokens.length > 0 && tokens[0] === '(') {
743
+ tokens.shift(); // 移除 '('
744
+ const result = this.parseLogicalExpression(tokens, params);
745
+ if (tokens.length > 0 && (tokens[0] as string) === ')') {
746
+ tokens.shift(); // 移除 ')'
747
+ }
748
+ return result;
749
+ }
750
+
751
+ // 处理比较表达式
752
+ if (tokens.length >= 3) {
753
+ const field = tokens[0].replace(/"/g, '');
754
+ const operator = tokens[1];
755
+ let value = tokens[2];
756
+
757
+ // 移除已处理的 tokens
758
+ tokens.splice(0, 3);
759
+
760
+ // 处理参数占位符
761
+ if (value === '?') {
762
+ value = params.shift();
763
+ } else {
764
+ value = this.parseValue(value);
765
+ }
766
+
767
+ return { type: 'comparison', field, operator, value };
768
+ }
769
+
770
+ throw new Error('Invalid comparison expression');
771
+ }
772
+
773
+ private evaluateCondition(row: any, condition: any): boolean {
774
+ if (!condition) return true;
775
+
776
+ switch (condition.type) {
777
+ case 'logical':
778
+ const left = this.evaluateCondition(row, condition.left);
779
+ const right = this.evaluateCondition(row, condition.right);
780
+
781
+ if (condition.operator === 'and') {
782
+ return left && right;
783
+ } else if (condition.operator === 'or') {
784
+ return left || right;
785
+ }
786
+ return false;
787
+
788
+ case 'comparison':
789
+ return this.evaluateComparison(row[condition.field], condition.operator, condition.value);
790
+
791
+ default:
792
+ return true;
793
+ }
794
+ }
795
+
796
+ private evaluateComparison(fieldValue: any, operator: string, value: any): boolean {
797
+ switch (operator) {
798
+ case '=':
799
+ return fieldValue == value;
800
+ case '!=':
801
+ case '<>':
802
+ return fieldValue != value;
803
+ case '>':
804
+ return fieldValue > value;
805
+ case '>=':
806
+ return fieldValue >= value;
807
+ case '<':
808
+ return fieldValue < value;
809
+ case '<=':
810
+ return fieldValue <= value;
811
+ case 'LIKE':
812
+ if (typeof fieldValue === 'string' && typeof value === 'string') {
813
+ const pattern = value.replace(/%/g, '.*').replace(/_/g, '.');
814
+ return new RegExp(`^${pattern}$`, 'i').test(fieldValue);
815
+ }
816
+ return false;
817
+ case 'NOT LIKE':
818
+ if (typeof fieldValue === 'string' && typeof value === 'string') {
819
+ const pattern = value.replace(/%/g, '.*').replace(/_/g, '.');
820
+ return !new RegExp(`^${pattern}$`, 'i').test(fieldValue);
821
+ }
822
+ return true;
823
+ case 'IN':
824
+ if (Array.isArray(value)) {
825
+ return value.includes(fieldValue);
826
+ }
827
+ return false;
828
+ case 'NOT IN':
829
+ if (Array.isArray(value)) {
830
+ return !value.includes(fieldValue);
831
+ }
832
+ return true;
833
+ case 'IS NULL':
834
+ return fieldValue === null || fieldValue === undefined;
835
+ case 'IS NOT NULL':
836
+ return fieldValue !== null && fieldValue !== undefined;
837
+ case 'BETWEEN':
838
+ if (Array.isArray(value) && value.length === 2) {
839
+ return fieldValue >= value[0] && fieldValue <= value[1];
840
+ }
841
+ return false;
842
+ case 'NOT BETWEEN':
843
+ if (Array.isArray(value) && value.length === 2) {
844
+ return fieldValue < value[0] || fieldValue > value[1];
845
+ }
846
+ return true;
847
+ default:
848
+ return true;
849
+ }
850
+ }
851
+
852
+ private applyOrderBy(data: any[], orderClause: string): any[] {
853
+ const orderParts = orderClause.split(',').map(part => part.trim());
854
+
855
+ return data.sort((a, b) => {
856
+ for (const part of orderParts) {
857
+ const match = part.match(/"?(\w+)"?\s*(ASC|DESC)?/i);
858
+ if (!match) continue;
859
+
860
+ const field = match[1];
861
+ const direction = (match[2] || 'ASC').toUpperCase();
862
+
863
+ const aVal = a[field];
864
+ const bVal = b[field];
865
+
866
+ let comparison = 0;
867
+ if (aVal < bVal) comparison = -1;
868
+ else if (aVal > bVal) comparison = 1;
869
+
870
+ if (direction === 'DESC') comparison *= -1;
871
+
872
+ if (comparison !== 0) return comparison;
873
+ }
874
+ return 0;
875
+ });
876
+ }
877
+ }
878
+ Registry.register('memory', (config, schemas?: any) => {
879
+ return new RelatedDatabase(new MemoryDialect(config), schemas);
880
+ });