From d981b3801d87c73ce055cd554a5177febe271f05 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 11:33:26 +0900 Subject: [PATCH 01/16] install packages --- frontend/packages/db-structure/package.json | 8 ++++++-- frontend/pnpm-lock.yaml | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/frontend/packages/db-structure/package.json b/frontend/packages/db-structure/package.json index e006124cf..2f204cf34 100644 --- a/frontend/packages/db-structure/package.json +++ b/frontend/packages/db-structure/package.json @@ -7,14 +7,18 @@ "lint:biome": "biome check .", "lint:tsc": "tsc --noEmit", "fmt": "conc -c auto pnpm:fmt:*", - "fmt:biome": "biome check --write --unsafe ." + "fmt:biome": "biome check --write --unsafe .", + "test": "vitest" }, "dependencies": { + "lodash": "^4.17.21", + "pluralize": "^8.0.0", "valibot": "^1.0.0-beta.5" }, "devDependencies": { "@biomejs/biome": "1.9.3", "@packages/configs": "workspace:*", - "typescript": "^5" + "typescript": "^5.6.2", + "vitest": "^2.1.4" } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 55f4e3876..b3e224e6f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -84,6 +84,12 @@ importers: packages/db-structure: dependencies: + lodash: + specifier: ^4.17.21 + version: 4.17.21 + pluralize: + specifier: ^8.0.0 + version: 8.0.0 valibot: specifier: ^1.0.0-beta.5 version: 1.0.0-beta.5(typescript@5.6.2) @@ -95,8 +101,11 @@ importers: specifier: workspace:* version: link:../configs typescript: - specifier: ^5 + specifier: ^5.6.2 version: 5.6.2 + vitest: + specifier: ^2.1.4 + version: 2.1.4(@types/node@22.9.0) packages/erd-core: dependencies: @@ -2575,6 +2584,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -5903,6 +5916,8 @@ snapshots: picomatch@2.3.1: {} + pluralize@8.0.0: {} + possible-typed-array-names@1.0.0: {} postcss-modules-extract-imports@3.1.0(postcss@8.4.47): From ab138d3de9b31d14ff7f3576dd19d0c8e4dcbb95 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 12:12:18 +0900 Subject: [PATCH 02/16] generate parser --- .../src/parser/schemarb/parser.js | 2317 +++++++++++++++++ .../src/parser/schemarb/parser.pegjs | 380 +++ 2 files changed, 2697 insertions(+) create mode 100644 frontend/packages/db-structure/src/parser/schemarb/parser.js create mode 100644 frontend/packages/db-structure/src/parser/schemarb/parser.pegjs diff --git a/frontend/packages/db-structure/src/parser/schemarb/parser.js b/frontend/packages/db-structure/src/parser/schemarb/parser.js new file mode 100644 index 000000000..5df1a509e --- /dev/null +++ b/frontend/packages/db-structure/src/parser/schemarb/parser.js @@ -0,0 +1,2317 @@ +// @generated by Peggy 4.1.1. +// +// https://peggyjs.org/ + +"use strict"; + + +function peg$subclass(child, parent) { + function C() { this.constructor = child; } + C.prototype = parent.prototype; + child.prototype = new C(); +} + +function peg$SyntaxError(message, expected, found, location) { + var self = Error.call(this, message); + // istanbul ignore next Check is a necessary evil to support older environments + if (Object.setPrototypeOf) { + Object.setPrototypeOf(self, peg$SyntaxError.prototype); + } + self.expected = expected; + self.found = found; + self.location = location; + self.name = "SyntaxError"; + return self; +} + +peg$subclass(peg$SyntaxError, Error); + +function peg$padEnd(str, targetLength, padString) { + padString = padString || " "; + if (str.length > targetLength) { return str; } + targetLength -= str.length; + padString += padString.repeat(targetLength); + return str + padString.slice(0, targetLength); +} + +peg$SyntaxError.prototype.format = function(sources) { + var str = "Error: " + this.message; + if (this.location) { + var src = null; + var k; + for (k = 0; k < sources.length; k++) { + if (sources[k].source === this.location.source) { + src = sources[k].text.split(/\r\n|\n|\r/g); + break; + } + } + var s = this.location.start; + var offset_s = (this.location.source && (typeof this.location.source.offset === "function")) + ? this.location.source.offset(s) + : s; + var loc = this.location.source + ":" + offset_s.line + ":" + offset_s.column; + if (src) { + var e = this.location.end; + var filler = peg$padEnd("", offset_s.line.toString().length, ' '); + var line = src[s.line - 1]; + var last = s.line === e.line ? e.column : line.length + 1; + var hatLen = (last - s.column) || 1; + str += "\n --> " + loc + "\n" + + filler + " |\n" + + offset_s.line + " | " + line + "\n" + + filler + " | " + peg$padEnd("", s.column - 1, ' ') + + peg$padEnd("", hatLen, "^"); + } else { + str += "\n at " + loc; + } + } + return str; +}; + +peg$SyntaxError.buildMessage = function(expected, found) { + var DESCRIBE_EXPECTATION_FNS = { + literal: function(expectation) { + return "\"" + literalEscape(expectation.text) + "\""; + }, + + class: function(expectation) { + var escapedParts = expectation.parts.map(function(part) { + return Array.isArray(part) + ? classEscape(part[0]) + "-" + classEscape(part[1]) + : classEscape(part); + }); + + return "[" + (expectation.inverted ? "^" : "") + escapedParts.join("") + "]"; + }, + + any: function() { + return "any character"; + }, + + end: function() { + return "end of input"; + }, + + other: function(expectation) { + return expectation.description; + } + }; + + function hex(ch) { + return ch.charCodeAt(0).toString(16).toUpperCase(); + } + + function literalEscape(s) { + return s + .replace(/\\/g, "\\\\") + .replace(/"/g, "\\\"") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) + .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); + } + + function classEscape(s) { + return s + .replace(/\\/g, "\\\\") + .replace(/\]/g, "\\]") + .replace(/\^/g, "\\^") + .replace(/-/g, "\\-") + .replace(/\0/g, "\\0") + .replace(/\t/g, "\\t") + .replace(/\n/g, "\\n") + .replace(/\r/g, "\\r") + .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); }) + .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); }); + } + + function describeExpectation(expectation) { + return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); + } + + function describeExpected(expected) { + var descriptions = expected.map(describeExpectation); + var i, j; + + descriptions.sort(); + + if (descriptions.length > 0) { + for (i = 1, j = 1; i < descriptions.length; i++) { + if (descriptions[i - 1] !== descriptions[i]) { + descriptions[j] = descriptions[i]; + j++; + } + } + descriptions.length = j; + } + + switch (descriptions.length) { + case 1: + return descriptions[0]; + + case 2: + return descriptions[0] + " or " + descriptions[1]; + + default: + return descriptions.slice(0, -1).join(", ") + + ", or " + + descriptions[descriptions.length - 1]; + } + } + + function describeFound(found) { + return found ? "\"" + literalEscape(found) + "\"" : "end of input"; + } + + return "Expected " + describeExpected(expected) + " but " + describeFound(found) + " found."; +}; + +function peg$parse(input, options) { + options = options !== undefined ? options : {}; + + var peg$FAILED = {}; + var peg$source = options.grammarSource; + + var peg$startRuleFunctions = { schema: peg$parseschema }; + var peg$startRuleFunction = peg$parseschema; + + var peg$c0 = ","; + var peg$c1 = ":"; + var peg$c2 = "on_delete"; + var peg$c3 = "on_update"; + var peg$c4 = "add_index"; + var peg$c5 = "ActiveRecord::Schema.define"; + var peg$c6 = "create_table"; + var peg$c7 = ".index"; + var peg$c8 = ".references"; + var peg$c9 = "add_foreign_key"; + var peg$c10 = "column"; + var peg$c11 = "primary_key"; + var peg$c12 = "version"; + var peg$c13 = "do"; + var peg$c14 = "end"; + var peg$c15 = "=>"; + var peg$c16 = "->"; + var peg$c17 = "."; + var peg$c18 = "#"; + var peg$c19 = "'"; + var peg$c20 = "\""; + var peg$c21 = "|"; + var peg$c22 = "//"; + var peg$c23 = "\r\n"; + var peg$c24 = "\n"; + var peg$c25 = " "; + + var peg$r0 = /^[^"\n]/; + var peg$r1 = /^[^'\n]/; + var peg$r2 = /^[0-9]/i; + var peg$r3 = /^[a-z0-9_.]/i; + var peg$r4 = /^[^\t\r\n]/; + var peg$r5 = /^[^\n]/; + var peg$r6 = /^[ \t\r\n\r]/; + + var peg$e0 = peg$literalExpectation(",", false); + var peg$e1 = peg$literalExpectation(":", false); + var peg$e2 = peg$literalExpectation("on_delete", true); + var peg$e3 = peg$literalExpectation("on_update", true); + var peg$e4 = peg$otherExpectation("add index"); + var peg$e5 = peg$literalExpectation("add_index", false); + var peg$e6 = peg$otherExpectation("schema define"); + var peg$e7 = peg$literalExpectation("ActiveRecord::Schema.define", false); + var peg$e8 = peg$otherExpectation("create table"); + var peg$e9 = peg$literalExpectation("create_table", true); + var peg$e10 = peg$otherExpectation("do |t|"); + var peg$e11 = peg$otherExpectation("index"); + var peg$e12 = peg$literalExpectation(".index", false); + var peg$e13 = peg$otherExpectation("references"); + var peg$e14 = peg$literalExpectation(".references", false); + var peg$e15 = peg$otherExpectation("add foreign key"); + var peg$e16 = peg$literalExpectation("add_foreign_key", true); + var peg$e17 = peg$otherExpectation("column"); + var peg$e18 = peg$literalExpectation("column", false); + var peg$e19 = peg$otherExpectation("primary key"); + var peg$e20 = peg$literalExpectation("primary_key", false); + var peg$e21 = peg$literalExpectation("version", false); + var peg$e22 = peg$literalExpectation("do", false); + var peg$e23 = peg$literalExpectation("end", false); + var peg$e24 = peg$otherExpectation("lambda function"); + var peg$e25 = peg$literalExpectation("=>", false); + var peg$e26 = peg$literalExpectation("->", false); + var peg$e27 = peg$classExpectation(["\"", "\n"], true, false); + var peg$e28 = peg$classExpectation(["'", "\n"], true, false); + var peg$e29 = peg$literalExpectation(".", false); + var peg$e30 = peg$anyExpectation(); + var peg$e31 = peg$classExpectation([["0", "9"]], false, true); + var peg$e32 = peg$otherExpectation("letter, number or underscore"); + var peg$e33 = peg$classExpectation([["a", "z"], ["0", "9"], "_", "."], false, true); + var peg$e34 = peg$otherExpectation("comment line"); + var peg$e35 = peg$literalExpectation("#", false); + var peg$e36 = peg$otherExpectation("whatever"); + var peg$e37 = peg$classExpectation(["\t", "\r", "\n"], true, false); + var peg$e38 = peg$literalExpectation("'", false); + var peg$e39 = peg$literalExpectation("\"", false); + var peg$e40 = peg$literalExpectation("|", false); + var peg$e41 = peg$otherExpectation("comment"); + var peg$e42 = peg$literalExpectation("//", false); + var peg$e43 = peg$classExpectation(["\n"], true, false); + var peg$e44 = peg$otherExpectation("newline"); + var peg$e45 = peg$literalExpectation("\r\n", false); + var peg$e46 = peg$literalExpectation("\n", false); + var peg$e47 = peg$otherExpectation("whitespace"); + var peg$e48 = peg$classExpectation([" ", "\t", "\r", "\n", "\r"], false, false); + var peg$e49 = peg$literalExpectation(" ", false); + + var peg$f0 = function() { + return implicityRef(data) +}; + var peg$f1 = function(tableData) { + const { table, refs } = tableData; + pushTable(table); + pushRefs(refs) +}; + var peg$f2 = function(r) { + pushRef(r); + }; + var peg$f3 = function(fromTable, toTable, props) { + const foreign = refactorForeign(createForeign(fromTable, toTable, props)); + return foreign; + }; + var peg$f4 = function(columnName) { return ({ columnName }) }; + var peg$f5 = function(primaryKey) { return ({ primaryKey }) }; + var peg$f6 = function(r, value) { + switch (r.toLowerCase()) { + case 'on_delete': + return { + onDelete: value.split('_').join(' ') + } + case 'on_update': + return { + onUpdate: value.split('_').join(' ') + } + } + }; + var peg$f7 = function(name, body) { + const table = ({ + name, + fields: addPrimaryKey(body.fields), + // index: _.union(...body.index) + }) + return { + table, + refs: createRefFromTableWithReference(table, body.references) + }; + }; + var peg$f8 = function(fields) { + return ({ + fields: fields.filter(field => field.isField).map(field => field.field), + index: fields.filter(field => field.isIndex).map(field => field.index), + references: fields.filter(field => field.isReferences).map(field => field.reference), + }); + }; + var peg$f9 = function(field) { return field }; + var peg$f10 = function(reference) { return ({ reference, isReferences: true })}; + var peg$f11 = function(field) { return ({ field, isField: true }) }; + var peg$f12 = function(reference) { + return reference; + }; + var peg$f13 = function(type, name) { + return ({ + name: name, + type: {type_name: type}, + }) + }; + var peg$f14 = function(reference) { return reference }; + var peg$f15 = function(reference) { return reference }; + var peg$f16 = function(c) { return c.join("") }; + var peg$f17 = function(c) { return c.join("") }; + var peg$f18 = function(c) { return c.join("") }; + var peg$f19 = function(c) { return c.join("") }; + var peg$f20 = function(c) { return c.join("") }; + var peg$f21 = function() {return text()}; + var peg$currPos = options.peg$currPos | 0; + var peg$savedPos = peg$currPos; + var peg$posDetailsCache = [{ line: 1, column: 1 }]; + var peg$maxFailPos = peg$currPos; + var peg$maxFailExpected = options.peg$maxFailExpected || []; + var peg$silentFails = options.peg$silentFails | 0; + + var peg$result; + + if (options.startRule) { + if (!(options.startRule in peg$startRuleFunctions)) { + throw new Error("Can't start parsing from rule \"" + options.startRule + "\"."); + } + + peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; + } + + function text() { + return input.substring(peg$savedPos, peg$currPos); + } + + function offset() { + return peg$savedPos; + } + + function range() { + return { + source: peg$source, + start: peg$savedPos, + end: peg$currPos + }; + } + + function location() { + return peg$computeLocation(peg$savedPos, peg$currPos); + } + + function expected(description, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildStructuredError( + [peg$otherExpectation(description)], + input.substring(peg$savedPos, peg$currPos), + location + ); + } + + function error(message, location) { + location = location !== undefined + ? location + : peg$computeLocation(peg$savedPos, peg$currPos); + + throw peg$buildSimpleError(message, location); + } + + function peg$literalExpectation(text, ignoreCase) { + return { type: "literal", text: text, ignoreCase: ignoreCase }; + } + + function peg$classExpectation(parts, inverted, ignoreCase) { + return { type: "class", parts: parts, inverted: inverted, ignoreCase: ignoreCase }; + } + + function peg$anyExpectation() { + return { type: "any" }; + } + + function peg$endExpectation() { + return { type: "end" }; + } + + function peg$otherExpectation(description) { + return { type: "other", description: description }; + } + + function peg$computePosDetails(pos) { + var details = peg$posDetailsCache[pos]; + var p; + + if (details) { + return details; + } else { + if (pos >= peg$posDetailsCache.length) { + p = peg$posDetailsCache.length - 1; + } else { + p = pos; + while (!peg$posDetailsCache[--p]) {} + } + + details = peg$posDetailsCache[p]; + details = { + line: details.line, + column: details.column + }; + + while (p < pos) { + if (input.charCodeAt(p) === 10) { + details.line++; + details.column = 1; + } else { + details.column++; + } + + p++; + } + + peg$posDetailsCache[pos] = details; + + return details; + } + } + + function peg$computeLocation(startPos, endPos, offset) { + var startPosDetails = peg$computePosDetails(startPos); + var endPosDetails = peg$computePosDetails(endPos); + + var res = { + source: peg$source, + start: { + offset: startPos, + line: startPosDetails.line, + column: startPosDetails.column + }, + end: { + offset: endPos, + line: endPosDetails.line, + column: endPosDetails.column + } + }; + if (offset && peg$source && (typeof peg$source.offset === "function")) { + res.start = peg$source.offset(res.start); + res.end = peg$source.offset(res.end); + } + return res; + } + + function peg$fail(expected) { + if (peg$currPos < peg$maxFailPos) { return; } + + if (peg$currPos > peg$maxFailPos) { + peg$maxFailPos = peg$currPos; + peg$maxFailExpected = []; + } + + peg$maxFailExpected.push(expected); + } + + function peg$buildSimpleError(message, location) { + return new peg$SyntaxError(message, null, null, location); + } + + function peg$buildStructuredError(expected, found, location) { + return new peg$SyntaxError( + peg$SyntaxError.buildMessage(expected, found), + expected, + found, + location + ); + } + + function peg$parseschema() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parseline_rule(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parseline_rule(); + } + peg$savedPos = s0; + s1 = peg$f0(); + s0 = s1; + + return s0; + } + + function peg$parseline_rule() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsewhitespace(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsewhitespace(); + } + s2 = peg$parserule(); + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$parsecomment_line(); + if (s0 === peg$FAILED) { + s0 = peg$parseend_line(); + if (s0 === peg$FAILED) { + s0 = peg$parse__(); + } + } + } + + return s0; + } + + function peg$parserule() { + var s0, s1; + + s0 = peg$currPos; + s1 = peg$parsecreate_table_syntax(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f1(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parseadd_foreign_key_syntax(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f2(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$parseother_class_prop(); + } + } + + return s0; + } + + function peg$parseadd_foreign_key_syntax() { + var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsesp(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsesp(); + } + s2 = peg$parseadd_foreign_key(); + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parsesp(); + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parsesp(); + } + s4 = peg$parsename(); + if (s4 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 44) { + s5 = peg$c0; + peg$currPos++; + } else { + s5 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s5 !== peg$FAILED) { + s6 = []; + s7 = peg$parsesp(); + while (s7 !== peg$FAILED) { + s6.push(s7); + s7 = peg$parsesp(); + } + s7 = peg$parsename(); + if (s7 !== peg$FAILED) { + s8 = []; + s9 = peg$parseadd_foreign_key_props_syntax(); + while (s9 !== peg$FAILED) { + s8.push(s9); + s9 = peg$parseadd_foreign_key_props_syntax(); + } + peg$savedPos = s0; + s0 = peg$f3(s4, s7, s8); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseadd_foreign_key_props_syntax() { + var s0, s1, s2, s3, s4, s5, s6; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s1 = peg$c0; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + s3 = peg$parsecolumn(); + if (s3 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 58) { + s4 = peg$c1; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = peg$parsesp(); + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = peg$parsesp(); + } + s6 = peg$parsename(); + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f4(s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s1 = peg$c0; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + s3 = peg$parseprimary_key(); + if (s3 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 58) { + s4 = peg$c1; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = peg$parsesp(); + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = peg$parsesp(); + } + s6 = peg$parsename(); + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f5(s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 44) { + s1 = peg$c0; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e0); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + s3 = peg$parsereferential_actions(); + if (s3 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 58) { + s4 = peg$c1; + peg$currPos++; + } else { + s4 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s4 !== peg$FAILED) { + s5 = []; + s6 = peg$parsesp(); + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = peg$parsesp(); + } + s6 = peg$parsesymbol(); + if (s6 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f6(s3, s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } + } + + return s0; + } + + function peg$parsecreate_table_syntax() { + var s0, s1, s2, s3, s4, s5, s6, s7; + + s0 = peg$currPos; + s1 = peg$parsecreate_table(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + s3 = peg$parsename(); + if (s3 !== peg$FAILED) { + s4 = peg$parsewhateters(); + s5 = peg$parseendline(); + if (s5 !== peg$FAILED) { + s6 = peg$parsetable_body(); + s7 = peg$parseend_line(); + if (s7 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f7(s3, s6); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsetable_body() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsefield(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsefield(); + } + peg$savedPos = s0; + s1 = peg$f8(s1); + s0 = s1; + + return s0; + } + + function peg$parsefield() { + var s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsewhitespace(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsewhitespace(); + } + s2 = peg$parsetable_field_syntax(); + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parsewhatever(); + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parsewhatever(); + } + s4 = peg$parseendline(); + if (s4 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f9(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsetable_field_syntax() { + var s0, s1; + + s0 = peg$parsefield_index_syntax(); + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parsefield_reference_syntax(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f10(s1); + } + s0 = s1; + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parsefield_type_syntax(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f11(s1); + } + s0 = s1; + } + } + + return s0; + } + + function peg$parsefield_index_syntax() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parseindex(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parsewhateters(); + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsefield_reference_syntax() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parsereferences(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parsereference_value(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f12(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsefield_type_syntax() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parsefield_type(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parsename(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f13(s1, s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsereference_value() { + var s0, s1, s2; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 58) { + s1 = peg$c1; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 !== peg$FAILED) { + s2 = peg$parsevariable(); + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f14(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + if (s0 === peg$FAILED) { + s0 = peg$currPos; + s1 = peg$parsename(); + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f15(s1); + } + s0 = s1; + } + + return s0; + } + + function peg$parsereferential_actions() { + var s0; + + s0 = input.substr(peg$currPos, 9); + if (s0.toLowerCase() === peg$c2) { + peg$currPos += 9; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e2); } + } + if (s0 === peg$FAILED) { + s0 = input.substr(peg$currPos, 9); + if (s0.toLowerCase() === peg$c3) { + peg$currPos += 9; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e3); } + } + } + + return s0; + } + + function peg$parseadd_index() { + var s0, s1; + + peg$silentFails++; + if (input.substr(peg$currPos, 9) === peg$c4) { + s0 = peg$c4; + peg$currPos += 9; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e5); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e4); } + } + + return s0; + } + + function peg$parseschema_define() { + var s0, s1; + + peg$silentFails++; + if (input.substr(peg$currPos, 27) === peg$c5) { + s0 = peg$c5; + peg$currPos += 27; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e7); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e6); } + } + + return s0; + } + + function peg$parsecreate_table() { + var s0, s1; + + peg$silentFails++; + s0 = input.substr(peg$currPos, 12); + if (s0.toLowerCase() === peg$c6) { + peg$currPos += 12; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e9); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e8); } + } + + return s0; + } + + function peg$parseend_create_table() { + var s0, s1, s2, s3, s4, s5, s6; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parsedo(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsesp(); + if (s3 !== peg$FAILED) { + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsesp(); + } + } else { + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseabs(); + if (s3 !== peg$FAILED) { + s4 = peg$parsecharacter(); + if (s4 !== peg$FAILED) { + s5 = peg$parseabs(); + if (s5 !== peg$FAILED) { + s6 = peg$parseendline(); + if (s6 !== peg$FAILED) { + s1 = [s1, s2, s3, s4, s5, s6]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e10); } + } + + return s0; + } + + function peg$parseindex() { + var s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parsecharacter(); + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 6) === peg$c7) { + s2 = peg$c7; + peg$currPos += 6; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e12); } + } + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e11); } + } + + return s0; + } + + function peg$parsereferences() { + var s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + s1 = peg$parsecharacter(); + if (s1 !== peg$FAILED) { + if (input.substr(peg$currPos, 11) === peg$c8) { + s2 = peg$c8; + peg$currPos += 11; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e14); } + } + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e13); } + } + + return s0; + } + + function peg$parseadd_foreign_key() { + var s0, s1; + + peg$silentFails++; + s0 = input.substr(peg$currPos, 15); + if (s0.toLowerCase() === peg$c9) { + peg$currPos += 15; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e16); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e15); } + } + + return s0; + } + + function peg$parsecolumn() { + var s0, s1; + + peg$silentFails++; + if (input.substr(peg$currPos, 6) === peg$c10) { + s0 = peg$c10; + peg$currPos += 6; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e18); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e17); } + } + + return s0; + } + + function peg$parseprimary_key() { + var s0, s1; + + peg$silentFails++; + if (input.substr(peg$currPos, 11) === peg$c11) { + s0 = peg$c11; + peg$currPos += 11; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e20); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e19); } + } + + return s0; + } + + function peg$parseversion() { + var s0; + + if (input.substr(peg$currPos, 7) === peg$c12) { + s0 = peg$c12; + peg$currPos += 7; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e21); } + } + + return s0; + } + + function peg$parsedo() { + var s0; + + if (input.substr(peg$currPos, 2) === peg$c13) { + s0 = peg$c13; + peg$currPos += 2; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e22); } + } + + return s0; + } + + function peg$parseend() { + var s0; + + if (input.substr(peg$currPos, 3) === peg$c14) { + s0 = peg$c14; + peg$currPos += 3; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e23); } + } + + return s0; + } + + function peg$parselambda_function() { + var s0, s1; + + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c15) { + s0 = peg$c15; + peg$currPos += 2; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e25); } + } + if (s0 === peg$FAILED) { + if (input.substr(peg$currPos, 2) === peg$c16) { + s0 = peg$c16; + peg$currPos += 2; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e26); } + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e24); } + } + + return s0; + } + + function peg$parseother_class_prop() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parsevariable(); + if (s1 !== peg$FAILED) { + s2 = peg$parsewhateters(); + s3 = peg$parseendline(); + if (s3 === peg$FAILED) { + s3 = null; + } + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsename() { + var s0; + + s0 = peg$parsedouble_quote_name(); + if (s0 === peg$FAILED) { + s0 = peg$parsesingle_quote_name(); + } + + return s0; + } + + function peg$parsedouble_quote_name() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parsedouble_quote(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r0.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e27); } + } + } + s3 = peg$parsedouble_quote(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f16(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsesingle_quote_name() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = peg$parsesingle_quote(); + if (s1 !== peg$FAILED) { + s2 = []; + s3 = input.charAt(peg$currPos); + if (peg$r1.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = input.charAt(peg$currPos); + if (peg$r1.test(s3)) { + peg$currPos++; + } else { + s3 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e28); } + } + } + s3 = peg$parsesingle_quote(); + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f17(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsesymbol() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + if (input.charCodeAt(peg$currPos) === 58) { + s1 = peg$c1; + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e1); } + } + if (s1 !== peg$FAILED) { + s2 = []; + s3 = peg$parsecharacter(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsecharacter(); + } + peg$savedPos = s0; + s0 = peg$f18(s2); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsevariable() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsecharacter(); + if (s2 !== peg$FAILED) { + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsecharacter(); + } + } else { + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + peg$savedPos = s0; + s1 = peg$f19(s1); + } + s0 = s1; + + return s0; + } + + function peg$parsefield_type() { + var s0, s1, s2, s3, s4; + + s0 = peg$currPos; + s1 = peg$parsecharacter(); + if (s1 !== peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 46) { + s2 = peg$c17; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e29); } + } + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parsecharacter(); + if (s4 !== peg$FAILED) { + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parsecharacter(); + } + } else { + s3 = peg$FAILED; + } + if (s3 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f20(s3); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsenot_whitespace() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = peg$currPos; + peg$silentFails++; + s2 = peg$parsewhitespace(); + peg$silentFails--; + if (s2 === peg$FAILED) { + s1 = undefined; + } else { + peg$currPos = s1; + s1 = peg$FAILED; + } + if (s1 !== peg$FAILED) { + if (input.length > peg$currPos) { + s2 = input.charAt(peg$currPos); + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e30); } + } + if (s2 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f21(); + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsenumber() { + var s0; + + s0 = input.charAt(peg$currPos); + if (peg$r2.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e31); } + } + + return s0; + } + + function peg$parsecharacter() { + var s0, s1; + + peg$silentFails++; + s0 = input.charAt(peg$currPos); + if (peg$r3.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e33); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e32); } + } + + return s0; + } + + function peg$parseend_line() { + var s0, s1, s2, s3; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsewhitespace(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsewhitespace(); + } + s2 = peg$parseend(); + if (s2 !== peg$FAILED) { + s3 = peg$parseendline(); + if (s3 === peg$FAILED) { + s3 = null; + } + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsewhatever_line() { + var s0, s1, s2, s3, s4, s5; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsewhitespace(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsewhitespace(); + } + s2 = peg$currPos; + s3 = peg$parsewhateters(); + s4 = peg$currPos; + peg$silentFails++; + s5 = peg$parseend(); + peg$silentFails--; + if (s5 === peg$FAILED) { + s4 = undefined; + } else { + peg$currPos = s4; + s4 = peg$FAILED; + } + if (s4 !== peg$FAILED) { + s3 = [s3, s4]; + s2 = s3; + } else { + peg$currPos = s2; + s2 = peg$FAILED; + } + if (s2 !== peg$FAILED) { + s3 = peg$parseendline(); + if (s3 === peg$FAILED) { + s3 = null; + } + s1 = [s1, s2, s3]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsecomment_line() { + var s0, s1, s2, s3, s4; + + peg$silentFails++; + s0 = peg$currPos; + s1 = []; + s2 = peg$parsewhitespace(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsewhitespace(); + } + if (input.charCodeAt(peg$currPos) === 35) { + s2 = peg$c18; + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e35); } + } + if (s2 !== peg$FAILED) { + s3 = peg$parsewhateters(); + s4 = peg$parseendline(); + if (s4 === peg$FAILED) { + s4 = null; + } + s1 = [s1, s2, s3, s4]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e34); } + } + + return s0; + } + + function peg$parsewhateters() { + var s0, s1; + + peg$silentFails++; + s0 = []; + s1 = input.charAt(peg$currPos); + if (peg$r4.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = input.charAt(peg$currPos); + if (peg$r4.test(s1)) { + peg$currPos++; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + } + peg$silentFails--; + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e36); } + + return s0; + } + + function peg$parsewhatever() { + var s0; + + s0 = input.charAt(peg$currPos); + if (peg$r4.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e37); } + } + + return s0; + } + + function peg$parsesingle_quote() { + var s0; + + if (input.charCodeAt(peg$currPos) === 39) { + s0 = peg$c19; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e38); } + } + + return s0; + } + + function peg$parsedouble_quote() { + var s0; + + if (input.charCodeAt(peg$currPos) === 34) { + s0 = peg$c20; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e39); } + } + + return s0; + } + + function peg$parse_() { + var s0, s1; + + s0 = []; + s1 = peg$parsecomment(); + if (s1 === peg$FAILED) { + s1 = peg$parsewhitespace(); + } + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parsecomment(); + if (s1 === peg$FAILED) { + s1 = peg$parsewhitespace(); + } + } + + return s0; + } + + function peg$parse__() { + var s0, s1; + + s0 = []; + s1 = peg$parsecomment(); + if (s1 === peg$FAILED) { + s1 = peg$parsewhitespace(); + } + if (s1 !== peg$FAILED) { + while (s1 !== peg$FAILED) { + s0.push(s1); + s1 = peg$parsecomment(); + if (s1 === peg$FAILED) { + s1 = peg$parsewhitespace(); + } + } + } else { + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parseabs() { + var s0; + + if (input.charCodeAt(peg$currPos) === 124) { + s0 = peg$c21; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e40); } + } + + return s0; + } + + function peg$parseendline() { + var s0, s1, s2; + + s0 = peg$currPos; + s1 = []; + s2 = peg$parsesp(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsesp(); + } + s2 = peg$parsenewline(); + if (s2 !== peg$FAILED) { + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + + return s0; + } + + function peg$parsecomment() { + var s0, s1, s2; + + peg$silentFails++; + s0 = peg$currPos; + if (input.substr(peg$currPos, 2) === peg$c22) { + s1 = peg$c22; + peg$currPos += 2; + } else { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e42); } + } + if (s1 !== peg$FAILED) { + s2 = input.charAt(peg$currPos); + if (peg$r5.test(s2)) { + peg$currPos++; + } else { + s2 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e43); } + } + if (s2 === peg$FAILED) { + s2 = null; + } + s1 = [s1, s2]; + s0 = s1; + } else { + peg$currPos = s0; + s0 = peg$FAILED; + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e41); } + } + + return s0; + } + + function peg$parsenewline() { + var s0, s1; + + peg$silentFails++; + if (input.substr(peg$currPos, 2) === peg$c23) { + s0 = peg$c23; + peg$currPos += 2; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e45); } + } + if (s0 === peg$FAILED) { + if (input.charCodeAt(peg$currPos) === 10) { + s0 = peg$c24; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e46); } + } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e44); } + } + + return s0; + } + + function peg$parsewhitespace() { + var s0, s1; + + peg$silentFails++; + s0 = input.charAt(peg$currPos); + if (peg$r6.test(s0)) { + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e48); } + } + peg$silentFails--; + if (s0 === peg$FAILED) { + s1 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e47); } + } + + return s0; + } + + function peg$parsesp() { + var s0; + + if (input.charCodeAt(peg$currPos) === 32) { + s0 = peg$c25; + peg$currPos++; + } else { + s0 = peg$FAILED; + if (peg$silentFails === 0) { peg$fail(peg$e49); } + } + + return s0; + } + + + var pluralize = require('pluralize'); + var lodash = require('lodash'); + + let data = { + tables: [], + refs: [] + } + + function pushTable(table) { + if (data.tables.find(t => t.name == table.name)) { + error("Duplicated table name"); + } else { + const idField = table.fields.find(field => field.name === "id"); + if (!idField) { + table.fields.unshift({ + name: "id", + type: { type_name: "varchar" } + }); + } + data.tables.push(table); + } + } + + function addPrimaryKey(fields = [], props = []) { + const primaryKey = props.find(prop => prop.name === 'primary_key'); + if (!primaryKey) return fields; + if (fields.find(key => key.name === primaryKey.value)) { + return fields.map(({ name, type}) => ({ + name, type, + PK: primaryKey.value === field.name, + })) + } + const newFields = [{ + name: primaryKey.value, + type: { type_name: "varchar" }, + PK: true, + }]; + return newFields.concat(fields); + } + + function findTableByNameOrAlias(name) { + const table = data.tables.find(t => t.name == name || t.alias == name); + if (table === undefined) { + error("Table " + name +" not found"); + } + return table; + } + + function isSameEndpoints (endpoint1, endpoint2) { + return endpoint1.tableName == endpoint2.tableName && + lodash.isEqual(lodash.sortBy(endpoint1.fieldNames), lodash.sortBy(endpoint2.fieldNames)) + } + + function isSameEndpointsPairs (endpointsPair1, endpointsPair2) { + return isSameEndpoints(endpointsPair1[0], endpointsPair2[0]) + && isSameEndpoints(endpointsPair1[1], endpointsPair2[1]) + } + + function isSameEndpointsRefs(ref1, ref2) { + return isSameEndpointsPairs (ref1.endpoints, ref2.endpoints) + || isSameEndpointsPairs(ref1.endpoints, ref2.endpoints.slice().reverse()) + } + + function pushRef(ref) { + if (!ref) return; + if (data.refs.find(p => isSameEndpointsRefs(p, ref))) { + error("Duplicated references"); + } + data.refs.push(ref); + } + function pushRefs(refs = []) { + if (!refs || refs.length === 0) return + for(let i = 0; i < refs.length; i += 1) { + pushRef(refs[i]); + } + } + function refactorForeign(ref) { + // add relation + const { tables } = data; + const { endpoints } = ref; + const fromTable = tables.find(table => table.name === endpoints[0].tableName) + if (!fromTable) { + // TODO: handle error + // throw { + // message: `Table ${endpoints[0].table} not found` + // } + // return ref; + return null; + } + const toTable = tables.find(table => table.name === endpoints[1].tableName) + if (!toTable) { + // TODO: handle error + // throw { + // message: `Table ${endpoints[1].table} not found` + // } + // return ref; + return null; + } + if (!endpoints[0].fieldNames) { + const singleNameOfPrimaryTable = pluralize.singular(endpoints[1].tableName) + const columnName = `${singleNameOfPrimaryTable}_id`; + endpoints[0].fieldNames = [columnName]; + const columnField = fromTable.fields.find(field => field.name === columnName); + if (!columnField) { + // TODO: handle erro + // throw { + // message: `Field ${columnName} not found in table ${endpoints[0].table}` + // } + // return ref; + return null; + } + endpoints[0].fieldNames = [columnName]; + } + if (!endpoints[1].fieldNames) { + const primaryKey = 'id'; + endpoints[1].fieldNames = [primaryKey]; + } + return ref; + } + + function createForeign(fromTable, toTable, props) { + const endpoints = ([{ + tableName: fromTable, + relation: '1', + }, + { + tableName: toTable, + relation: '1', + }]); + let refProp = {}; + for (let i = 0; i < props.length; i += 1) { + const currentProp = props[i]; + if (currentProp.columnName) { + endpoints[0].fieldNames = [currentProp.columnName]; + } + if (currentProp.primaryKey) { + endpoints[1].fieldNames = [currentProp.primaryKey]; + } + if (currentProp.onDelete) { + refProp = { + ...refProp, + onDelete: currentProp.onDelete + } + } + if (currentProp.onUpdate) { + refProp = { + ...refProp, + onUpdate: currentProp.onUpdate + } + } + } + return { + name: `fk_rails_${fromTable}_${toTable}`, + endpoints, + ...refProp + }; + } + + function createRefFromTableWithReference(table, references) { + if (!references || references.length === 0) { + return []; + } + const refs = []; + for (let i = 0; i < references.length; i += 1) { + const reference = references[i]; + const referenceTable = pluralize.plural(reference); + const { tables } = data; + + const toTable = tables.find(table => table.name === referenceTable) + + if (!toTable) { + continue; + } + // add field to table if not exists (`${reference}_id`) + // auto add type of new field to be varchar if primaryKey not found + const columnName = `${reference}_id`; + const primaryKeyName = 'id'; + const column = table.fields.find(field => field.name === columnName); + const primaryKey = toTable.fields.find(field => field.name === primaryKeyName); + if (!column) { + table.fields.push({ + name: columnName, + type: { type_name: primaryKey ? primaryKey.type.type_name : 'varchar'}, + }) + } + + refs.push({ + name: `fk_rails_${table.name}_${referenceTable}`, + endpoints: [ + { + tableName: table.name, + fieldNames: [columnName], + relation: '1', + }, + { + tableName: referenceTable, + fieldNames: [primaryKeyName], + relation: '1', + } + ] + }) + } + return refs; + } + function implicityRef(data) { + const { tables, refs } = data; + const tableWithFieldName = tables.map(table => { + const { name } = table; + const singularName = pluralize.singular(name); + return ({ + name, + field: `${singularName}_id` + }); + }) + for (let i = 0; i < tables.length; i += 1) { + const table = tables[i]; + const { fields } = table; + for (let j = 0; j < fields.length; j += 1) { + const field = fields[j]; + const refWithTable = tableWithFieldName.find(table => table.field === field.name); + if (refWithTable) { + const newRef = ({ + name: `fk_rails_${table.name}_${refWithTable.name}`, + endpoints: [ + { + tableName: table.name, + fieldNames: [field.name], + relation: '1', + }, + { + tableName: refWithTable.name, + fieldNames: ['id'], + relation: '1', + } + ] + }); + const duplicateRef = refs.find(ref => isSameEndpointsRefs(ref, newRef)); + if (!duplicateRef) { + refs.push(newRef) + } + } + } + } + return data; + } + + peg$result = peg$startRuleFunction(); + + if (options.peg$library) { + return /** @type {any} */ ({ + peg$result, + peg$currPos, + peg$FAILED, + peg$maxFailExpected, + peg$maxFailPos + }); + } + if (peg$result !== peg$FAILED && peg$currPos === input.length) { + return peg$result; + } else { + if (peg$result !== peg$FAILED && peg$currPos < input.length) { + peg$fail(peg$endExpectation()); + } + + throw peg$buildStructuredError( + peg$maxFailExpected, + peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, + peg$maxFailPos < input.length + ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) + : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) + ); + } +} + +module.exports = { + StartRules: ["schema"], + SyntaxError: peg$SyntaxError, + parse: peg$parse +}; diff --git a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs new file mode 100644 index 000000000..db3751522 --- /dev/null +++ b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs @@ -0,0 +1,380 @@ +{ + var pluralize = require('pluralize'); + var lodash = require('lodash'); + + let data = { + tables: [], + refs: [] + } + + function pushTable(table) { + if (data.tables.find(t => t.name == table.name)) { + error("Duplicated table name"); + } else { + const idField = table.fields.find(field => field.name === "id"); + if (!idField) { + table.fields.unshift({ + name: "id", + type: { type_name: "varchar" } + }); + } + data.tables.push(table); + } + } + + function addPrimaryKey(fields = [], props = []) { + const primaryKey = props.find(prop => prop.name === 'primary_key'); + if (!primaryKey) return fields; + if (fields.find(key => key.name === primaryKey.value)) { + return fields.map(({ name, type}) => ({ + name, type, + PK: primaryKey.value === field.name, + })) + } + const newFields = [{ + name: primaryKey.value, + type: { type_name: "varchar" }, + PK: true, + }]; + return newFields.concat(fields); + } + + function findTableByNameOrAlias(name) { + const table = data.tables.find(t => t.name == name || t.alias == name); + if (table === undefined) { + error("Table " + name +" not found"); + } + return table; + } + + function isSameEndpoints (endpoint1, endpoint2) { + return endpoint1.tableName == endpoint2.tableName && + lodash.isEqual(lodash.sortBy(endpoint1.fieldNames), lodash.sortBy(endpoint2.fieldNames)) + } + + function isSameEndpointsPairs (endpointsPair1, endpointsPair2) { + return isSameEndpoints(endpointsPair1[0], endpointsPair2[0]) + && isSameEndpoints(endpointsPair1[1], endpointsPair2[1]) + } + + function isSameEndpointsRefs(ref1, ref2) { + return isSameEndpointsPairs (ref1.endpoints, ref2.endpoints) + || isSameEndpointsPairs(ref1.endpoints, ref2.endpoints.slice().reverse()) + } + + function pushRef(ref) { + if (!ref) return; + if (data.refs.find(p => isSameEndpointsRefs(p, ref))) { + error("Duplicated references"); + } + data.refs.push(ref); + } + function pushRefs(refs = []) { + if (!refs || refs.length === 0) return + for(let i = 0; i < refs.length; i += 1) { + pushRef(refs[i]); + } + } + function refactorForeign(ref) { + // add relation + const { tables } = data; + const { endpoints } = ref; + const fromTable = tables.find(table => table.name === endpoints[0].tableName) + if (!fromTable) { + // TODO: handle error + // throw { + // message: `Table ${endpoints[0].table} not found` + // } + // return ref; + return null; + } + const toTable = tables.find(table => table.name === endpoints[1].tableName) + if (!toTable) { + // TODO: handle error + // throw { + // message: `Table ${endpoints[1].table} not found` + // } + // return ref; + return null; + } + if (!endpoints[0].fieldNames) { + const singleNameOfPrimaryTable = pluralize.singular(endpoints[1].tableName) + const columnName = `${singleNameOfPrimaryTable}_id`; + endpoints[0].fieldNames = [columnName]; + const columnField = fromTable.fields.find(field => field.name === columnName); + if (!columnField) { + // TODO: handle erro + // throw { + // message: `Field ${columnName} not found in table ${endpoints[0].table}` + // } + // return ref; + return null; + } + endpoints[0].fieldNames = [columnName]; + } + if (!endpoints[1].fieldNames) { + const primaryKey = 'id'; + endpoints[1].fieldNames = [primaryKey]; + } + return ref; + } + + function createForeign(fromTable, toTable, props) { + const endpoints = ([{ + tableName: fromTable, + relation: '1', + }, + { + tableName: toTable, + relation: '1', + }]); + let refProp = {}; + for (let i = 0; i < props.length; i += 1) { + const currentProp = props[i]; + if (currentProp.columnName) { + endpoints[0].fieldNames = [currentProp.columnName]; + } + if (currentProp.primaryKey) { + endpoints[1].fieldNames = [currentProp.primaryKey]; + } + if (currentProp.onDelete) { + refProp = { + ...refProp, + onDelete: currentProp.onDelete + } + } + if (currentProp.onUpdate) { + refProp = { + ...refProp, + onUpdate: currentProp.onUpdate + } + } + } + return { + name: `fk_rails_${fromTable}_${toTable}`, + endpoints, + ...refProp + }; + } + + function createRefFromTableWithReference(table, references) { + if (!references || references.length === 0) { + return []; + } + const refs = []; + for (let i = 0; i < references.length; i += 1) { + const reference = references[i]; + const referenceTable = pluralize.plural(reference); + const { tables } = data; + + const toTable = tables.find(table => table.name === referenceTable) + + if (!toTable) { + continue; + } + // add field to table if not exists (`${reference}_id`) + // auto add type of new field to be varchar if primaryKey not found + const columnName = `${reference}_id`; + const primaryKeyName = 'id'; + const column = table.fields.find(field => field.name === columnName); + const primaryKey = toTable.fields.find(field => field.name === primaryKeyName); + if (!column) { + table.fields.push({ + name: columnName, + type: { type_name: primaryKey ? primaryKey.type.type_name : 'varchar'}, + }) + } + + refs.push({ + name: `fk_rails_${table.name}_${referenceTable}`, + endpoints: [ + { + tableName: table.name, + fieldNames: [columnName], + relation: '1', + }, + { + tableName: referenceTable, + fieldNames: [primaryKeyName], + relation: '1', + } + ] + }) + } + return refs; + } + function implicityRef(data) { + const { tables, refs } = data; + const tableWithFieldName = tables.map(table => { + const { name } = table; + const singularName = pluralize.singular(name); + return ({ + name, + field: `${singularName}_id` + }); + }) + for (let i = 0; i < tables.length; i += 1) { + const table = tables[i]; + const { fields } = table; + for (let j = 0; j < fields.length; j += 1) { + const field = fields[j]; + const refWithTable = tableWithFieldName.find(table => table.field === field.name); + if (refWithTable) { + const newRef = ({ + name: `fk_rails_${table.name}_${refWithTable.name}`, + endpoints: [ + { + tableName: table.name, + fieldNames: [field.name], + relation: '1', + }, + { + tableName: refWithTable.name, + fieldNames: ['id'], + relation: '1', + } + ] + }); + const duplicateRef = refs.find(ref => isSameEndpointsRefs(ref, newRef)); + if (!duplicateRef) { + refs.push(newRef) + } + } + } + } + return data; + } +} + +schema = line_rule* { + return implicityRef(data) +} + +line_rule + = whitespace* rule + / comment_line + / end_line + / __ + +rule = tableData:create_table_syntax { + const { table, refs } = tableData; + pushTable(table); + pushRefs(refs) +} +/ r:add_foreign_key_syntax { + pushRef(r); + } +/ other_class_prop + +add_foreign_key_syntax + = sp* add_foreign_key sp* fromTable:name","sp* toTable:name props:add_foreign_key_props_syntax* { + const foreign = refactorForeign(createForeign(fromTable, toTable, props)); + return foreign; + } + +add_foreign_key_props_syntax += "," sp* column":" sp* columnName:name { return ({ columnName }) } +/ "," sp* primary_key":" sp* primaryKey:name { return ({ primaryKey }) } +/ "," sp* r:referential_actions":" sp* value:symbol { + switch (r.toLowerCase()) { + case 'on_delete': + return { + onDelete: value.split('_').join(' ') + } + case 'on_update': + return { + onUpdate: value.split('_').join(' ') + } + } + } + +create_table_syntax + = create_table sp* name:name whateters endline + body:table_body + end_line { + const table = ({ + name, + fields: addPrimaryKey(body.fields), + // index: _.union(...body.index) + }) + return { + table, + refs: createRefFromTableWithReference(table, body.references) + }; + } + +table_body = fields:field* { + return ({ + fields: fields.filter(field => field.isField).map(field => field.field), + index: fields.filter(field => field.isIndex).map(field => field.index), + references: fields.filter(field => field.isReferences).map(field => field.reference), + }); + } + +field = whitespace* field:table_field_syntax whatever* endline { return field } + +table_field_syntax + = field_index_syntax + / reference: field_reference_syntax { return ({ reference, isReferences: true })} + / field:field_type_syntax { return ({ field, isField: true }) } + +field_index_syntax = index sp+ whateters +field_reference_syntax = references sp+ reference:reference_value { + return reference; + } +field_type_syntax = type:field_type sp+ name:name { + return ({ + name: name, + type: {type_name: type}, + }) + } + +reference_value = ":"reference:variable { return reference } + / reference:name { return reference } + +referential_actions = "on_delete"i / "on_update"i + +// Keywords +add_index "add index" = "add_index" +schema_define "schema define" = "ActiveRecord::Schema.define" +create_table "create table" = "create_table"i +end_create_table "do |t|" = do sp+ abs character abs endline +index "index" = character ".index" +references "references" = character ".references" +add_foreign_key "add foreign key" = "add_foreign_key"i +column "column" = "column" +primary_key "primary key" = "primary_key" +version = "version" +do = "do" +end = "end" +lambda_function "lambda function" = "=>" / "->" +other_class_prop = variable whateters endline? + +// normal syntax +name = double_quote_name / single_quote_name +double_quote_name = double_quote c:[^\"\n]* double_quote { return c.join("") } +single_quote_name = single_quote c:[^\'\n]* single_quote { return c.join("") } +symbol = ":" c:character* { return c.join("") } +variable = c:(character+) { return c.join("") } +field_type = character"."c:(character+) { return c.join("") } +not_whitespace = !whitespace . {return text()} +number = [0-9]i +character "letter, number or underscore" = [a-z0-9_.]i +end_line = whitespace* end endline? +whatever_line = whitespace* (whateters ! end) endline? +comment_line "comment line" = whitespace* "#" whateters endline? +whateters "whatever" = [^\t\r\n]* +whatever = [^\t\r\n] +single_quote = "'" +double_quote = "\"" + +// Ignored +_ = (comment/whitespace)* +__ = (comment/whitespace)+ + +abs = "|" +endline = sp* newline; +comment "comment" = "//" [^\n]? +newline "newline" = "\r\n" / "\n" +whitespace "whitespace" = [ \t\r\n\r] +sp = " " From 8bf77289c3a7cd9712bf047d845ca7cf18a0491b Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 12:12:49 +0900 Subject: [PATCH 03/16] fix db structure --- .../db-structure/src/schema/database.ts | 59 +++++++++++++++++++ .../packages/db-structure/src/schema/erd.ts | 7 +++ .../packages/db-structure/src/schema/index.ts | 58 +++--------------- 3 files changed, 75 insertions(+), 49 deletions(-) create mode 100644 frontend/packages/db-structure/src/schema/database.ts create mode 100644 frontend/packages/db-structure/src/schema/erd.ts diff --git a/frontend/packages/db-structure/src/schema/database.ts b/frontend/packages/db-structure/src/schema/database.ts new file mode 100644 index 000000000..e9a2840ed --- /dev/null +++ b/frontend/packages/db-structure/src/schema/database.ts @@ -0,0 +1,59 @@ +import * as v from 'valibot' + +const fieldNameSchema = v.string() + +export const tableNameSchema = v.string() + +const relationshipNameSchema = v.string() + +const fieldSchema = v.object({ + name: fieldNameSchema, + type: v.string(), + default: v.string(), + check: v.string(), + primary: v.boolean(), + unique: v.boolean(), + notNull: v.boolean(), + increment: v.boolean(), + comment: v.string(), +}) + +const indexSchema = v.object({ + name: v.string(), + unique: v.boolean(), + fields: v.array(v.string()), +}) + +export const tableSchema = v.object({ + name: tableNameSchema, + fields: v.array(fieldSchema), + comment: v.nullable(v.string()), + indices: v.array(indexSchema), +}) + +export type Table = v.InferOutput + +const tablesSchema = v.record(tableNameSchema, tableSchema) + +const relationshipSchema = v.object({ + name: relationshipNameSchema, + startTableName: tableNameSchema, + startFieldName: fieldNameSchema, + endTableName: tableNameSchema, + endFieldName: fieldNameSchema, + cardinality: v.string(), + updateConstraint: v.string(), + deleteConstraint: v.string(), +}) + +export const relationshipsSchema = v.record( + relationshipNameSchema, + relationshipSchema, +) + +export const dbStructureSchema = v.object({ + tables: tablesSchema, + relationships: relationshipsSchema, +}) + +export type DBStructure = v.InferOutput diff --git a/frontend/packages/db-structure/src/schema/erd.ts b/frontend/packages/db-structure/src/schema/erd.ts new file mode 100644 index 000000000..5d7a2e53b --- /dev/null +++ b/frontend/packages/db-structure/src/schema/erd.ts @@ -0,0 +1,7 @@ +import * as v from 'valibot' + +export const nodeMetaDataSchema = v.object({ + x: v.number(), + y: v.number(), + color: v.string(), +}) diff --git a/frontend/packages/db-structure/src/schema/index.ts b/frontend/packages/db-structure/src/schema/index.ts index bec9f3607..6d29924e4 100644 --- a/frontend/packages/db-structure/src/schema/index.ts +++ b/frontend/packages/db-structure/src/schema/index.ts @@ -1,57 +1,17 @@ import * as v from 'valibot' +import { relationshipsSchema, tableNameSchema, tableSchema } from './database' +import { nodeMetaDataSchema } from './erd' -const fieldNameSchema = v.string() - -const tableNameSchema = v.string() - -const relationshipNameSchema = v.string() - -const fieldSchema = v.object({ - name: fieldNameSchema, - type: v.string(), - default: v.string(), - check: v.string(), - primary: v.boolean(), - unique: v.boolean(), - notNull: v.boolean(), - increment: v.boolean(), - comment: v.string(), -}) - -const indexSchema = v.object({ - name: v.string(), - unique: v.boolean(), - fields: v.array(v.string()), +const nodeTableSchema = v.object({ + ...tableSchema.entries, + ...nodeMetaDataSchema.entries, }) -const tableSchema = v.object({ - name: tableNameSchema, - x: v.number(), - y: v.number(), - fields: v.array(fieldSchema), - comment: v.string(), - indices: v.array(indexSchema), - color: v.string(), -}) - -const relationshipSchema = v.object({ - name: relationshipNameSchema, - startTableName: tableNameSchema, - startFieldName: fieldNameSchema, - endTableName: tableNameSchema, - endFieldName: fieldNameSchema, - cardinality: v.string(), - updateConstraint: v.string(), - deleteConstraint: v.string(), -}) - -const tablesSchema = v.record(tableNameSchema, tableSchema) - -const relationshipsSchema = v.record(relationshipNameSchema, relationshipSchema) +const nodeTablesSchema = v.record(tableNameSchema, nodeTableSchema) -export const dbStructureSchema = v.object({ - tables: tablesSchema, +export const erdStructureSchema = v.object({ + tables: nodeTablesSchema, relationships: relationshipsSchema, }) -export type DBStructure = v.InferOutput +export type ERDStructure = v.InferOutput From 89396858459e617ee2973c01b6a41f7dc1eaf576 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 12:14:37 +0900 Subject: [PATCH 04/16] add paser entrypoint --- .../db-structure/src/parser/index.test.ts | 20 +++++++ .../packages/db-structure/src/parser/index.ts | 52 +++++++++++++++++++ .../db-structure/src/parser/schemarb/index.ts | 1 + .../src/parser/schemarb/input/schema1.in.rb | 3 ++ .../parser/schemarb/output/schema1.out.json | 34 ++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 frontend/packages/db-structure/src/parser/index.test.ts create mode 100644 frontend/packages/db-structure/src/parser/index.ts create mode 100644 frontend/packages/db-structure/src/parser/schemarb/index.ts create mode 100644 frontend/packages/db-structure/src/parser/schemarb/input/schema1.in.rb create mode 100644 frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json diff --git a/frontend/packages/db-structure/src/parser/index.test.ts b/frontend/packages/db-structure/src/parser/index.test.ts new file mode 100644 index 000000000..cc165601d --- /dev/null +++ b/frontend/packages/db-structure/src/parser/index.test.ts @@ -0,0 +1,20 @@ +import fs from 'node:fs' +import path from 'node:path' +import { describe, expect, it } from 'vitest' +import Parser from '.' + +describe('Schema Parser', () => { + it('should parse schema.rb to JSON correctly', () => { + const schemaText = fs.readFileSync( + path.resolve(__dirname, './schemarb/input/schema1.in.rb'), + 'utf-8', + ) + const expectedJson = fs.readFileSync( + path.resolve(__dirname, './schemarb/output/schema1.out.json'), + 'utf-8', + ) + + const result = Parser.parse(schemaText, 'schemarb') + expect(result).toEqual(JSON.parse(expectedJson)) + }) +}) diff --git a/frontend/packages/db-structure/src/parser/index.ts b/frontend/packages/db-structure/src/parser/index.ts new file mode 100644 index 000000000..ae13f4dd7 --- /dev/null +++ b/frontend/packages/db-structure/src/parser/index.ts @@ -0,0 +1,52 @@ +import type { DBStructure, Table } from 'src/schema/database' +import schemarbParser from './schemarb' + +type SupportedFormat = 'schemarb' | 'postgres' + +const convertToDBStructure = (data: any): DBStructure => { + return { + tables: data.tables.reduce((acc: Record, table: any) => { + acc[table.name] = { + comment: null, + fields: table.fields.map((field: any) => ({ + check: null, + comment: null, + default: null, + increment: false, + name: field.name, + notNull: false, + primary: false, + type: field.type.type_name, + unique: false, + })), + indices: [], + name: table.name, + } + return acc + }, {}), + relationships: {}, + } +} + +const selectParser = (format: SupportedFormat): any => { + switch (format) { + case 'schemarb': + return schemarbParser + default: + throw new Error(`Unsupported format: ${format}`) + } +} + +const parse = (str: string, format: SupportedFormat): DBStructure => { + try { + const parser = selectParser(format) + const parsedSchema = parser.parse(str) + const dbStructure = convertToDBStructure(parsedSchema) + return dbStructure + } catch (_error) { + throw new Error('Failed to parse schema') + } +} + +export const Parser = { parse } +export default Parser diff --git a/frontend/packages/db-structure/src/parser/schemarb/index.ts b/frontend/packages/db-structure/src/parser/schemarb/index.ts new file mode 100644 index 000000000..64d4be95e --- /dev/null +++ b/frontend/packages/db-structure/src/parser/schemarb/index.ts @@ -0,0 +1 @@ +export { default } from './parser' diff --git a/frontend/packages/db-structure/src/parser/schemarb/input/schema1.in.rb b/frontend/packages/db-structure/src/parser/schemarb/input/schema1.in.rb new file mode 100644 index 000000000..0c8390136 --- /dev/null +++ b/frontend/packages/db-structure/src/parser/schemarb/input/schema1.in.rb @@ -0,0 +1,3 @@ +create_table "xero_users", id: :uuid, default: -> { "uuid_generate_v4()" }, force: :cascade do |t| + t.string "email_address" +end diff --git a/frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json b/frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json new file mode 100644 index 000000000..155218b33 --- /dev/null +++ b/frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json @@ -0,0 +1,34 @@ +{ + "tables": { + "xero_users": { + "comment": null, + "fields": [ + { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "id", + "notNull": false, + "primary": false, + "type": "varchar", + "unique": false + }, + { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "email_address", + "notNull": false, + "primary": false, + "type": "string", + "unique": false + } + ], + "indices": [], + "name": "xero_users" + } + }, + "relationships": {} +} From d1af15d5fe121ce373c9c8ac0c57aea1bfff1ec2 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 12:15:26 +0900 Subject: [PATCH 05/16] arrange biome and tsconfig --- frontend/packages/db-structure/biome.jsonc | 13 ++++++++++++- frontend/packages/db-structure/tsconfig.json | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/packages/db-structure/biome.jsonc b/frontend/packages/db-structure/biome.jsonc index e6843d979..480c747ba 100644 --- a/frontend/packages/db-structure/biome.jsonc +++ b/frontend/packages/db-structure/biome.jsonc @@ -1,3 +1,14 @@ { - "extends": ["../../packages/configs/biome.jsonc"] + "extends": ["../../packages/configs/biome.jsonc"], + "linter": { + "enabled": true, + "rules": { + "suspicious": { + "noExplicitAny": "off" + } + } + }, + "files": { + "ignore": ["src/parser/schemarb/*"] + } } diff --git a/frontend/packages/db-structure/tsconfig.json b/frontend/packages/db-structure/tsconfig.json index 12273da35..16b87ee8e 100644 --- a/frontend/packages/db-structure/tsconfig.json +++ b/frontend/packages/db-structure/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "@packages/configs/tsconfig/base.json", "compilerOptions": { - "baseUrl": "." + "baseUrl": ".", + "noImplicitAny": false, + "allowJs": true, + "checkJs": false }, "include": ["src/**/*"] } From 6051d8fde69333f03c12866130261f01fe1087c9 Mon Sep 17 00:00:00 2001 From: MH4GF Date: Mon, 18 Nov 2024 14:13:19 +0900 Subject: [PATCH 06/16] docs(biome): Add comment for ignore settings --- frontend/packages/db-structure/biome.jsonc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/packages/db-structure/biome.jsonc b/frontend/packages/db-structure/biome.jsonc index 480c747ba..66bff7adb 100644 --- a/frontend/packages/db-structure/biome.jsonc +++ b/frontend/packages/db-structure/biome.jsonc @@ -9,6 +9,8 @@ } }, "files": { - "ignore": ["src/parser/schemarb/*"] + "ignore": [ + "src/parser/schemarb/parser.js" // Because it's generated + ] } } From 72b688fc9f473f6763b95930e519f9efbe4743f5 Mon Sep 17 00:00:00 2001 From: MH4GF Date: Mon, 18 Nov 2024 14:21:03 +0900 Subject: [PATCH 07/16] fix: Move ignore comment to each line --- frontend/packages/db-structure/biome.jsonc | 8 -------- frontend/packages/db-structure/src/parser/index.ts | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/frontend/packages/db-structure/biome.jsonc b/frontend/packages/db-structure/biome.jsonc index 66bff7adb..76bc7fe03 100644 --- a/frontend/packages/db-structure/biome.jsonc +++ b/frontend/packages/db-structure/biome.jsonc @@ -1,13 +1,5 @@ { "extends": ["../../packages/configs/biome.jsonc"], - "linter": { - "enabled": true, - "rules": { - "suspicious": { - "noExplicitAny": "off" - } - } - }, "files": { "ignore": [ "src/parser/schemarb/parser.js" // Because it's generated diff --git a/frontend/packages/db-structure/src/parser/index.ts b/frontend/packages/db-structure/src/parser/index.ts index ae13f4dd7..ba7a437ea 100644 --- a/frontend/packages/db-structure/src/parser/index.ts +++ b/frontend/packages/db-structure/src/parser/index.ts @@ -3,11 +3,14 @@ import schemarbParser from './schemarb' type SupportedFormat = 'schemarb' | 'postgres' +// biome-ignore lint/suspicious/noExplicitAny: TODO: Generate types with pegjs const convertToDBStructure = (data: any): DBStructure => { return { + // biome-ignore lint/suspicious/noExplicitAny: TODO: Generate types with pegjs tables: data.tables.reduce((acc: Record, table: any) => { acc[table.name] = { comment: null, + // biome-ignore lint/suspicious/noExplicitAny: TODO: Generate types with pegjs fields: table.fields.map((field: any) => ({ check: null, comment: null, @@ -28,6 +31,7 @@ const convertToDBStructure = (data: any): DBStructure => { } } +// biome-ignore lint/suspicious/noExplicitAny: TODO: Generate types with pegjs const selectParser = (format: SupportedFormat): any => { switch (format) { case 'schemarb': From 1403bb263843b585c1ca9c8ce1900a1a7fbddf72 Mon Sep 17 00:00:00 2001 From: MH4GF Date: Mon, 18 Nov 2024 14:29:30 +0900 Subject: [PATCH 08/16] Refactor: change to snapshot testing --- .../parser/__snapshots__/index.test.ts.snap | 75 +++++++++++++++++++ .../db-structure/src/parser/index.test.ts | 6 +- .../parser/schemarb/output/schema1.out.json | 34 --------- 3 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap delete mode 100644 frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json diff --git a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000..6815132df --- /dev/null +++ b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap @@ -0,0 +1,75 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Schema Parser > should parse schema.rb to JSON correctly 1`] = ` +{ + "relationships": {}, + "tables": { + "xero_users": { + "comment": null, + "fields": [ + { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "id", + "notNull": false, + "primary": false, + "type": "varchar", + "unique": false, + }, + { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "email_address", + "notNull": false, + "primary": false, + "type": "string", + "unique": false, + }, + ], + "indices": [], + "name": "xero_users", + }, + }, +} +`; + +exports[`parse > should parse schema.rb to JSON correctly 1`] = ` +{ + "relationships": {}, + "tables": { + "xero_users": { + "comment": null, + "fields": [ + { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "id", + "notNull": false, + "primary": false, + "type": "varchar", + "unique": false, + }, + { + "check": null, + "comment": null, + "default": null, + "increment": false, + "name": "email_address", + "notNull": false, + "primary": false, + "type": "string", + "unique": false, + }, + ], + "indices": [], + "name": "xero_users", + }, + }, +} +`; diff --git a/frontend/packages/db-structure/src/parser/index.test.ts b/frontend/packages/db-structure/src/parser/index.test.ts index cc165601d..01c608c83 100644 --- a/frontend/packages/db-structure/src/parser/index.test.ts +++ b/frontend/packages/db-structure/src/parser/index.test.ts @@ -9,12 +9,8 @@ describe('Schema Parser', () => { path.resolve(__dirname, './schemarb/input/schema1.in.rb'), 'utf-8', ) - const expectedJson = fs.readFileSync( - path.resolve(__dirname, './schemarb/output/schema1.out.json'), - 'utf-8', - ) const result = Parser.parse(schemaText, 'schemarb') - expect(result).toEqual(JSON.parse(expectedJson)) + expect(result).toMatchSnapshot() }) }) diff --git a/frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json b/frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json deleted file mode 100644 index 155218b33..000000000 --- a/frontend/packages/db-structure/src/parser/schemarb/output/schema1.out.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "tables": { - "xero_users": { - "comment": null, - "fields": [ - { - "check": null, - "comment": null, - "default": null, - "increment": false, - "name": "id", - "notNull": false, - "primary": false, - "type": "varchar", - "unique": false - }, - { - "check": null, - "comment": null, - "default": null, - "increment": false, - "name": "email_address", - "notNull": false, - "primary": false, - "type": "string", - "unique": false - } - ], - "indices": [], - "name": "xero_users" - } - }, - "relationships": {} -} From a8408fd700ba2d3839ba6f2c545540bb651236fe Mon Sep 17 00:00:00 2001 From: MH4GF Date: Mon, 18 Nov 2024 14:43:28 +0900 Subject: [PATCH 09/16] Refactor: Refactor to use a named export instead of default export --- frontend/packages/db-structure/src/parser/index.test.ts | 6 +++--- frontend/packages/db-structure/src/parser/index.ts | 9 +++------ .../packages/db-structure/src/parser/schemarb/index.ts | 4 +++- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/frontend/packages/db-structure/src/parser/index.test.ts b/frontend/packages/db-structure/src/parser/index.test.ts index 01c608c83..e0ac89c40 100644 --- a/frontend/packages/db-structure/src/parser/index.test.ts +++ b/frontend/packages/db-structure/src/parser/index.test.ts @@ -1,16 +1,16 @@ import fs from 'node:fs' import path from 'node:path' import { describe, expect, it } from 'vitest' -import Parser from '.' +import { parse } from '.' -describe('Schema Parser', () => { +describe(parse, () => { it('should parse schema.rb to JSON correctly', () => { const schemaText = fs.readFileSync( path.resolve(__dirname, './schemarb/input/schema1.in.rb'), 'utf-8', ) - const result = Parser.parse(schemaText, 'schemarb') + const result = parse(schemaText, 'schemarb') expect(result).toMatchSnapshot() }) }) diff --git a/frontend/packages/db-structure/src/parser/index.ts b/frontend/packages/db-structure/src/parser/index.ts index ba7a437ea..cc0d50788 100644 --- a/frontend/packages/db-structure/src/parser/index.ts +++ b/frontend/packages/db-structure/src/parser/index.ts @@ -1,5 +1,5 @@ import type { DBStructure, Table } from 'src/schema/database' -import schemarbParser from './schemarb' +import { schemaRbParser } from './schemarb' type SupportedFormat = 'schemarb' | 'postgres' @@ -35,13 +35,13 @@ const convertToDBStructure = (data: any): DBStructure => { const selectParser = (format: SupportedFormat): any => { switch (format) { case 'schemarb': - return schemarbParser + return schemaRbParser default: throw new Error(`Unsupported format: ${format}`) } } -const parse = (str: string, format: SupportedFormat): DBStructure => { +export const parse = (str: string, format: SupportedFormat): DBStructure => { try { const parser = selectParser(format) const parsedSchema = parser.parse(str) @@ -51,6 +51,3 @@ const parse = (str: string, format: SupportedFormat): DBStructure => { throw new Error('Failed to parse schema') } } - -export const Parser = { parse } -export default Parser diff --git a/frontend/packages/db-structure/src/parser/schemarb/index.ts b/frontend/packages/db-structure/src/parser/schemarb/index.ts index 64d4be95e..494965880 100644 --- a/frontend/packages/db-structure/src/parser/schemarb/index.ts +++ b/frontend/packages/db-structure/src/parser/schemarb/index.ts @@ -1 +1,3 @@ -export { default } from './parser' +import schemaRbParser from './parser' + +export { schemaRbParser } From 9002d12fa96a911edc946153bc893a4ce22aa495 Mon Sep 17 00:00:00 2001 From: MH4GF Date: Mon, 18 Nov 2024 14:48:59 +0900 Subject: [PATCH 10/16] Refactor: Add ts-nocheck comment to parser.js and remove unnecessary compiler options from tsconfig.json --- frontend/packages/db-structure/src/parser/schemarb/parser.js | 1 + frontend/packages/db-structure/tsconfig.json | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/packages/db-structure/src/parser/schemarb/parser.js b/frontend/packages/db-structure/src/parser/schemarb/parser.js index 5df1a509e..bbe76f0e8 100644 --- a/frontend/packages/db-structure/src/parser/schemarb/parser.js +++ b/frontend/packages/db-structure/src/parser/schemarb/parser.js @@ -1,3 +1,4 @@ +// @ts-nocheck // @generated by Peggy 4.1.1. // // https://peggyjs.org/ diff --git a/frontend/packages/db-structure/tsconfig.json b/frontend/packages/db-structure/tsconfig.json index 16b87ee8e..12273da35 100644 --- a/frontend/packages/db-structure/tsconfig.json +++ b/frontend/packages/db-structure/tsconfig.json @@ -1,10 +1,7 @@ { "extends": "@packages/configs/tsconfig/base.json", "compilerOptions": { - "baseUrl": ".", - "noImplicitAny": false, - "allowJs": true, - "checkJs": false + "baseUrl": "." }, "include": ["src/**/*"] } From cb2131e0c98fdddef26c844f4993ca0246ddb105 Mon Sep 17 00:00:00 2001 From: MH4GF Date: Mon, 18 Nov 2024 14:50:23 +0900 Subject: [PATCH 11/16] fix: remove unused test snapshot --- .../parser/__snapshots__/index.test.ts.snap | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap index 6815132df..479fd4bc5 100644 --- a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap +++ b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap @@ -1,42 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Schema Parser > should parse schema.rb to JSON correctly 1`] = ` -{ - "relationships": {}, - "tables": { - "xero_users": { - "comment": null, - "fields": [ - { - "check": null, - "comment": null, - "default": null, - "increment": false, - "name": "id", - "notNull": false, - "primary": false, - "type": "varchar", - "unique": false, - }, - { - "check": null, - "comment": null, - "default": null, - "increment": false, - "name": "email_address", - "notNull": false, - "primary": false, - "type": "string", - "unique": false, - }, - ], - "indices": [], - "name": "xero_users", - }, - }, -} -`; - exports[`parse > should parse schema.rb to JSON correctly 1`] = ` { "relationships": {}, From 9db07614cadd1e43d4d5c3355c4708f619e8a547 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 15:05:48 +0900 Subject: [PATCH 12/16] undo schema changes --- .../parser/__snapshots__/index.test.ts.snap | 3 + .../packages/db-structure/src/parser/index.ts | 5 +- .../db-structure/src/schema/database.ts | 59 ------------------ .../packages/db-structure/src/schema/erd.ts | 7 --- .../packages/db-structure/src/schema/index.ts | 60 ++++++++++++++++--- 5 files changed, 58 insertions(+), 76 deletions(-) delete mode 100644 frontend/packages/db-structure/src/schema/database.ts delete mode 100644 frontend/packages/db-structure/src/schema/erd.ts diff --git a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap index 479fd4bc5..046d63315 100644 --- a/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap +++ b/frontend/packages/db-structure/src/parser/__snapshots__/index.test.ts.snap @@ -5,6 +5,7 @@ exports[`parse > should parse schema.rb to JSON correctly 1`] = ` "relationships": {}, "tables": { "xero_users": { + "color": null, "comment": null, "fields": [ { @@ -32,6 +33,8 @@ exports[`parse > should parse schema.rb to JSON correctly 1`] = ` ], "indices": [], "name": "xero_users", + "x": 0, + "y": 0, }, }, } diff --git a/frontend/packages/db-structure/src/parser/index.ts b/frontend/packages/db-structure/src/parser/index.ts index cc0d50788..158fb77ef 100644 --- a/frontend/packages/db-structure/src/parser/index.ts +++ b/frontend/packages/db-structure/src/parser/index.ts @@ -1,4 +1,4 @@ -import type { DBStructure, Table } from 'src/schema/database' +import type { DBStructure, Table } from 'src/schema' import { schemaRbParser } from './schemarb' type SupportedFormat = 'schemarb' | 'postgres' @@ -24,6 +24,9 @@ const convertToDBStructure = (data: any): DBStructure => { })), indices: [], name: table.name, + x: 0, + y: 0, + color: null, } return acc }, {}), diff --git a/frontend/packages/db-structure/src/schema/database.ts b/frontend/packages/db-structure/src/schema/database.ts deleted file mode 100644 index e9a2840ed..000000000 --- a/frontend/packages/db-structure/src/schema/database.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as v from 'valibot' - -const fieldNameSchema = v.string() - -export const tableNameSchema = v.string() - -const relationshipNameSchema = v.string() - -const fieldSchema = v.object({ - name: fieldNameSchema, - type: v.string(), - default: v.string(), - check: v.string(), - primary: v.boolean(), - unique: v.boolean(), - notNull: v.boolean(), - increment: v.boolean(), - comment: v.string(), -}) - -const indexSchema = v.object({ - name: v.string(), - unique: v.boolean(), - fields: v.array(v.string()), -}) - -export const tableSchema = v.object({ - name: tableNameSchema, - fields: v.array(fieldSchema), - comment: v.nullable(v.string()), - indices: v.array(indexSchema), -}) - -export type Table = v.InferOutput - -const tablesSchema = v.record(tableNameSchema, tableSchema) - -const relationshipSchema = v.object({ - name: relationshipNameSchema, - startTableName: tableNameSchema, - startFieldName: fieldNameSchema, - endTableName: tableNameSchema, - endFieldName: fieldNameSchema, - cardinality: v.string(), - updateConstraint: v.string(), - deleteConstraint: v.string(), -}) - -export const relationshipsSchema = v.record( - relationshipNameSchema, - relationshipSchema, -) - -export const dbStructureSchema = v.object({ - tables: tablesSchema, - relationships: relationshipsSchema, -}) - -export type DBStructure = v.InferOutput diff --git a/frontend/packages/db-structure/src/schema/erd.ts b/frontend/packages/db-structure/src/schema/erd.ts deleted file mode 100644 index 5d7a2e53b..000000000 --- a/frontend/packages/db-structure/src/schema/erd.ts +++ /dev/null @@ -1,7 +0,0 @@ -import * as v from 'valibot' - -export const nodeMetaDataSchema = v.object({ - x: v.number(), - y: v.number(), - color: v.string(), -}) diff --git a/frontend/packages/db-structure/src/schema/index.ts b/frontend/packages/db-structure/src/schema/index.ts index 6d29924e4..950d80ac8 100644 --- a/frontend/packages/db-structure/src/schema/index.ts +++ b/frontend/packages/db-structure/src/schema/index.ts @@ -1,17 +1,59 @@ import * as v from 'valibot' -import { relationshipsSchema, tableNameSchema, tableSchema } from './database' -import { nodeMetaDataSchema } from './erd' -const nodeTableSchema = v.object({ - ...tableSchema.entries, - ...nodeMetaDataSchema.entries, +const fieldNameSchema = v.string() + +const tableNameSchema = v.string() + +const relationshipNameSchema = v.string() + +const fieldSchema = v.object({ + name: fieldNameSchema, + type: v.string(), + default: v.string(), + check: v.string(), + primary: v.boolean(), + unique: v.boolean(), + notNull: v.boolean(), + increment: v.boolean(), + comment: v.string(), }) -const nodeTablesSchema = v.record(tableNameSchema, nodeTableSchema) +const indexSchema = v.object({ + name: v.string(), + unique: v.boolean(), + fields: v.array(v.string()), +}) + +const tableSchema = v.object({ + name: tableNameSchema, + x: v.number(), + y: v.number(), + fields: v.array(fieldSchema), + comment: v.nullable(v.string()), + indices: v.array(indexSchema), + color: v.nullable(v.string()), +}) + +export type Table = v.InferOutput + +const relationshipSchema = v.object({ + name: relationshipNameSchema, + startTableName: tableNameSchema, + startFieldName: fieldNameSchema, + endTableName: tableNameSchema, + endFieldName: fieldNameSchema, + cardinality: v.string(), + updateConstraint: v.string(), + deleteConstraint: v.string(), +}) + +const tablesSchema = v.record(tableNameSchema, tableSchema) + +const relationshipsSchema = v.record(relationshipNameSchema, relationshipSchema) -export const erdStructureSchema = v.object({ - tables: nodeTablesSchema, +export const dbStructureSchema = v.object({ + tables: tablesSchema, relationships: relationshipsSchema, }) -export type ERDStructure = v.InferOutput +export type DBStructure = v.InferOutput From 022ec8e7941a7699f04ab89c0f46861734e8d28f Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 15:56:35 +0900 Subject: [PATCH 13/16] add lisense notice --- .../src/parser/schemarb/parser.pegjs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs index db3751522..ad827cd30 100644 --- a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs +++ b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs @@ -1,3 +1,21 @@ +// This file is a modified version of schemarb/parser.pegjs from the dbml +// repository (https://github.com/holistics/dbml). +// Last updated: 2024-11-18 + +// Copyright 2019 Holistics Software Pte Ltd. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + { var pluralize = require('pluralize'); var lodash = require('lodash'); From 8d86366ad35c2613e29919f493655c33aa157727 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 15:57:27 +0900 Subject: [PATCH 14/16] fix syntax --- .../packages/db-structure/src/parser/schemarb/parser.pegjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs index ad827cd30..946eaa8c7 100644 --- a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs +++ b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs @@ -121,7 +121,7 @@ endpoints[0].fieldNames = [columnName]; const columnField = fromTable.fields.find(field => field.name === columnName); if (!columnField) { - // TODO: handle erro + // TODO: handle error // throw { // message: `Field ${columnName} not found in table ${endpoints[0].table}` // } @@ -365,7 +365,7 @@ primary_key "primary key" = "primary_key" version = "version" do = "do" end = "end" -lambda_function "lambda function" = "=>" / "->" +lambda_function "lambda function" = "->" other_class_prop = variable whateters endline? // normal syntax From b8c0606de60ef0f5ecbf821ba54046c1a15463c5 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 16:05:47 +0900 Subject: [PATCH 15/16] install single function from lodash --- frontend/packages/db-structure/package.json | 3 ++- .../src/parser/schemarb/parser.pegjs | 5 +++-- frontend/pnpm-lock.yaml | 19 ++++++++++++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/frontend/packages/db-structure/package.json b/frontend/packages/db-structure/package.json index 2f204cf34..455a35e77 100644 --- a/frontend/packages/db-structure/package.json +++ b/frontend/packages/db-structure/package.json @@ -11,7 +11,8 @@ "test": "vitest" }, "dependencies": { - "lodash": "^4.17.21", + "lodash.isequal": "^4.5.0", + "lodash.sortby": "^4.7.0", "pluralize": "^8.0.0", "valibot": "^1.0.0-beta.5" }, diff --git a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs index 946eaa8c7..9b4c710c8 100644 --- a/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs +++ b/frontend/packages/db-structure/src/parser/schemarb/parser.pegjs @@ -18,7 +18,8 @@ { var pluralize = require('pluralize'); - var lodash = require('lodash'); + var isEqual = require('lodash/isEqual'); + var sortBy = require('lodash/sortBy'); let data = { tables: [], @@ -67,7 +68,7 @@ function isSameEndpoints (endpoint1, endpoint2) { return endpoint1.tableName == endpoint2.tableName && - lodash.isEqual(lodash.sortBy(endpoint1.fieldNames), lodash.sortBy(endpoint2.fieldNames)) + isEqual(sortBy(endpoint1.fieldNames), sortBy(endpoint2.fieldNames)) } function isSameEndpointsPairs (endpointsPair1, endpointsPair2) { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index b3e224e6f..563e440cd 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -84,9 +84,12 @@ importers: packages/db-structure: dependencies: - lodash: - specifier: ^4.17.21 - version: 4.17.21 + lodash.isequal: + specifier: ^4.5.0 + version: 4.5.0 + lodash.sortby: + specifier: ^4.7.0 + version: 4.7.0 pluralize: specifier: ^8.0.0 version: 8.0.0 @@ -2266,6 +2269,12 @@ packages: lodash.get@4.4.2: resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -5583,6 +5592,10 @@ snapshots: lodash.get@4.4.2: {} + lodash.isequal@4.5.0: {} + + lodash.sortby@4.7.0: {} + lodash@4.17.21: {} log-symbols@3.0.0: From 55855473dff58a4608e01737ce7184e04bfbddf0 Mon Sep 17 00:00:00 2001 From: "ryota.sasazawa" Date: Mon, 18 Nov 2024 16:12:48 +0900 Subject: [PATCH 16/16] ts version fixed --- frontend/packages/db-structure/package.json | 2 +- frontend/pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/packages/db-structure/package.json b/frontend/packages/db-structure/package.json index 455a35e77..bb4aeb857 100644 --- a/frontend/packages/db-structure/package.json +++ b/frontend/packages/db-structure/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@biomejs/biome": "1.9.3", "@packages/configs": "workspace:*", - "typescript": "^5.6.2", + "typescript": "^5", "vitest": "^2.1.4" } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 563e440cd..4db9cb046 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -104,7 +104,7 @@ importers: specifier: workspace:* version: link:../configs typescript: - specifier: ^5.6.2 + specifier: ^5 version: 5.6.2 vitest: specifier: ^2.1.4