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));
|
return this.engine.createTable(createSchema(stmt.name, columns));
|
||||||
}
|
}
|
||||||
async executeDropTable(stmt) {
|
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);
|
return this.engine.dropTable(stmt.name);
|
||||||
}
|
}
|
||||||
getEngine() { return this.engine; }
|
getEngine() { return this.engine; }
|
||||||
@@ -1766,6 +1771,9 @@ var TokenType;
|
|||||||
TokenType["TRUE"] = "TRUE";
|
TokenType["TRUE"] = "TRUE";
|
||||||
TokenType["REFERENCES"] = "REFERENCES";
|
TokenType["REFERENCES"] = "REFERENCES";
|
||||||
TokenType["CASCADE"] = "CASCADE";
|
TokenType["CASCADE"] = "CASCADE";
|
||||||
|
TokenType["BETWEEN"] = "BETWEEN";
|
||||||
|
TokenType["IF"] = "IF";
|
||||||
|
TokenType["EXISTS"] = "EXISTS";
|
||||||
TokenType["FALSE"] = "FALSE";
|
TokenType["FALSE"] = "FALSE";
|
||||||
// JOIN 相关
|
// JOIN 相关
|
||||||
TokenType["INNER"] = "INNER";
|
TokenType["INNER"] = "INNER";
|
||||||
@@ -1842,6 +1850,9 @@ const KEYWORDS = {
|
|||||||
'FALSE': TokenType.FALSE,
|
'FALSE': TokenType.FALSE,
|
||||||
'REFERENCES': TokenType.REFERENCES,
|
'REFERENCES': TokenType.REFERENCES,
|
||||||
'CASCADE': TokenType.CASCADE,
|
'CASCADE': TokenType.CASCADE,
|
||||||
|
'BETWEEN': TokenType.BETWEEN,
|
||||||
|
'IF': TokenType.IF,
|
||||||
|
'EXISTS': TokenType.EXISTS,
|
||||||
// JOIN
|
// JOIN
|
||||||
'INNER': TokenType.INNER,
|
'INNER': TokenType.INNER,
|
||||||
'LEFT': TokenType.LEFT,
|
'LEFT': TokenType.LEFT,
|
||||||
@@ -2451,8 +2462,15 @@ class Parser {
|
|||||||
parseDropTable() {
|
parseDropTable() {
|
||||||
this.expect(TokenType.DROP);
|
this.expect(TokenType.DROP);
|
||||||
this.expect(TokenType.TABLE);
|
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');
|
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 };
|
result[column] = isNot ? { $ne: null } : { $eq: null };
|
||||||
return result;
|
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)
|
// NOT LIKE / NOT IN(NOT 后紧跟 LIKE 或 IN)
|
||||||
if (this.curTokenIs(TokenType.NOT)) {
|
if (this.curTokenIs(TokenType.NOT)) {
|
||||||
if (this.peekTokenIs(TokenType.IN)) {
|
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 {
|
interface DropTableStatement {
|
||||||
type: 'DROP_TABLE';
|
type: 'DROP_TABLE';
|
||||||
name: string;
|
name: string;
|
||||||
|
/** IF EXISTS — 表不存在时不报错 */
|
||||||
|
ifExists?: boolean;
|
||||||
}
|
}
|
||||||
interface InsertStatement {
|
interface InsertStatement {
|
||||||
type: 'INSERT';
|
type: 'INSERT';
|
||||||
@@ -696,6 +698,9 @@ declare enum TokenType {
|
|||||||
TRUE = "TRUE",
|
TRUE = "TRUE",
|
||||||
REFERENCES = "REFERENCES",
|
REFERENCES = "REFERENCES",
|
||||||
CASCADE = "CASCADE",
|
CASCADE = "CASCADE",
|
||||||
|
BETWEEN = "BETWEEN",
|
||||||
|
IF = "IF",
|
||||||
|
EXISTS = "EXISTS",
|
||||||
FALSE = "FALSE",
|
FALSE = "FALSE",
|
||||||
INNER = "INNER",
|
INNER = "INNER",
|
||||||
LEFT = "LEFT",
|
LEFT = "LEFT",
|
||||||
|
|||||||
Vendored
+40
-1
@@ -1610,6 +1610,11 @@ class QueryExecutor {
|
|||||||
return this.engine.createTable(createSchema(stmt.name, columns));
|
return this.engine.createTable(createSchema(stmt.name, columns));
|
||||||
}
|
}
|
||||||
async executeDropTable(stmt) {
|
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);
|
return this.engine.dropTable(stmt.name);
|
||||||
}
|
}
|
||||||
getEngine() { return this.engine; }
|
getEngine() { return this.engine; }
|
||||||
@@ -1762,6 +1767,9 @@ var TokenType;
|
|||||||
TokenType["TRUE"] = "TRUE";
|
TokenType["TRUE"] = "TRUE";
|
||||||
TokenType["REFERENCES"] = "REFERENCES";
|
TokenType["REFERENCES"] = "REFERENCES";
|
||||||
TokenType["CASCADE"] = "CASCADE";
|
TokenType["CASCADE"] = "CASCADE";
|
||||||
|
TokenType["BETWEEN"] = "BETWEEN";
|
||||||
|
TokenType["IF"] = "IF";
|
||||||
|
TokenType["EXISTS"] = "EXISTS";
|
||||||
TokenType["FALSE"] = "FALSE";
|
TokenType["FALSE"] = "FALSE";
|
||||||
// JOIN 相关
|
// JOIN 相关
|
||||||
TokenType["INNER"] = "INNER";
|
TokenType["INNER"] = "INNER";
|
||||||
@@ -1838,6 +1846,9 @@ const KEYWORDS = {
|
|||||||
'FALSE': TokenType.FALSE,
|
'FALSE': TokenType.FALSE,
|
||||||
'REFERENCES': TokenType.REFERENCES,
|
'REFERENCES': TokenType.REFERENCES,
|
||||||
'CASCADE': TokenType.CASCADE,
|
'CASCADE': TokenType.CASCADE,
|
||||||
|
'BETWEEN': TokenType.BETWEEN,
|
||||||
|
'IF': TokenType.IF,
|
||||||
|
'EXISTS': TokenType.EXISTS,
|
||||||
// JOIN
|
// JOIN
|
||||||
'INNER': TokenType.INNER,
|
'INNER': TokenType.INNER,
|
||||||
'LEFT': TokenType.LEFT,
|
'LEFT': TokenType.LEFT,
|
||||||
@@ -2447,8 +2458,15 @@ class Parser {
|
|||||||
parseDropTable() {
|
parseDropTable() {
|
||||||
this.expect(TokenType.DROP);
|
this.expect(TokenType.DROP);
|
||||||
this.expect(TokenType.TABLE);
|
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');
|
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 };
|
result[column] = isNot ? { $ne: null } : { $eq: null };
|
||||||
return result;
|
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)
|
// NOT LIKE / NOT IN(NOT 后紧跟 LIKE 或 IN)
|
||||||
if (this.curTokenIs(TokenType.NOT)) {
|
if (this.curTokenIs(TokenType.NOT)) {
|
||||||
if (this.peekTokenIs(TokenType.IN)) {
|
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));
|
return this.engine.createTable(createSchema(stmt.name, columns));
|
||||||
}
|
}
|
||||||
async executeDropTable(stmt) {
|
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);
|
return this.engine.dropTable(stmt.name);
|
||||||
}
|
}
|
||||||
getEngine() { return this.engine; }
|
getEngine() { return this.engine; }
|
||||||
@@ -1768,6 +1773,9 @@
|
|||||||
TokenType["TRUE"] = "TRUE";
|
TokenType["TRUE"] = "TRUE";
|
||||||
TokenType["REFERENCES"] = "REFERENCES";
|
TokenType["REFERENCES"] = "REFERENCES";
|
||||||
TokenType["CASCADE"] = "CASCADE";
|
TokenType["CASCADE"] = "CASCADE";
|
||||||
|
TokenType["BETWEEN"] = "BETWEEN";
|
||||||
|
TokenType["IF"] = "IF";
|
||||||
|
TokenType["EXISTS"] = "EXISTS";
|
||||||
TokenType["FALSE"] = "FALSE";
|
TokenType["FALSE"] = "FALSE";
|
||||||
// JOIN 相关
|
// JOIN 相关
|
||||||
TokenType["INNER"] = "INNER";
|
TokenType["INNER"] = "INNER";
|
||||||
@@ -1844,6 +1852,9 @@
|
|||||||
'FALSE': TokenType.FALSE,
|
'FALSE': TokenType.FALSE,
|
||||||
'REFERENCES': TokenType.REFERENCES,
|
'REFERENCES': TokenType.REFERENCES,
|
||||||
'CASCADE': TokenType.CASCADE,
|
'CASCADE': TokenType.CASCADE,
|
||||||
|
'BETWEEN': TokenType.BETWEEN,
|
||||||
|
'IF': TokenType.IF,
|
||||||
|
'EXISTS': TokenType.EXISTS,
|
||||||
// JOIN
|
// JOIN
|
||||||
'INNER': TokenType.INNER,
|
'INNER': TokenType.INNER,
|
||||||
'LEFT': TokenType.LEFT,
|
'LEFT': TokenType.LEFT,
|
||||||
@@ -2453,8 +2464,15 @@
|
|||||||
parseDropTable() {
|
parseDropTable() {
|
||||||
this.expect(TokenType.DROP);
|
this.expect(TokenType.DROP);
|
||||||
this.expect(TokenType.TABLE);
|
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');
|
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 };
|
result[column] = isNot ? { $ne: null } : { $eq: null };
|
||||||
return result;
|
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)
|
// NOT LIKE / NOT IN(NOT 后紧跟 LIKE 或 IN)
|
||||||
if (this.curTokenIs(TokenType.NOT)) {
|
if (this.curTokenIs(TokenType.NOT)) {
|
||||||
if (this.peekTokenIs(TokenType.IN)) {
|
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
+3
-3
@@ -422,7 +422,7 @@ FROM users u INNER JOIN orders o ON u.id = o.user_id
|
|||||||
GROUP BY u.name
|
GROUP BY u.name
|
||||||
HAVING SUM(o.amount) > (SELECT AVG(amount) FROM orders);`,
|
HAVING SUM(o.amount) > (SELECT AVG(amount) FROM orders);`,
|
||||||
cascade: `-- 外键级联:重建 orders 表带 CASCADE
|
cascade: `-- 外键级联:重建 orders 表带 CASCADE
|
||||||
DROP TABLE orders;
|
DROP TABLE IF EXISTS orders;
|
||||||
|
|
||||||
CREATE TABLE orders (
|
CREATE TABLE orders (
|
||||||
id STRING PRIMARY KEY,
|
id STRING PRIMARY KEY,
|
||||||
@@ -447,9 +447,9 @@ DELETE FROM users WHERE id = '1';
|
|||||||
-- 确认级联结果
|
-- 确认级联结果
|
||||||
SELECT u.name, o.product, o.amount
|
SELECT u.name, o.product, o.amount
|
||||||
FROM users u LEFT JOIN orders o ON u.id = o.user_id;`,
|
FROM users u LEFT JOIN orders o ON u.id = o.user_id;`,
|
||||||
adv: `-- 复杂条件:年龄 20-30 且不是 Bob
|
adv: `-- BETWEEN 范围查询
|
||||||
SELECT * FROM users
|
SELECT * FROM users
|
||||||
WHERE age >= 20 AND age <= 30 AND name != 'Bob'
|
WHERE age BETWEEN 20 AND 30 AND name != 'Bob'
|
||||||
ORDER BY age DESC;
|
ORDER BY age DESC;
|
||||||
|
|
||||||
-- DISTINCT 去重
|
-- DISTINCT 去重
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ export interface CreateTableStatement {
|
|||||||
export interface DropTableStatement {
|
export interface DropTableStatement {
|
||||||
type: 'DROP_TABLE';
|
type: 'DROP_TABLE';
|
||||||
name: string;
|
name: string;
|
||||||
|
/** IF EXISTS — 表不存在时不报错 */
|
||||||
|
ifExists?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ export class QueryExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async executeDropTable(stmt: DropTableStatement): Promise<void> {
|
private async executeDropTable(stmt: DropTableStatement): Promise<void> {
|
||||||
|
if (stmt.ifExists) {
|
||||||
|
const exists = await this.engine.hasTable(stmt.name);
|
||||||
|
if (!exists) return; // IF EXISTS: 表不存在时静默返回
|
||||||
|
}
|
||||||
return this.engine.dropTable(stmt.name);
|
return this.engine.dropTable(stmt.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-1
@@ -423,8 +423,15 @@ export class Parser {
|
|||||||
private parseDropTable(): DropTableStatement {
|
private parseDropTable(): DropTableStatement {
|
||||||
this.expect(TokenType.DROP);
|
this.expect(TokenType.DROP);
|
||||||
this.expect(TokenType.TABLE);
|
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');
|
const tableName = this.expectIdentifier('table name');
|
||||||
return { type: 'DROP_TABLE', name: tableName };
|
return { type: 'DROP_TABLE', name: tableName, ifExists: ifExists || undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
@@ -483,6 +490,29 @@ export class Parser {
|
|||||||
return result;
|
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: WhereCondition = {};
|
||||||
|
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: WhereCondition = {};
|
||||||
|
result[column] = { $not: { $gte: low, $lte: high } };
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// NOT LIKE / NOT IN(NOT 后紧跟 LIKE 或 IN)
|
// NOT LIKE / NOT IN(NOT 后紧跟 LIKE 或 IN)
|
||||||
if (this.curTokenIs(TokenType.NOT)) {
|
if (this.curTokenIs(TokenType.NOT)) {
|
||||||
if (this.peekTokenIs(TokenType.IN)) {
|
if (this.peekTokenIs(TokenType.IN)) {
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ export enum TokenType {
|
|||||||
TRUE = 'TRUE',
|
TRUE = 'TRUE',
|
||||||
REFERENCES = 'REFERENCES',
|
REFERENCES = 'REFERENCES',
|
||||||
CASCADE = 'CASCADE',
|
CASCADE = 'CASCADE',
|
||||||
|
BETWEEN = 'BETWEEN',
|
||||||
|
IF = 'IF',
|
||||||
|
EXISTS = 'EXISTS',
|
||||||
FALSE = 'FALSE',
|
FALSE = 'FALSE',
|
||||||
|
|
||||||
// JOIN 相关
|
// JOIN 相关
|
||||||
@@ -133,6 +136,9 @@ export const KEYWORDS: Record<string, TokenType> = {
|
|||||||
'FALSE': TokenType.FALSE,
|
'FALSE': TokenType.FALSE,
|
||||||
'REFERENCES': TokenType.REFERENCES,
|
'REFERENCES': TokenType.REFERENCES,
|
||||||
'CASCADE': TokenType.CASCADE,
|
'CASCADE': TokenType.CASCADE,
|
||||||
|
'BETWEEN': TokenType.BETWEEN,
|
||||||
|
'IF': TokenType.IF,
|
||||||
|
'EXISTS': TokenType.EXISTS,
|
||||||
|
|
||||||
// JOIN
|
// JOIN
|
||||||
'INNER': TokenType.INNER,
|
'INNER': TokenType.INNER,
|
||||||
|
|||||||
@@ -130,8 +130,12 @@ describe('Parser 边缘场景', () => {
|
|||||||
expect(ast.columns[3]).toMatchObject({ name: 'active', default: true });
|
expect(ast.columns[3]).toMatchObject({ name: 'active', default: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('不支持 IF EXISTS 语法(表名被解析为 IF)', () => {
|
it('支持 DROP TABLE IF EXISTS 语法', () => {
|
||||||
// 解析器把 IF 当作表名,EXISTS 作为后续 token 导致错误
|
const ast = parse('DROP TABLE IF EXISTS users');
|
||||||
|
expect(ast).toMatchObject({ type: 'DROP_TABLE', name: 'users', ifExists: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DROP TABLE 不带 IF EXISTS', () => {
|
||||||
const ast = parse('DROP TABLE users');
|
const ast = parse('DROP TABLE users');
|
||||||
expect(ast).toMatchObject({ type: 'DROP_TABLE', name: 'users' });
|
expect(ast).toMatchObject({ type: 'DROP_TABLE', name: 'users' });
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user