Skip to content

Commit dd22d3b

Browse files
committed
"create view" and "drop view" are working; views are shown in database explorer
1 parent d23ef0b commit dd22d3b

File tree

9 files changed

+122
-19
lines changed

9 files changed

+122
-19
lines changed

css/icons.css

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client/compiler/lexer/Token.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export enum TokenType {
5656
keywordNull,
5757
keywordDatabase,
5858
keywordUnion,
59+
keywordView,
5960

6061
keywordOr,
6162
keywordAnd,
@@ -227,6 +228,7 @@ export var TokenTypeReadable: {[tt: number]: string} = {
227228
[TokenType.keywordNull]: "null",
228229
[TokenType.keywordDatabase]: "database",
229230
[TokenType.keywordUnion]: "union",
231+
[TokenType.keywordView]: "view",
230232

231233

232234
[TokenType.keywordAnd]: "and",
@@ -414,6 +416,7 @@ export var keywordList: {[keyword: string]:TokenType} = {
414416
"null": TokenType.keywordNull,
415417
"database": TokenType.keywordDatabase,
416418
"union": TokenType.keywordUnion,
419+
"view": TokenType.keywordView,
417420

418421
"or": TokenType.keywordOr,
419422
"and": TokenType.keywordAnd,

src/client/compiler/parser/AST.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Table } from "./SQLTable.js";
77
export type ASTNode =
88
StatementNode | TermNode | ColumnNode | CreateTableColumnNode
99

10-
export type StatementNode = SelectNode | UpdateNode | InsertNode | CreateTableNode |
10+
export type StatementNode = SelectNode | UpdateNode | InsertNode | CreateTableNode | CreateViewNode |
1111
DeleteNode | DropTableNode | AlterTableNode | CreateIndexNode | OmittedStatementNode;
1212

1313
export type TermNode = BinaryOpNode | UnaryOpNode | MethodcallNode |
@@ -79,6 +79,20 @@ export type CreateTableNode = {
7979
foreignKeyInfoList: ForeignKeyInfo[]
8080
}
8181

82+
export type CreateViewNode = {
83+
type: TokenType.keywordView,
84+
position: TextPosition,
85+
endPosition: TextPosition,
86+
identifier: string,
87+
symbolTable: SymbolTable,
88+
ifNotExists: boolean,
89+
90+
columnIdentifierList: string[],
91+
selectStatement: SelectNode
92+
}
93+
94+
95+
8296
export type ForeignKeyInfo = {
8397
column: string,
8498
referencesTable: string,

src/client/compiler/parser/Parser.ts

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { param, timers } from "jquery";
22
import { Error, QuickFix, ErrorLevel } from "../lexer/Lexer.js";
33
import { TextPosition, Token, TokenList, TokenType, TokenTypeReadable } from "../lexer/Token.js";
4-
import { ASTNode, BracketsNode, SelectNode, TermNode, TableOrSubqueryNode, TableNode, SubqueryNode, GroupByNode, OrderByNode, LimitNode, IdentifierNode, DotNode, ListNode, ColumnNode, InsertNode, ConstantNode, UnaryOpNode, CreateTableNode, CreateTableColumnNode, ForeignKeyInfo, UpdateNode, DeleteNode, DropTableNode, AlterTableNode, AlterTableKind, OmittedStatementNode } from "./AST.js";
4+
import { ASTNode, BracketsNode, SelectNode, TermNode, TableOrSubqueryNode, TableNode, SubqueryNode, GroupByNode, OrderByNode, LimitNode, IdentifierNode, DotNode, ListNode, ColumnNode, InsertNode, ConstantNode, UnaryOpNode, CreateTableNode, CreateTableColumnNode, ForeignKeyInfo, UpdateNode, DeleteNode, DropTableNode, AlterTableNode, AlterTableKind, OmittedStatementNode, CreateViewNode } from "./AST.js";
55
import { Module } from "./Module.js";
66
import { Column } from "./SQLTable.js";
77
import { SQLBaseType } from "./SQLTypes.js";
@@ -435,13 +435,13 @@ export class Parser {
435435
case TokenType.keywordInsert:
436436
return this.parseInsert();
437437
case TokenType.keywordCreate:
438-
return this.parseCreateTableOrDatabase();
438+
return this.parseCreateTableOrDatabaseOrView();
439439
case TokenType.keywordUpdate:
440440
return this.parseUpdate();
441441
case TokenType.keywordDelete:
442442
return this.parseDelete();
443443
case TokenType.keywordDrop:
444-
return this.parseDropTable();
444+
return this.parseDropTableOrView();
445445
case TokenType.keywordAlter:
446446
return this.parseAlterTable();
447447
case TokenType.keywordCommit:
@@ -859,10 +859,10 @@ export class Parser {
859859
node.columnDef = this.parseColumnDefinition(false);
860860
}
861861

862-
parseDropTable(): DropTableNode {
862+
parseDropTableOrView(): DropTableNode {
863863

864864
let startPosition = this.getCurrentPosition();
865-
this.nextToken(); // skip "Delete"
865+
this.nextToken(); // skip "Drop"
866866

867867
let node: DropTableNode = {
868868
type: TokenType.keywordDrop,
@@ -874,8 +874,8 @@ export class Parser {
874874
ifExists: false
875875
}
876876

877-
if (!this.expect(TokenType.keywordTable, true)) {
878-
this.addCompletionHintHere(false, false, ["table"], 1);
877+
if (!this.expect([TokenType.keywordTable, TokenType.keywordView], true)) {
878+
this.addCompletionHintHere(false, false, ["table", "view"], 1);
879879
}
880880

881881
if (this.comesToken(TokenType.keywordIf, true)) {
@@ -1015,12 +1015,14 @@ export class Parser {
10151015

10161016
}
10171017

1018-
parseCreateTableOrDatabase(): CreateTableNode | OmittedStatementNode {
1018+
parseCreateTableOrDatabaseOrView(): CreateTableNode | OmittedStatementNode | CreateViewNode {
10191019
switch (this.ct[1].tt) {
10201020
case TokenType.keywordDatabase:
10211021
return this.parseCreateDatabase();
10221022
case TokenType.keywordTable:
10231023
return this.parseCreateTable();
1024+
case TokenType.keywordView:
1025+
return this.parseCreateView();
10241026
default:
10251027
this.nextToken();
10261028
this.pushError("Nach 'create' wird 'table' erwartet.");
@@ -1047,6 +1049,65 @@ export class Parser {
10471049
return node;
10481050
}
10491051

1052+
parseCreateView(): CreateViewNode {
1053+
let startPosition = this.getCurrentPosition();
1054+
this.nextToken(); // skip "create"
1055+
this.nextToken(); // skip "view"
1056+
1057+
let ifNotExists: boolean = false;
1058+
if (this.comesToken(TokenType.keywordIf)) {
1059+
this.nextToken();
1060+
this.expect(TokenType.keywordNot, true);
1061+
this.expect(TokenType.keywordExists, true);
1062+
ifNotExists = true;
1063+
}
1064+
1065+
let identifier = "";
1066+
if (!this.expect(TokenType.identifier, false)){
1067+
return null;
1068+
}
1069+
1070+
identifier = <string>this.cct.value;
1071+
this.nextToken();
1072+
1073+
let columnIdentifiers: string[] = [];
1074+
1075+
if(this.comesToken(TokenType.leftBracket, true)){
1076+
1077+
do {
1078+
if(this.expect(TokenType.identifier, false)){
1079+
columnIdentifiers.push(<string>this.cct.value)
1080+
}
1081+
this.nextToken();
1082+
} while(this.comesToken(TokenType.comma, true))
1083+
1084+
this.expect(TokenType.rightBracket, true);
1085+
}
1086+
1087+
if (!this.expect(TokenType.keywordAs, true)){
1088+
return null;
1089+
}
1090+
1091+
if(!this.expect(TokenType.keywordSelect, false)){
1092+
return null;
1093+
}
1094+
1095+
let selectStatement = this.parseSelect();
1096+
1097+
let node: CreateViewNode = {
1098+
type: TokenType.keywordView,
1099+
identifier: identifier,
1100+
position: startPosition,
1101+
endPosition: this.getCurrentPosition(),
1102+
symbolTable: null,
1103+
ifNotExists: ifNotExists,
1104+
columnIdentifierList: columnIdentifiers,
1105+
selectStatement: selectStatement
1106+
}
1107+
1108+
return node;
1109+
}
1110+
10501111
parseCreateTable(): CreateTableNode {
10511112

10521113
let startPosition = this.getCurrentPosition();

src/client/compiler/parser/SQLTable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class Table {
4545
public columns: Column[] = [];
4646

4747
public size: number;
48+
type: ("table"|"view");
4849

4950
constructor(public identifier: string){
5051

@@ -54,6 +55,7 @@ export class Table {
5455
let table: Table = new Table(ts.name);
5556
table.columns = ts.columns.map( column => Column.fromColumnStructure(column, table));
5657
table.size = ts.size;
58+
table.type = ts.type;
5759
return table;
5860
}
5961

src/client/compiler/parser/SymbolResolver.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DatabaseTool } from "../../tools/DatabaseTools.js";
22
import { TextPosition, TokenType, TokenTypeReadable } from "../lexer/Token.js";
33
import { CompletionHint, Module } from "./Module.js";
44
import { Symbol, SymbolTable } from "./SymbolTable.js";
5-
import { AlterTableNode, ASTNode, BinaryOpNode, CreateTableNode, DeleteNode, DotNode, DropTableNode, IdentifierNode, InsertNode, MethodcallNode, SelectNode, TableOrSubqueryNode, TermNode, UpdateNode } from "./Ast.js";
5+
import { AlterTableNode, ASTNode, BinaryOpNode, CreateTableNode, CreateViewNode, DeleteNode, DotNode, DropTableNode, IdentifierNode, InsertNode, MethodcallNode, SelectNode, TableOrSubqueryNode, TermNode, UpdateNode } from "./Ast.js";
66
import { Error, ErrorLevel, QuickFix } from "../lexer/Lexer.js";
77
import { Column, Table } from "./SQLTable.js";
88
import { SQLBaseType, SQLType } from "./SQLTypes.js";
@@ -68,6 +68,9 @@ export class SymbolResolver {
6868
this.resolveAlterTable(astNode)
6969
this.symbolTableStack.pop();
7070
break;
71+
case TokenType.keywordView:
72+
this.resolveCreateView(astNode);
73+
this.symbolTableStack.pop();
7174

7275
default:
7376
break;
@@ -319,7 +322,8 @@ export class SymbolResolver {
319322
let thisTable: Table = {
320323
identifier: createTableNode.identifier,
321324
columns: null,
322-
size: 0
325+
size: 0,
326+
type: "table"
323327
}
324328

325329
thisTable.columns = createTableNode.columnList.map(c => {
@@ -664,6 +668,13 @@ export class SymbolResolver {
664668
return SQLBaseType.getBaseType("boolean");
665669
}
666670

671+
resolveCreateView(astNode: CreateViewNode){
672+
let symbolTable = this.pushNewSymbolTable(astNode.position, astNode.endPosition);
673+
this.resolveSelect(astNode.selectStatement);
674+
symbolTable.childSymbolTables.push(this.symbolTableStack.pop());
675+
}
676+
677+
667678
resolveInsert(astNode: InsertNode) {
668679

669680
let table: Table = null;

src/client/main/gui/DatabaseExplorer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ export class DatabaseExplorer {
5656
<div class="jo_tableheader">
5757
<div class="${isCollapsed ? 'img_tree-collapsed-dark' : 'img_tree-expanded-dark'} jo_treeswitch jo_button jo_active"></div>
5858
<div class="jo_tableheaderlink">
59-
<div class="img_table"></div>
60-
<div>${table.identifier}</div></div><div class="jo_tablesize">(${table.size}&nbsp;Datensätze)</div>
59+
<div class="${table.type == "table"?"img_table" : "img_view"}"></div>
60+
<div>${table.identifier}</div></div><div class="jo_tablesize">(${table.type == "view"?"View, " : ""}${table.size}&nbsp;Datensätze)</div>
6161
</div>
6262
</div>
6363
</div>`);

src/client/main/gui/ResultsetPresenter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ export class ResultsetPresenter {
377377
}
378378

379379
isDDLStatement(statement: SQLStatement): boolean {
380-
return statement.ast != null && [TokenType.keywordCreate, TokenType.keywordDrop, TokenType.keywordAlter].indexOf(statement.ast.type) >= 0;
380+
return statement.ast != null && [TokenType.keywordCreate, TokenType.keywordDrop, TokenType.keywordAlter, TokenType.keywordView].indexOf(statement.ast.type) >= 0;
381381
}
382382

383383
isWriteStatement(statement: SQLStatement): boolean {

src/client/tools/DatabaseTools.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export type TableStructure = {
4242
size: number;
4343
columns: ColumnStructure[];
4444
completeSQL: string;
45+
type: ("table"|"view");
4546
}
4647

4748
export type DatabaseStructure = {
@@ -239,19 +240,22 @@ export class DatabaseTool {
239240
/*
240241
@see https://stackoverflow.com/questions/6460671/sqlite-schema-information-metadata
241242
*/
242-
let sql = `SELECT name, sql FROM sqlite_master WHERE type='table';`
243+
let sql = `SELECT name, sql, type FROM sqlite_master WHERE type='table' or type='view';`
243244
let that = this;
244245

245246
this.executeQuery(sql, (result) => {
246247
let sql1 = "";
247-
result[0]?.values?.forEach(value => sql1 += `PRAGMA table_info(${value[0]});\nPRAGMA foreign_key_list(${value[0]});\nselect count(*) from ${value[0]};\n\n`)
248+
let values = result[0]?.values;
249+
let types: ("table"|"view")[] = values?.map(value => value[2]);
250+
251+
values?.forEach(value => sql1 += `PRAGMA table_info(${value[0]});\nPRAGMA foreign_key_list(${value[0]});\nselect count(*) from ${value[0]};\n\n`)
248252

249253
if (sql1 != "") {
250254
this.executeQuery(sql1, (result1) => {
251255
// console.log("DB structure: ");
252256
// console.log(result1);
253257

254-
that.databaseStructure = that.parseDatabaseStructure(result, result1)
258+
that.databaseStructure = that.parseDatabaseStructure(result, result1, types)
255259

256260
callback(that.databaseStructure);
257261

@@ -266,7 +270,7 @@ export class DatabaseTool {
266270

267271
}
268272

269-
parseDatabaseStructure(tables: QueryResult[], columns: QueryResult[]): DatabaseStructure {
273+
parseDatabaseStructure(tables: QueryResult[], columns: QueryResult[], types: ("table"|"view")[]): DatabaseStructure {
270274
this.databaseStructure = {
271275
tables: []
272276
};
@@ -282,7 +286,8 @@ export class DatabaseTool {
282286
name: tableName,
283287
size: 0,
284288
completeSQL: tableSQL,
285-
columns: []
289+
columns: [],
290+
type: types[i]
286291
}
287292

288293
tableNameToStructureMap.set(tableName, tableStructure);

0 commit comments

Comments
 (0)