0.0.1-alpha.6:

1. 修改 `package.json` 中的项目名称和版本号。
2. 更新 `node_modules` 中的依赖包版本。
3. 修改 `src/index.ts` 中的 SQL 语句和 `caretColumn` 的计算方式。
4. 修改 `src/parse/default-rules.ts` 中的规则和导入语句。
5. 修改 `src/parse/visitor.ts` 中的访问者和规则。
6. 修改 `src/preprocess/default-preprocessor.ts` 中的预处理逻辑。
7. 修改 `src/types.ts` 中的导入语句。
This commit is contained in:
Kijin-Seija 2024-04-15 17:48:24 +08:00
parent d7f6568157
commit 645a73d6af
9 changed files with 109 additions and 122 deletions

View File

@ -12,7 +12,7 @@
<script type="module">
const { DtSqlParserSemAnalysePlugin } = await import(/* @vite-ignore */import.meta.env.VITE_ENTRY_PATH)
const myPlugin = new DtSqlParserSemAnalysePlugin()
const sql = 'SELECT a.b| FROM t'
const sql = 'SELECT a.| AS c'
const caretColumn = sql.indexOf('|') + 1
const result = myPlugin.parse(sql.replace('|', ''), { lineNumber: 1, columnNumber: caretColumn })
console.log(result)

45
package-lock.json generated
View File

@ -1,12 +1,14 @@
{
"name": "dt-sql-parser-analyse-demo",
"name": "dt-sql-parser-semantic-analyse-plugin",
"version": "0.0.1-alpha.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dt-sql-parser-analyse-demo",
"name": "dt-sql-parser-semantic-analyse-plugin",
"version": "0.0.1-alpha.6",
"dependencies": {
"dt-sql-parser": "^4.0.0-beta.4.11"
"dt-sql-parser": "^4.0.0-beta.4.12"
},
"devDependencies": {
"@eslint/eslintrc": "^3.0.2",
@ -1099,17 +1101,29 @@
}
},
"node_modules/antlr4-c3": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/antlr4-c3/-/antlr4-c3-3.1.1.tgz",
"integrity": "sha512-S7DixV12kxWexTkQYGvooCgHYU5AjF74oYio+ZNgm0XN3EzxDY3J6Si9GprQ4KksvgWwK//EgZnL/26WB+bOpw==",
"version": "3.3.7",
"resolved": "http://npm.oushu.com:14837/antlr4-c3/-/antlr4-c3-3.3.7.tgz",
"integrity": "sha512-F3ndE38wwA6z6AjUbL3heSdEGl4TxulGDPf9xB0/IY4dbRHWBh6XNaqFwur8vHKQk9FS5yNABHeg2wqlqIYO0w==",
"dependencies": {
"antlr4ts": "0.5.0-alpha.4"
"antlr4ng": "2.0.11"
}
},
"node_modules/antlr4ts": {
"version": "0.5.0-alpha.4",
"resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
"integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ=="
"node_modules/antlr4ng": {
"version": "2.0.11",
"resolved": "http://npm.oushu.com:14837/antlr4ng/-/antlr4ng-2.0.11.tgz",
"integrity": "sha512-9jM91VVtHSqHkAHQsXHaoaiewFETMvUTI1/tXvwTiFw4f7zke3IGlwEyoKN9NS0FqIwDKFvUNW2e1cKPniTkVQ==",
"peerDependencies": {
"antlr4ng-cli": "1.0.7"
}
},
"node_modules/antlr4ng-cli": {
"version": "1.0.7",
"resolved": "http://npm.oushu.com:14837/antlr4ng-cli/-/antlr4ng-cli-1.0.7.tgz",
"integrity": "sha512-qN2FsDBmLvsQcA5CWTrPz8I8gNXeS1fgXBBhI78VyxBSBV/EJgqy8ks6IDTC9jyugpl40csCQ4sL5K4i2YZ/2w==",
"peer": true,
"bin": {
"antlr4ng": "index.js"
}
},
"node_modules/argparse": {
"version": "2.0.1",
@ -1890,11 +1904,12 @@
}
},
"node_modules/dt-sql-parser": {
"version": "4.0.0-beta.4.11",
"integrity": "sha512-bgMJAMImikNwE0OPZApI+R+PjpI8xoJAksQIUJdtS4+piZII1LHzbmlp8T7iRJ4Fo56EwY5ILmv9VcZ/PwKV1A==",
"version": "4.0.0-beta.4.12",
"resolved": "http://npm.oushu.com:14837/dt-sql-parser/-/dt-sql-parser-4.0.0-beta.4.12.tgz",
"integrity": "sha512-kfLRecn+dfdZjrKt3Ovm52ryoh9UGF+dCjNzV2pk8XI5kgF7lgQJhPZdRi+2yn1AU/BWQ1/0E6LnvFYbZ7Wr9Q==",
"dependencies": {
"antlr4-c3": "3.1.1",
"antlr4ts": "0.5.0-alpha.4"
"antlr4-c3": "3.3.7",
"antlr4ng": "2.0.11"
}
},
"node_modules/elliptic": {

View File

@ -37,6 +37,6 @@
"vite-plugin-node-polyfills": "^0.21.0"
},
"dependencies": {
"dt-sql-parser": "^4.0.0-beta.4.11"
"dt-sql-parser": "^4.0.0-beta.4.12"
}
}

