feat: SQL 语法支持 BETWEEN AND、DROP TABLE IF EXISTS
This commit is contained in:
Vendored
+40
-1
@@ -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 IN(NOT 后紧跟 LIKE 或 IN)
|
||||
if (this.curTokenIs(TokenType.NOT)) {
|
||||
if (this.peekTokenIs(TokenType.IN)) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+5
@@ -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",
|
||||
|
||||
Vendored
+40
-1
@@ -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 IN(NOT 后紧跟 LIKE 或 IN)
|
||||
if (this.curTokenIs(TokenType.NOT)) {
|
||||
if (this.peekTokenIs(TokenType.IN)) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+40
-1
@@ -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 IN(NOT 后紧跟 LIKE 或 IN)
|
||||
if (this.curTokenIs(TokenType.NOT)) {
|
||||
if (this.peekTokenIs(TokenType.IN)) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user