diff --git a/packages/engines/default/SqlDumper.js b/packages/engines/default/SqlDumper.js index 295dd67e7..e7c849074 100644 --- a/packages/engines/default/SqlDumper.js +++ b/packages/engines/default/SqlDumper.js @@ -1,30 +1,39 @@ class SqlDumper { /** @param driver {import('@dbgate/types').EngineDriver} */ constructor(driver) { - this.s = ''; + this.s = ""; this.driver = driver; this.dialect = driver.dialect; + this.indentLevel = 0; + } + endCommand() { + this.putRaw(";\n"); } putRaw(text) { this.s += text; } putCmd(format, ...args) { this.put(format, ...args); - this.putRaw(';\n'); + this.endCommand(); } putFormattedValue(c, value) { switch (c) { - case 's': + case "s": if (value != null) { this.putRaw(value.toString()); } break; - case 'f': + case "i": + { + this.putRaw(this.dialect.quoteIdentifier(value)); + } + break; + case "f": { const { schemaName, pureName } = value; if (schemaName) { this.putRaw(this.dialect.quoteIdentifier(schemaName)); - this.putRaw('.'); + this.putRaw("."); } this.putRaw(this.dialect.quoteIdentifier(pureName)); } @@ -40,18 +49,37 @@ class SqlDumper { let c = format[i]; i++; switch (c) { - case '^': - while (i < length && format[i].match(/[a-z]/i)) { + case "^": + while (i < length && format[i].match(/[a-z0-9_]/i)) { this.putRaw(format[i].toUpperCase()); i++; } break; - case '%': + case "%": c = format[i]; i++; this.putFormattedValue(c, args[argIndex]); argIndex++; break; + case "&": + c = format[i]; + i++; + switch (c) { + case "&": + this.putRaw("&"); + break; + case ">": + this.indentLevel++; + break; + case "<": + this.indentLevel--; + break; + case "n": + this.putRaw("\n"); + this.putRaw(" ".repeat(2 * this.indentLevel)); + break; + } + break; default: this.putRaw(c); @@ -59,6 +87,123 @@ class SqlDumper { } } } + autoIncrement() { + this.put(" ^auto_increment"); + } + + /** + * @param column {import('@dbgate/types').ColumnInfo} + */ + columnDefinition( + column, + { + includeDefault = true, + includeNullable = true, + includeCollate = true + } = {} + ) { + if (column.computedExpression) { + this.put("^as %s", column.computedExpression); + if (column.isPersisted) this.put(" ^persisted"); + return; + } + this.put("%k", column.dataType); + if (column.autoIncrement) { + this.autoIncrement(); + } + + this.putRaw(" "); + if (column.isSparse) { + this.put(" ^sparse "); + } + if (includeNullable) { + this.put(column.notNull ? "^not ^null" : "^null"); + } + if (includeDefault && column.defaultValue != null) { + this.columnDefault(column); + } + } + + /** + * @param column {import('@dbgate/types').ColumnInfo} + */ + columnDefault(column) { + if (column.defaultConstraint != null) { + this.put( + " ^constraint %i ^default %s ", + column.defaultConstraint, + column.defaultValue + ); + } else { + this.put(" ^default %s ", column.defaultValue); + } + } + + /** + * @template T + * @param {string} delimiter + * @param {T[]} collection + * @param {(col: T) => void} lambda + */ + putCollection(delimiter, collection, lambda) { + let first = true; + for (const item of collection) { + if (!first) this.put(delimiter); + first = false; + lambda(item); + } + } + /** @param table {import('@dbgate/types').TableInfo} */ + createTable(table) { + this.put("^create ^table %f ( &>&n", table); + this.putCollection(", &n", table.columns, col => { + this.put("%i", col.columnName); + this.columnDefinition(col); + }); + // bool first = true; + // _primaryKeyWrittenInCreateTable = false; + // foreach (var col in table.Columns) + // { + // if (!first) Put(", &n"); + // first = false; + // Put("%i ", col.Name); + // ColumnDefinition(col, true, true, true); + // } + // if (table.PrimaryKey != null && !_primaryKeyWrittenInCreateTable) + // { + // if (!first) Put(", &n"); + // first = false; + // if (table.PrimaryKey.ConstraintName != null) + // { + // Put("^constraint %i", table.PrimaryKey.ConstraintName); + // } + // Put(" ^primary ^key (%,i)", table.PrimaryKey.Columns); + // } + // foreach (var cnt in table.ForeignKeys) + // { + // if (!first) Put(", &n"); + // first = false; + // CreateForeignKeyCore(cnt); + // } + // foreach (var cnt in table.Uniques) + // { + // if (!first) Put(", &n"); + // first = false; + // CreateUniqueCore(cnt); + // } + // foreach (var cnt in table.Checks) + // { + // if (!first) Put(", &n"); + // first = false; + // CreateCheckCore(cnt); + // } + this.put("&<&n)"); + this.endCommand(); + // foreach (var ix in table.Indexes) + // { + // CreateIndex(ix); + // } + } } module.exports = SqlDumper; diff --git a/packages/types/dumper.d.ts b/packages/types/dumper.d.ts index f3aab2db6..617968f12 100644 --- a/packages/types/dumper.d.ts +++ b/packages/types/dumper.d.ts @@ -1,5 +1,9 @@ +import { TableInfo } from "./dbinfo"; + export interface SqlDumper { s: string; put(format: string, ...args); putCmd(format: string, ...args); + endCommand(); + createTable(table: TableInfo); } diff --git a/packages/web/src/tabs/TableCreateScriptTab.js b/packages/web/src/tabs/TableCreateScriptTab.js index 4025b6c5a..d5c6215e6 100644 --- a/packages/web/src/tabs/TableCreateScriptTab.js +++ b/packages/web/src/tabs/TableCreateScriptTab.js @@ -5,6 +5,7 @@ import theme from '../theme'; import AceEditor from 'react-ace'; import useDimensions from '../utility/useDimensions'; import engines from '@dbgate/engines'; +import useTableInfo from '../utility/useTableInfo'; const Wrapper = styled.div` position: absolute; @@ -15,19 +16,15 @@ const Wrapper = styled.div` `; export default function TableCreateScriptTab({ conid, database, schemaName, pureName }) { - const sql = `SELECT * FROM MOJE`; const [containerRef, { height, width }] = useDimensions(); - /** @type {import('@dbgate/types').TableInfo} */ - const tableInfo = useFetch({ - url: 'tables/table-info', - params: { conid, database, schemaName, pureName }, - }); + const tableInfo = useTableInfo({ conid, database, schemaName, pureName }); + + console.log(tableInfo); - /** @type {import('@dbgate/types').EngineDriver} */ const driver = engines('mssql'); const dumper = driver.createDumper(); - dumper.putCmd('^select * ^from %f', { schemaName, pureName }); + if (tableInfo) dumper.createTable(tableInfo); return ( diff --git a/packages/web/src/tabs/TableStructureTab.js b/packages/web/src/tabs/TableStructureTab.js index 09ac6339a..5fdda944d 100644 --- a/packages/web/src/tabs/TableStructureTab.js +++ b/packages/web/src/tabs/TableStructureTab.js @@ -7,6 +7,7 @@ import ObjectListControl from '../utility/ObjectListControl'; import { TableColumn } from '../utility/TableControl'; import columnAppObject from '../appobj/columnAppObject'; import constraintAppObject from '../appobj/constraintAppObject'; +import useTableInfo from '../utility/useTableInfo'; const WhitePage = styled.div` position: absolute; @@ -18,11 +19,7 @@ const WhitePage = styled.div` `; export default function TableStructureTab({ conid, database, schemaName, pureName }) { - /** @type {import('@dbgate/types').TableInfo} */ - const tableInfo = useFetch({ - url: 'tables/table-info', - params: { conid, database, schemaName, pureName }, - }); + const tableInfo = useTableInfo({ conid, database, schemaName, pureName }); if (!tableInfo) return null; const { columns, primaryKey, foreignKeys, dependencies } = tableInfo; return ( diff --git a/packages/web/src/utility/useTableInfo.js b/packages/web/src/utility/useTableInfo.js new file mode 100644 index 000000000..225381d6b --- /dev/null +++ b/packages/web/src/utility/useTableInfo.js @@ -0,0 +1,10 @@ +import useFetch from './useFetch'; + +export default function useTableInfo({ conid, database, schemaName, pureName }) { + /** @type {import('@dbgate/types').TableInfo} */ + const tableInfo = useFetch({ + url: 'tables/table-info', + params: { conid, database, schemaName, pureName }, + }); + return tableInfo; +}