View File

@ -1,10 +1,10 @@
import { defaultEntities, defaultRules, defaultStmts } from './parse/default-rules'
import { defaultAlias, defaultEntities, defaultRules, defaultStmts } from './parse/default-rules'
import { parse } from './parse'
import { preprocess } from './preprocess'
import { type PluginSettings, type InsertCaretPlaceholderConfig } from './types'
import { defaultPreprocessorList } from './preprocess/default-preprocessor'
import { insertCaret } from './caret'
import { PostgresSQL } from 'dt-sql-parser'
import { PostgreSQL } from 'dt-sql-parser'
export class DtSqlParserSemAnalysePlugin {
private readonly settings: PluginSettings = {}
@ -18,10 +18,11 @@ export class DtSqlParserSemAnalysePlugin {
const sqlAfterPreprocess = preprocess(sqlAfterInsertCaret, this.settings.preprocessor || defaultPreprocessorList)
const sqlParseResult = parse(
sqlAfterPreprocess,
this.settings.parse?.parser || new PostgresSQL(),
this.settings.parse?.parser || new PostgreSQL(),
this.settings.parse?.stmts || defaultStmts,
this.settings.parse?.entities || defaultEntities,
this.settings.parse?.rules || defaultRules
this.settings.parse?.rules || defaultRules,
this.settings.parse?.alias || defaultAlias
)
return sqlParseResult
}

View File

@ -1,89 +1,39 @@
import { PostgreSQLParser } from 'dt-sql-parser/dist/lib/pgsql/PostgreSQLParser'
import { PostgreSqlParser } from 'dt-sql-parser/dist/lib/postgresql/PostgreSqlParser'
export const defaultAlias = {
selectstmt: 'selectStatement',
target_el: 'target_label'
}
export const defaultStmts = [
'stmt',
// select statement
'selectstmt'
'simple_select',
]
export const defaultEntities = [
// column_name directly. ex: select column1
'column_name',
// column_name indirectly. ex: select schema.column1
'columnref',
'table_name',
'view_name',
'function_name',
'schema_name',
'target_el',
'colid',
'attr_name',
'collabel',
'func_arg_expr'
'collabel'
]
export const defaultRules: Record<string, number[]> = {
// 通用的简单column规则不带.运算符的column)
common_column_simple: [
PostgreSQLParser.RULE_stmt,
PostgreSQLParser.RULE_column_name
select_target: [
PostgreSqlParser.RULE_simple_select,
PostgreSqlParser.RULE_target_el,
],
// 通用的复合column规则带.运算符的column)
common_column_ref: [
PostgreSQLParser.RULE_stmt,
PostgreSQLParser.RULE_columnref
select_target_colid: [
PostgreSqlParser.RULE_target_el,
PostgreSqlParser.RULE_function_name,
PostgreSqlParser.RULE_colid
],
select_target_column_simple: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_target_list,
PostgreSQLParser.RULE_column_name
select_target_attr: [
PostgreSqlParser.RULE_target_el,
PostgreSqlParser.RULE_function_name,
PostgreSqlParser.RULE_attr_name
],
select_target_column_ref: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_target_list,
PostgreSQLParser.RULE_columnref
],
select_target_function: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_target_list,
PostgreSQLParser.RULE_function_name
],
select_column_alias: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_target_list,
PostgreSQLParser.RULE_collabel
],
select_from_table: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_from_clause,
PostgreSQLParser.RULE_table_name
],
select_from_view: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_from_clause,
PostgreSQLParser.RULE_view_name
],
select_from_function: [
PostgreSQLParser.RULE_selectstmt,
PostgreSQLParser.RULE_select_clause,
PostgreSQLParser.RULE_from_clause,
PostgreSQLParser.RULE_function_name
],
column_ref_colid: [
PostgreSQLParser.RULE_columnref,
PostgreSQLParser.RULE_colid
],
column_ref_attr: [
PostgreSQLParser.RULE_columnref,
PostgreSQLParser.RULE_attr_name
],
function_arg_expr: [
PostgreSQLParser.RULE_function_name,
PostgreSQLParser.RULE_func_arg_expr
select_target_alias: [
PostgreSqlParser.RULE_target_el,
-PostgreSqlParser.RULE_attr_name,
PostgreSqlParser.RULE_collabel
]
}

