diff --git a/src/components/table.jsx b/src/components/table.jsx
index a82c51a..293bd19 100644
--- a/src/components/table.jsx
+++ b/src/components/table.jsx
@@ -41,6 +41,7 @@ import {
TableContext,
UndoRedoContext,
} from "../pages/editor";
+import { getSize, hasPrecision, isSized } from "../utils";
export default function Table(props) {
const [isHovered, setIsHovered] = useState(false);
@@ -380,34 +381,35 @@ export default function Table(props) {
if (value === "ENUM" || value === "SET") {
updateField(props.tableData.id, j, {
type: value,
+ default: "",
values: [],
increment: incr,
});
- } else if (value === "VARCHAR") {
+ } else if (isSized(value) || hasPrecision(value)) {
updateField(props.tableData.id, j, {
type: value,
- length: 255,
+ size: getSize(value),
increment: incr,
});
} else if (
- f.type === "BLOB" ||
- f.type === "JSON" ||
- f.type === "GEOMETRY" ||
- f.type === "TEXT" ||
+ value === "BLOB" ||
+ value === "JSON" ||
+ value === "UUID" ||
+ value === "TEXT" ||
incr
) {
updateField(props.tableData.id, j, {
type: value,
increment: incr,
default: "",
- length: "",
+ size: "",
values: [],
});
} else {
updateField(props.tableData.id, j, {
type: value,
increment: incr,
- length: "",
+ size: "",
values: [],
});
}
@@ -481,8 +483,8 @@ export default function Table(props) {
disabled={
f.type === "BLOB" ||
f.type === "JSON" ||
- f.type === "GEOMETRY" ||
f.type === "TEXT" ||
+ f.type === "UUID" ||
f.increment
}
onChange={(value) =>
@@ -556,26 +558,23 @@ export default function Table(props) {
/>
>
)}
- {(f.type === "VARCHAR" || f.type === "CHAR") && (
+ {isSized(f.type) && (
<>
-
Length
+ Size
updateField(props.tableData.id, j, {
- length: value,
+ size: value,
})
}
onFocus={(e) =>
- setEditField({ length: e.target.value })
+ setEditField({ size: e.target.value })
}
onBlur={(e) => {
- if (e.target.value === editField.length) return;
+ if (e.target.value === editField.size) return;
setUndoStack((prev) => [
...prev,
{
@@ -585,8 +584,48 @@ export default function Table(props) {
tid: props.tableData.id,
fid: j,
undo: editField,
- redo: { length: e.target.value },
- message: `Edit table field length to "${e.target.value}"`,
+ redo: { size: e.target.value },
+ message: `Edit table field size to ${e.target.value}`,
+ },
+ ]);
+ setRedoStack([]);
+ }}
+ />
+ >
+ )}
+ {hasPrecision(f.type) && (
+ <>
+ Precision
+
+ updateField(props.tableData.id, j, {
+ size: value,
+ })
+ }
+ onFocus={(e) =>
+ setEditField({ size: e.target.value })
+ }
+ onBlur={(e) => {
+ if (e.target.value === editField.size) return;
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.TABLE,
+ component: "field",
+ tid: props.tableData.id,
+ fid: j,
+ undo: editField,
+ redo: { size: e.target.value },
+ message: `Edit table field precision to ${e.target.value}`,
},
]);
setRedoStack([]);
diff --git a/src/components/table_overview.jsx b/src/components/table_overview.jsx
index 062355b..c204b43 100644
--- a/src/components/table_overview.jsx
+++ b/src/components/table_overview.jsx
@@ -36,6 +36,7 @@ import {
IllustrationNoContentDark,
} from "@douyinfe/semi-illustrations";
import { SelectContext, TableContext, UndoRedoContext } from "../pages/editor";
+import { getSize, hasPrecision, isSized } from "../utils";
export default function TableOverview(props) {
const [indexActiveKey, setIndexActiveKey] = useState("");
@@ -226,34 +227,35 @@ export default function TableOverview(props) {
if (value === "ENUM" || value === "SET") {
updateField(i, j, {
type: value,
+ default: "",
values: [],
increment: incr,
});
- } else if (value === "VARCHAR") {
+ } else if (isSized(value) || hasPrecision(value)) {
updateField(i, j, {
type: value,
- length: 255,
+ size: getSize(value),
increment: incr,
});
} else if (
- f.type === "BLOB" ||
- f.type === "JSON" ||
- f.type === "GEOMETRY" ||
- f.type === "TEXT" ||
+ value === "BLOB" ||
+ value === "JSON" ||
+ value === "UUID" ||
+ value === "TEXT" ||
incr
) {
- updateField(props.tableData.id, j, {
+ updateField(t.id, j, {
type: value,
increment: incr,
default: "",
- length: "",
+ size: "",
values: [],
});
} else {
updateField(i, j, {
type: value,
increment: incr,
- length: "",
+ size: "",
values: [],
});
}
@@ -327,8 +329,8 @@ export default function TableOverview(props) {
disabled={
f.type === "BLOB" ||
f.type === "JSON" ||
- f.type === "GEOMETRY" ||
f.type === "TEXT" ||
+ f.type === "UUID" ||
f.increment
}
onChange={(value) =>
@@ -403,24 +405,21 @@ export default function TableOverview(props) {
/>
>
)}
- {(f.type === "VARCHAR" || f.type === "CHAR") && (
+ {isSized(f.type) && (
<>
- Length
+ Size
- updateField(i, j, { length: value })
+ updateField(i, j, { size: value })
}
onFocus={(e) =>
- setEditField({ length: e.target.value })
+ setEditField({ size: e.target.value })
}
onBlur={(e) => {
- if (e.target.value === editField.length)
+ if (e.target.value === editField.size)
return;
setUndoStack((prev) => [
...prev,
@@ -431,8 +430,47 @@ export default function TableOverview(props) {
tid: i,
fid: j,
undo: editField,
- redo: { length: e.target.value },
- message: `Edit table field length to ${e.target.value}`,
+ redo: { size: e.target.value },
+ message: `Edit table field size to ${e.target.value}`,
+ },
+ ]);
+ setRedoStack([]);
+ }}
+ />
+ >
+ )}
+ {hasPrecision(f.type) && (
+ <>
+ Precision
+
+ updateField(i, j, { size: value })
+ }
+ onFocus={(e) =>
+ setEditField({ size: e.target.value })
+ }
+ onBlur={(e) => {
+ if (e.target.value === editField.size)
+ return;
+ setUndoStack((prev) => [
+ ...prev,
+ {
+ action: Action.EDIT,
+ element: ObjectType.TABLE,
+ component: "field",
+ tid: i,
+ fid: j,
+ undo: editField,
+ redo: { size: e.target.value },
+ message: `Edit table field precision to ${e.target.value}`,
},
]);
setRedoStack([]);
@@ -601,11 +639,9 @@ export default function TableOverview(props) {
.filter(
(e) =>
!(
- (e.startTableId ===
- props.tableData.id &&
+ (e.startTableId === t.id &&
e.startFieldId === j) ||
- (e.endTableId ===
- props.tableData.id &&
+ (e.endTableId === t.id &&
e.endFieldId === j)
)
)
diff --git a/src/data/data.js b/src/data/data.js
index 6a202f1..d1f04fd 100644
--- a/src/data/data.js
+++ b/src/data/data.js
@@ -5,20 +5,21 @@ const sqlDataTypes = [
"DECIMAL",
"NUMERIC",
"FLOAT",
+ "DOUBLE",
"REAL",
- "DOUBLE PRECISION",
"CHAR",
"VARCHAR",
"TEXT",
"DATE",
"TIME",
"TIMESTAMP",
- "INTERVAL",
+ "DATETIME",
"BOOLEAN",
"BINARY",
"VARBINARY",
"BLOB",
"JSON",
+ "UUID",
"ENUM",
"SET",
];
diff --git a/src/utils/index.js b/src/utils/index.js
index f3550a5..1676767 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -47,6 +47,36 @@ function dataURItoBlob(dataUrl) {
return new Blob([intArray], { type: mimeString });
}
+function getTypeString(field) {
+ if (field.type === "UUID") {
+ return `VARCHAR(36)`;
+ }
+ if (isSized(field.type)) {
+ return `${field.type}(${field.size})`;
+ }
+ if (hasPrecision(field.type) && field.size !== "") {
+ return `${field.type}${field.size}`;
+ }
+ if (field.type === "SET" || field.type === "ENUM") {
+ return `${field.type}(${field.values.map((v) => `"${v}"`).join(", ")})`;
+ }
+ return field.type;
+}
+
+function hasQuotes(type) {
+ return [
+ "CHAR",
+ "VARCHAR",
+ "BINARY",
+ "VARBINARY",
+ "ENUM",
+ "DATE",
+ "TIME",
+ "TIMESTAMP",
+ "DATETIME",
+ ].includes(type);
+}
+
function jsonToSQL(obj) {
return `${obj.tables
.map(
@@ -58,18 +88,12 @@ function jsonToSQL(obj) {
(field) =>
`${field.comment === "" ? "" : `\t-- ${field.comment}\n`}\t\`${
field.name
- }\` ${field.type}${
- field.type === "VARCHAR"
- ? `(${field.length})`
- : field.type === "ENUM" || field.type === "SET"
- ? `(${field.values.map((v) => `"${v}"`).join(", ")})`
- : ""
- }${field.notNull ? " NOT NULL" : ""}${
+ }\` ${getTypeString(field)}${field.notNull ? " NOT NULL" : ""}${
field.increment ? " AUTO_INCREMENT" : ""
}${field.unique ? " UNIQUE" : ""}${
field.default !== ""
? ` DEFAULT ${
- (field.type === "VARCHAR" || field.type === "ENUM") &&
+ hasQuotes(field.type) &&
field.default.toLowerCase() !== "null"
? `"${field.default}"`
: `${field.default}`
@@ -115,6 +139,37 @@ function arrayIsEqual(arr1, arr2) {
return JSON.stringify(arr1) === JSON.stringify(arr2);
}
+function isSized(type) {
+ return ["CHAR", "VARCHAR", "BINARY", "VARBINARY", "TEXT", "FLOAT"].includes(
+ type
+ );
+}
+
+function hasPrecision(type) {
+ return ["DOUBLE", "NUMERIC", "DECIMAL"].includes(type);
+}
+
+function getSize(type) {
+ switch (type) {
+ case "CHAR":
+ case "BINARY":
+ return 1;
+ case "VARCHAR":
+ case "VARBINARY":
+ return 255;
+ case "TEXT":
+ return 65535;
+ default:
+ return "";
+ }
+}
+
+function validateDateStr(str) {
+ return /^(?!0000)(?!00)(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[01])|(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31))$/.test(
+ str
+ );
+}
+
function checkDefault(field) {
if (field.default === "") return true;
@@ -122,17 +177,51 @@ function checkDefault(field) {
case "INT":
case "BIGINT":
case "SMALLINT":
- return /^\d*$/.test(field.default);
+ return /^-?\d*$/.test(field.default);
case "ENUM":
case "SET":
return field.values.includes(field.default);
case "CHAR":
case "VARCHAR":
- return field.default.length <= field.length;
+ return field.default.length <= field.size;
+ case "BINARY":
+ case "VARBINARY":
+ return (
+ field.default.length <= field.size && /^[01]+$/.test(field.default)
+ );
case "BOOLEAN":
return (
field.default.trim() === "false" || field.default.trim() === "true"
);
+ case "FLOAT":
+ case "DECIMAL":
+ case "DOUBLE":
+ case "NUMERIC":
+ case "REAL":
+ return /^-?\d*.?\d+$/.test(field.default);
+ case "DATE":
+ return validateDateStr(field.default);
+ case "TIME":
+ return /^(?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d$/.test(field.default);
+ case "TIMESTAMP":
+ if (field.default.toUpperCase() === "CURRENT_TIMESTAMP") {
+ return true;
+ }
+ if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default)) {
+ return false;
+ }
+ const content = field.default.split(" ");
+ const date = content[0].split("-");
+
+ return parseInt(date[0]) >= 1970 && parseInt(date[0]) <= 2038;
+ case "DATETIME":
+ if (!/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(field.default)) {
+ return false;
+ }
+ const c = field.default.split(" ");
+ const d = c[0].split("-");
+
+ return parseInt(d[0]) >= 1000 && parseInt(d[0]) <= 9999;
default:
return true;
}
@@ -180,6 +269,18 @@ function validateDiagram(diagram) {
);
}
+ if (field.notNull && field.default.toLowerCase() === "null") {
+ issues.push(
+ `"${field.name}" field of table "${table.name}" is NOT NULL but has default NULL`
+ );
+ }
+
+ if (field.type === "DOUBLE" && field.size !== "") {
+ issues.push(
+ `Specifying number of digits for floating point data types is deprecated.`
+ );
+ }
+
if (duplicateFieldNames[field.name]) {
issues.push(`Duplicate table fields in table "${table.name}"`);
} else {
@@ -254,4 +355,8 @@ export {
jsonToSQL,
validateDiagram,
arrayIsEqual,
+ isSized,
+ getSize,
+ hasPrecision,
+ validateDateStr,
};