feat: SQL 语法支持 CREATE TABLE IF NOT EXISTS
CI / test (20.x) (push) Canceled after 0s
CI / test (22.x) (push) Canceled after 0s
CI / test (24.x) (push) Canceled after 0s
CI / test (18.x) (push) Canceled after 10s

- Parser 支持 CREATE TABLE IF NOT EXISTS 语法
- AST CreateTableStatement 新增 ifNotExists 字段
- Executor 表已存在时静默跳过
- 修复 demo.html Aria 预设报错
This commit is contained in:
2026-07-27 17:18:52 +08:00
parent f84673e519
commit 648526c188
11 changed files with 69 additions and 8 deletions
+15 -1
View File
@@ -4001,6 +4001,12 @@ class QueryExecutor {
return this.engine.delete(plan.table, plan); return this.engine.delete(plan.table, plan);
} }
async executeCreateTable(stmt) { async executeCreateTable(stmt) {
// IF NOT EXISTS: 表已存在时静默返回
if (stmt.ifNotExists) {
const exists = await this.engine.hasTable(stmt.name);
if (exists)
return;
}
const columns = {}; const columns = {};
for (const col of stmt.columns) for (const col of stmt.columns)
columns[col.name] = astColumnToColumnDef(col); columns[col.name] = astColumnToColumnDef(col);
@@ -4763,6 +4769,14 @@ class Parser {
parseCreateTable() { parseCreateTable() {
this.expect(TokenType.CREATE); this.expect(TokenType.CREATE);
this.expect(TokenType.TABLE); this.expect(TokenType.TABLE);
// IF NOT EXISTS(可选)
let ifNotExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.NOT);
this.expect(TokenType.EXISTS);
ifNotExists = true;
}
const tableName = this.expectIdentifier('table name'); const tableName = this.expectIdentifier('table name');
this.expect(TokenType.LPAREN); this.expect(TokenType.LPAREN);
const columns = []; const columns = [];
@@ -4772,7 +4786,7 @@ class Parser {
columns.push(this.parseColumnDef()); columns.push(this.parseColumnDef());
} while (this.curTokenIs(TokenType.COMMA)); } while (this.curTokenIs(TokenType.COMMA));
this.expect(TokenType.RPAREN); this.expect(TokenType.RPAREN);
return { type: 'CREATE_TABLE', name: tableName, columns }; return { type: 'CREATE_TABLE', name: tableName, columns, ifNotExists: ifNotExists || undefined };
} }
parseColumnDef() { parseColumnDef() {
const name = this.expectIdentifier('column name'); const name = this.expectIdentifier('column name');
+1 -1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -224,6 +224,8 @@ interface CreateTableStatement {
type: 'CREATE_TABLE'; type: 'CREATE_TABLE';
name: string; name: string;
columns: ASTColumnDef[]; columns: ASTColumnDef[];
/** IF NOT EXISTS — 表已存在时不报错 */
ifNotExists?: boolean;
} }
interface DropTableStatement { interface DropTableStatement {
type: 'DROP_TABLE'; type: 'DROP_TABLE';
+15 -1
View File
@@ -3997,6 +3997,12 @@ class QueryExecutor {
return this.engine.delete(plan.table, plan); return this.engine.delete(plan.table, plan);
} }
async executeCreateTable(stmt) { async executeCreateTable(stmt) {
// IF NOT EXISTS: 表已存在时静默返回
if (stmt.ifNotExists) {
const exists = await this.engine.hasTable(stmt.name);
if (exists)
return;
}
const columns = {}; const columns = {};
for (const col of stmt.columns) for (const col of stmt.columns)
columns[col.name] = astColumnToColumnDef(col); columns[col.name] = astColumnToColumnDef(col);
@@ -4759,6 +4765,14 @@ class Parser {
parseCreateTable() { parseCreateTable() {
this.expect(TokenType.CREATE); this.expect(TokenType.CREATE);
this.expect(TokenType.TABLE); this.expect(TokenType.TABLE);
// IF NOT EXISTS(可选)
let ifNotExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.NOT);
this.expect(TokenType.EXISTS);
ifNotExists = true;
}
const tableName = this.expectIdentifier('table name'); const tableName = this.expectIdentifier('table name');
this.expect(TokenType.LPAREN); this.expect(TokenType.LPAREN);
const columns = []; const columns = [];
@@ -4768,7 +4782,7 @@ class Parser {
columns.push(this.parseColumnDef()); columns.push(this.parseColumnDef());
} while (this.curTokenIs(TokenType.COMMA)); } while (this.curTokenIs(TokenType.COMMA));
this.expect(TokenType.RPAREN); this.expect(TokenType.RPAREN);
return { type: 'CREATE_TABLE', name: tableName, columns }; return { type: 'CREATE_TABLE', name: tableName, columns, ifNotExists: ifNotExists || undefined };
} }
parseColumnDef() { parseColumnDef() {
const name = this.expectIdentifier('column name'); const name = this.expectIdentifier('column name');
+1 -1
View File
File diff suppressed because one or more lines are too long
+15 -1
View File
@@ -4003,6 +4003,12 @@
return this.engine.delete(plan.table, plan); return this.engine.delete(plan.table, plan);
} }
async executeCreateTable(stmt) { async executeCreateTable(stmt) {
// IF NOT EXISTS: 表已存在时静默返回
if (stmt.ifNotExists) {
const exists = await this.engine.hasTable(stmt.name);
if (exists)
return;
}
const columns = {}; const columns = {};
for (const col of stmt.columns) for (const col of stmt.columns)
columns[col.name] = astColumnToColumnDef(col); columns[col.name] = astColumnToColumnDef(col);
@@ -4765,6 +4771,14 @@
parseCreateTable() { parseCreateTable() {
this.expect(TokenType.CREATE); this.expect(TokenType.CREATE);
this.expect(TokenType.TABLE); this.expect(TokenType.TABLE);
// IF NOT EXISTS(可选)
let ifNotExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.NOT);
this.expect(TokenType.EXISTS);
ifNotExists = true;
}
const tableName = this.expectIdentifier('table name'); const tableName = this.expectIdentifier('table name');
this.expect(TokenType.LPAREN); this.expect(TokenType.LPAREN);
const columns = []; const columns = [];
@@ -4774,7 +4788,7 @@
columns.push(this.parseColumnDef()); columns.push(this.parseColumnDef());
} while (this.curTokenIs(TokenType.COMMA)); } while (this.curTokenIs(TokenType.COMMA));
this.expect(TokenType.RPAREN); this.expect(TokenType.RPAREN);
return { type: 'CREATE_TABLE', name: tableName, columns }; return { type: 'CREATE_TABLE', name: tableName, columns, ifNotExists: ifNotExists || undefined };
} }
parseColumnDef() { parseColumnDef() {
const name = this.expectIdentifier('column name'); const name = this.expectIdentifier('column name');
+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
+2
View File
@@ -94,6 +94,8 @@ export interface CreateTableStatement {
type: 'CREATE_TABLE'; type: 'CREATE_TABLE';
name: string; name: string;
columns: ASTColumnDef[]; columns: ASTColumnDef[];
/** IF NOT EXISTS — 表已存在时不报错 */
ifNotExists?: boolean;
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
+5
View File
@@ -228,6 +228,11 @@ export class QueryExecutor {
} }
private async executeCreateTable(stmt: CreateTableStatement): Promise<void> { private async executeCreateTable(stmt: CreateTableStatement): Promise<void> {
// IF NOT EXISTS: 表已存在时静默返回
if (stmt.ifNotExists) {
const exists = await this.engine.hasTable(stmt.name);
if (exists) return;
}
const columns: Record<string, any> = {}; const columns: Record<string, any> = {};
for (const col of stmt.columns) columns[col.name] = astColumnToColumnDef(col); for (const col of stmt.columns) columns[col.name] = astColumnToColumnDef(col);
return this.engine.createTable(createSchema(stmt.name, columns)); return this.engine.createTable(createSchema(stmt.name, columns));
+11 -1
View File
@@ -327,6 +327,16 @@ export class Parser {
private parseCreateTable(): CreateTableStatement { private parseCreateTable(): CreateTableStatement {
this.expect(TokenType.CREATE); this.expect(TokenType.CREATE);
this.expect(TokenType.TABLE); this.expect(TokenType.TABLE);
// IF NOT EXISTS(可选)
let ifNotExists = false;
if (this.curTokenIs(TokenType.IF)) {
this.nextToken();
this.expect(TokenType.NOT);
this.expect(TokenType.EXISTS);
ifNotExists = true;
}
const tableName = this.expectIdentifier('table name'); const tableName = this.expectIdentifier('table name');
this.expect(TokenType.LPAREN); this.expect(TokenType.LPAREN);
@@ -338,7 +348,7 @@ export class Parser {
this.expect(TokenType.RPAREN); this.expect(TokenType.RPAREN);
return { type: 'CREATE_TABLE', name: tableName, columns }; return { type: 'CREATE_TABLE', name: tableName, columns, ifNotExists: ifNotExists || undefined };
} }
private parseColumnDef(): ASTColumnDef { private parseColumnDef(): ASTColumnDef {