View File

@ -1,18 +1,20 @@
import { PostgresSQL } from 'dt-sql-parser'
import { PostgreSQL } from 'dt-sql-parser'
import { SQLVisitor } from './visitor'
import { type SQLParseResult } from '../types'
import type BasicParser from 'dt-sql-parser/dist/parser/common/basicParser'
import { type BasicSQL } from 'dt-sql-parser/dist/parser/common/basicSQL'
export function parse (
sql: string,
parser: BasicParser = new PostgresSQL(),
parser: BasicSQL = new PostgreSQL(),
stmts: string[] = [],
entities: string[] = [],
rules: Record<string, number[]> = {}
rules: Record<string, number[]> = {},
alias: Record<string, string> = {},
): SQLParseResult {
const tree = parser.parse(sql)
console.log('tree', tree)
const visitor = new SQLVisitor()
visitor.visitorAlias = alias
stmts.forEach(stmt => { visitor.addStmt(stmt) })
entities.forEach(entity => { visitor.addEntity(entity) })
Object.keys(rules).forEach(name => { visitor.addRules(name, rules[name]) })

View File

@ -1,21 +1,24 @@
import { type ParserRuleContext } from 'antlr4ts'
import { AbstractParseTreeVisitor, type PostgreSQLParserVisitor } from 'dt-sql-parser'
import { type ProgramContext, PostgreSQLParser } from 'dt-sql-parser/dist/lib/pgsql/PostgreSQLParser'
import { AbstractParseTreeVisitor, type RuleContext } from 'antlr4ng'
import { type PostgreSqlParserVisitor } from 'dt-sql-parser'
import { type ProgramContext, PostgreSqlParser } from 'dt-sql-parser/dist/lib/postgresql/PostgreSqlParser'
import { type Entity, type SQLParseResult, type Stmt } from '../types'
import { caretPlaceholder } from '../caret'
function withCaret (ctx: ParserRuleContext) {
return ctx.text.includes(caretPlaceholder)
function toVisitorAlias (node: string, alias: Record<string, string>) {
const result = alias[node] || node
return `visit${result.slice(0, 1).toUpperCase()}${result.slice(1)}`
}
export class SQLVisitor extends AbstractParseTreeVisitor<void> implements PostgreSQLParserVisitor<void> {
function withCaret (ctx: RuleContext) {
return ctx.getText().includes(caretPlaceholder)
}
export class SQLVisitor extends AbstractParseTreeVisitor<void> implements PostgreSqlParserVisitor<void> {
private result: SQLParseResult = {
stmtList: [],
nerestCaretEntityList: []
}
protected defaultResult = () => ({ list: [], nerestCaret: null })
public clear () {
this.result = { stmtList: [], nerestCaretEntityList: [] }
}
@ -24,6 +27,8 @@ export class SQLVisitor extends AbstractParseTreeVisitor<void> implements Postgr
return this.result
}
public visitorAlias = {}
private readonly stmtStack: Stmt[] = []
private readonly entityStack: Entity[] = []
@ -50,12 +55,12 @@ export class SQLVisitor extends AbstractParseTreeVisitor<void> implements Postgr
}
public addEntity (name: string) {
this.entityRules.set((PostgreSQLParser as any)[`RULE_${name}`], [])
this.entityRules.set((PostgreSqlParser as any)[`RULE_${name}`], [])
let isHitRule = false
const visitorName = `visit${name.slice(0, 1).toUpperCase()}${name.slice(1)}`
const visitorName = toVisitorAlias(name, this.visitorAlias)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const rules = this.entityRules.get((PostgreSQLParser as any)[`RULE_${name}`])!;
(this as any)[visitorName] = (ctx: ParserRuleContext) => {
const rules = this.entityRules.get((PostgreSqlParser as any)[`RULE_${name}`])!;
(this as any)[visitorName] = (ctx: RuleContext) => {
const chain = this.getNodeChain(ctx)
for (const rule of rules) {
const ruleChain = this.rules.get(rule)
@ -66,7 +71,7 @@ export class SQLVisitor extends AbstractParseTreeVisitor<void> implements Postgr
const beginEntity = this.entityStack.find(entity => entity.type === ruleChainBegin)
const result: Entity = {
rule,
text: ctx.text,
text: ctx.getText(),
type: ctx.ruleIndex,
caret: withCaret(ctx),
belongsToStmt: beginStmt || null,
@ -91,11 +96,11 @@ export class SQLVisitor extends AbstractParseTreeVisitor<void> implements Postgr
}
public addStmt (name: string) {
this.stmtRules.set((PostgreSQLParser as any)[`RULE_${name}`], [])
const visitorName = `visit${name.slice(0, 1).toUpperCase()}${name.slice(1)}`;
(this as any)[visitorName] = (ctx: ParserRuleContext) => {
this.stmtRules.set((PostgreSqlParser as any)[`RULE_${name}`], [])
const visitorName = toVisitorAlias(name, this.visitorAlias);
(this as any)[visitorName] = (ctx: RuleContext) => {
this.stmtStack.push({
text: ctx.text,
text: ctx.getText(),
type: ctx.ruleIndex,
caret: withCaret(ctx),
relatedEntities: {}
@ -106,8 +111,8 @@ export class SQLVisitor extends AbstractParseTreeVisitor<void> implements Postgr
}
}
private getNodeChain (ctx: ParserRuleContext) {
let _ctx: ParserRuleContext | undefined = ctx
private getNodeChain (ctx: RuleContext) {
let _ctx: RuleContext | null = ctx
const result = []
while (_ctx) {
result.unshift(_ctx.ruleIndex)
@ -118,10 +123,13 @@ export class SQLVisitor extends AbstractParseTreeVisitor<void> implements Postgr
private matchRules (chain: number[], ruleChain: number[] | undefined) {
// 只要ruleChain里面每个元素都出现在chain里面且顺序一致则返回true。否则返回false
// 当元素value为负数时表示NOT即不出现id为-value的规则。
if (!ruleChain) return false
let index = 0
for (let i = 0; i < ruleChain.length; i++) {
if (chain.indexOf(ruleChain[i]) < index) return false
if (ruleChain[i] < 0) {
if (chain.indexOf(-ruleChain[i]) >= index) return false
} else if (chain.indexOf(ruleChain[i]) < index) return false
else index = chain.indexOf(ruleChain[i])
}
return true

View File

@ -1,5 +1,14 @@
import { type Preprocessor } from '../types'
export const addSuffixForParseSelectProcessor: Preprocessor = (sql: string) => {
const suffix = ' '
const maxWords = 3
if (/select( )+/.test(sql.toLowerCase()) && sql.split(' ').filter(item => item).length < maxWords) {
return sql + '' + suffix
}
return sql
}
export const addSuffixForParseAlterFunctionProcessor: Preprocessor = (sql: string) => {
const suffix = 'RESET ALL'
const maxWords = 4
@ -20,5 +29,6 @@ export const addSuffixForParseAlterTableProcessor: Preprocessor = (sql: string)
export const defaultPreprocessorList = [
addSuffixForParseAlterFunctionProcessor,
addSuffixForParseAlterTableProcessor
addSuffixForParseAlterTableProcessor,
addSuffixForParseSelectProcessor
]

View File

@ -1,4 +1,4 @@
import type BasicParser from 'dt-sql-parser/dist/parser/common/basicParser'
import { type BasicSQL } from 'dt-sql-parser/dist/parser/common/basicSQL'
export interface InsertCaretPlaceholderConfig {
lineNumber: number
@ -24,10 +24,11 @@ export interface PluginSettings {
*
*/
parse?: {
parser?: BasicParser
parser?: BasicSQL
stmts?: string[]
entities?: string[]
rules?: Record<string, number[]>
alias?: Record<string, string>
}
}