feat: SQL 语法支持 BETWEEN AND、DROP TABLE IF EXISTS
CI / test (18.x) (push) Waiting to run
CI / test (20.x) (push) Waiting to run
CI / test (22.x) (push) Waiting to run
CI / test (24.x) (push) Waiting to run

This commit is contained in:
thzxx
2026-07-26 17:55:45 +08:00
parent 287f2a2b22
commit 1f8e11bf72
14 changed files with 181 additions and 13 deletions
+40 -1
View File
@@ -1614,6 +1614,11 @@ class QueryExecutor {
return this.engine.createTable(createSchema(stmt.name, columns));
}
async executeDropTable(stmt) {
if (stmt.ifExists) {
const exists = await this.engine.hasTable(stmt.name);
if (!exists)
return; // IF EXISTS: 表不存在时静默返回
}
return this.engine.dropTable(stmt.name);
}
getEngine() { return this.engine; }
@@ -1766,6 +1771,9 @@ var TokenType;
TokenType["TRUE"] = "TRUE";
TokenType["REFERENCES"] = "REFERENCES";
TokenType["CASCADE"] = "CASCADE";
TokenType["BETWEEN"] = "BETWEEN";
TokenType["IF"] = "IF";
TokenType["EXISTS"] = "EXISTS";
TokenType["FALSE"] = "FALSE";
// JOIN 相关
TokenType["INNER"] = "INNER";
@@ -1842,6 +1850,9 @@ const KEYWORDS = {
'FALSE': TokenType.FALSE,
'REFERENCES': TokenType.REFERENCES,
'CASCADE': TokenType.CASCADE,
'BETWEEN': TokenType.BETWEEN,
'IF': TokenType.IF,
'EXISTS': TokenType.EXISTS,
// JOIN
'INNER': TokenType.INNER,
'LEFT': TokenType.LEFT,
@@ -2451,8 +2462,15 @@ class Parser {
parseDropTable() {
this.expect(TokenType.DROP);
this.expect(TokenType.TABLE);
// IF EXISTS(可选)
let ifExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.EXISTS);
ifExists = true;
}
const tableName = this.expectIdentifier('table name');
return { type: 'DROP_TABLE', name: tableName };
return { type: 'DROP_TABLE', name: tableName, ifExists: ifExists || undefined };
}
// ===================================================================
// 条件表达式
@@ -2503,6 +2521,27 @@ class Parser {
result[column] = isNot ? { $ne: null } : { $eq: null };
return result;
}
// BETWEEN val1 AND val2
if (this.curTokenIs(TokenType.BETWEEN)) {
this.nextToken();
const low = this.parseValue();
this.expect(TokenType.AND);
const high = this.parseValue();
const result = {};
result[column] = { $gte: low, $lte: high };
return result;
}
// NOT BETWEEN val1 AND val2
if (this.curTokenIs(TokenType.NOT) && this.peekTokenIs(TokenType.BETWEEN)) {
this.nextToken(); // skip NOT
this.nextToken(); // skip BETWEEN
const low = this.parseValue();
this.expect(TokenType.AND);
const high = this.parseValue();
const result = {};
result[column] = { $not: { $gte: low, $lte: high } };
return result;
}
// NOT LIKE / NOT INNOT 后紧跟 LIKE 或 IN
if (this.curTokenIs(TokenType.NOT)) {
if (this.peekTokenIs(TokenType.IN)) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+5
View File
@@ -228,6 +228,8 @@ interface CreateTableStatement {
interface DropTableStatement {
type: 'DROP_TABLE';
name: string;
/** IF EXISTS — 表不存在时不报错 */
ifExists?: boolean;
}
interface InsertStatement {
type: 'INSERT';
@@ -696,6 +698,9 @@ declare enum TokenType {
TRUE = "TRUE",
REFERENCES = "REFERENCES",
CASCADE = "CASCADE",
BETWEEN = "BETWEEN",
IF = "IF",
EXISTS = "EXISTS",
FALSE = "FALSE",
INNER = "INNER",
LEFT = "LEFT",
+40 -1
View File
@@ -1610,6 +1610,11 @@ class QueryExecutor {
return this.engine.createTable(createSchema(stmt.name, columns));
}
async executeDropTable(stmt) {
if (stmt.ifExists) {
const exists = await this.engine.hasTable(stmt.name);
if (!exists)
return; // IF EXISTS: 表不存在时静默返回
}
return this.engine.dropTable(stmt.name);
}
getEngine() { return this.engine; }
@@ -1762,6 +1767,9 @@ var TokenType;
TokenType["TRUE"] = "TRUE";
TokenType["REFERENCES"] = "REFERENCES";
TokenType["CASCADE"] = "CASCADE";
TokenType["BETWEEN"] = "BETWEEN";
TokenType["IF"] = "IF";
TokenType["EXISTS"] = "EXISTS";
TokenType["FALSE"] = "FALSE";
// JOIN 相关
TokenType["INNER"] = "INNER";
@@ -1838,6 +1846,9 @@ const KEYWORDS = {
'FALSE': TokenType.FALSE,
'REFERENCES': TokenType.REFERENCES,
'CASCADE': TokenType.CASCADE,
'BETWEEN': TokenType.BETWEEN,
'IF': TokenType.IF,
'EXISTS': TokenType.EXISTS,
// JOIN
'INNER': TokenType.INNER,
'LEFT': TokenType.LEFT,
@@ -2447,8 +2458,15 @@ class Parser {
parseDropTable() {
this.expect(TokenType.DROP);
this.expect(TokenType.TABLE);
// IF EXISTS(可选)
let ifExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.EXISTS);
ifExists = true;
}
const tableName = this.expectIdentifier('table name');
return { type: 'DROP_TABLE', name: tableName };
return { type: 'DROP_TABLE', name: tableName, ifExists: ifExists || undefined };
}
// ===================================================================
// 条件表达式
@@ -2499,6 +2517,27 @@ class Parser {
result[column] = isNot ? { $ne: null } : { $eq: null };
return result;
}
// BETWEEN val1 AND val2
if (this.curTokenIs(TokenType.BETWEEN)) {
this.nextToken();
const low = this.parseValue();
this.expect(TokenType.AND);
const high = this.parseValue();
const result = {};
result[column] = { $gte: low, $lte: high };
return result;
}
// NOT BETWEEN val1 AND val2
if (this.curTokenIs(TokenType.NOT) && this.peekTokenIs(TokenType.BETWEEN)) {
this.nextToken(); // skip NOT
this.nextToken(); // skip BETWEEN
const low = this.parseValue();
this.expect(TokenType.AND);
const high = this.parseValue();
const result = {};
result[column] = { $not: { $gte: low, $lte: high } };
return result;
}
// NOT LIKE / NOT INNOT 后紧跟 LIKE 或 IN
if (this.curTokenIs(TokenType.NOT)) {
if (this.peekTokenIs(TokenType.IN)) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+40 -1
View File
@@ -1616,6 +1616,11 @@
return this.engine.createTable(createSchema(stmt.name, columns));
}
async executeDropTable(stmt) {
if (stmt.ifExists) {
const exists = await this.engine.hasTable(stmt.name);
if (!exists)
return; // IF EXISTS: 表不存在时静默返回
}
return this.engine.dropTable(stmt.name);
}
getEngine() { return this.engine; }
@@ -1768,6 +1773,9 @@
TokenType["TRUE"] = "TRUE";
TokenType["REFERENCES"] = "REFERENCES";
TokenType["CASCADE"] = "CASCADE";
TokenType["BETWEEN"] = "BETWEEN";
TokenType["IF"] = "IF";
TokenType["EXISTS"] = "EXISTS";
TokenType["FALSE"] = "FALSE";
// JOIN 相关
TokenType["INNER"] = "INNER";
@@ -1844,6 +1852,9 @@
'FALSE': TokenType.FALSE,
'REFERENCES': TokenType.REFERENCES,
'CASCADE': TokenType.CASCADE,
'BETWEEN': TokenType.BETWEEN,
'IF': TokenType.IF,
'EXISTS': TokenType.EXISTS,
// JOIN
'INNER': TokenType.INNER,
'LEFT': TokenType.LEFT,
@@ -2453,8 +2464,15 @@
parseDropTable() {
this.expect(TokenType.DROP);
this.expect(TokenType.TABLE);
// IF EXISTS(可选)
let ifExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.EXISTS);
ifExists = true;
}
const tableName = this.expectIdentifier('table name');
return { type: 'DROP_TABLE', name: tableName };
return { type: 'DROP_TABLE', name: tableName, ifExists: ifExists || undefined };
}
// ===================================================================
// 条件表达式
@@ -2505,6 +2523,27 @@
result[column] = isNot ? { $ne: null } : { $eq: null };
return result;
}
// BETWEEN val1 AND val2
if (this.curTokenIs(TokenType.BETWEEN)) {
this.nextToken();
const low = this.parseValue();
this.expect(TokenType.AND);
const high = this.parseValue();
const result = {};
result[column] = { $gte: low, $lte: high };
return result;
}
// NOT BETWEEN val1 AND val2
if (this.curTokenIs(TokenType.NOT) && this.peekTokenIs(TokenType.BETWEEN)) {
this.nextToken(); // skip NOT
this.nextToken(); // skip BETWEEN
const low = this.parseValue();
this.expect(TokenType.AND);
const high = this.parseValue();
const result = {};
result[column] = { $not: { $gte: low, $lte: high } };
return result;
}
// NOT LIKE / NOT INNOT 后紧跟 LIKE 或 IN
if (this.curTokenIs(TokenType.NOT)) {
if (this.peekTokenIs(TokenType.IN)) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long