velocious 1.0.566 → 1.0.568
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/bin/velocious.js +28 -1
- package/build/bin/velocious.js +28 -1
- package/build/database/drivers/base-table.js +4 -3
- package/build/database/drivers/base.js +28 -0
- package/build/database/drivers/index-metadata.js +28 -0
- package/build/database/drivers/mssql/column.js +2 -34
- package/build/database/drivers/mssql/columns-index.js +37 -0
- package/build/database/drivers/mssql/table.js +55 -11
- package/build/database/drivers/mysql/sql/alter-table.js +13 -0
- package/build/database/drivers/pgsql/column.js +2 -27
- package/build/database/drivers/pgsql/columns-index.js +37 -0
- package/build/database/drivers/pgsql/table.js +61 -10
- package/build/database/drivers/sqlite/sql/alter-table.js +18 -0
- package/build/database/migration/index.js +92 -3
- package/build/database/query/alter-table-base.js +20 -1
- package/build/database/record/attachments/normalize-input.js +38 -10
- package/build/database/record/attachments/storage-drivers/filesystem.js +33 -2
- package/build/database/record/attachments/storage-drivers/native.js +17 -2
- package/build/database/record/attachments/storage-drivers/s3.js +27 -7
- package/build/database/record/attachments/store.js +159 -43
- package/build/database/table-data/table-foreign-key.js +9 -1
- package/build/environment-handlers/base.js +1 -1
- package/build/environment-handlers/node/attachment-path-source.js +144 -0
- package/build/environment-handlers/node.js +28 -3
- package/build/src/database/drivers/base-table.d.ts.map +1 -1
- package/build/src/database/drivers/base-table.js +4 -3
- package/build/src/database/drivers/base.d.ts +7 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +24 -1
- package/build/src/database/drivers/index-metadata.d.ts +41 -0
- package/build/src/database/drivers/index-metadata.d.ts.map +1 -0
- package/build/src/database/drivers/index-metadata.js +26 -0
- package/build/src/database/drivers/mssql/column.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/column.js +3 -31
- package/build/src/database/drivers/mssql/columns-index.d.ts +41 -0
- package/build/src/database/drivers/mssql/columns-index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/columns-index.js +35 -1
- package/build/src/database/drivers/mssql/table.d.ts +42 -0
- package/build/src/database/drivers/mssql/table.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/table.js +49 -12
- package/build/src/database/drivers/mysql/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/alter-table.js +12 -1
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +3 -24
- package/build/src/database/drivers/pgsql/columns-index.d.ts +41 -0
- package/build/src/database/drivers/pgsql/columns-index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/columns-index.js +35 -1
- package/build/src/database/drivers/pgsql/table.d.ts +42 -0
- package/build/src/database/drivers/pgsql/table.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/table.js +54 -11
- package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +15 -1
- package/build/src/database/migration/index.d.ts +35 -1
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +78 -4
- package/build/src/database/query/alter-table-base.d.ts +6 -0
- package/build/src/database/query/alter-table-base.d.ts.map +1 -1
- package/build/src/database/query/alter-table-base.js +18 -2
- package/build/src/database/record/attachments/normalize-input.d.ts +33 -4
- package/build/src/database/record/attachments/normalize-input.d.ts.map +1 -1
- package/build/src/database/record/attachments/normalize-input.js +39 -11
- package/build/src/database/record/attachments/storage-drivers/filesystem.d.ts +2 -5
- package/build/src/database/record/attachments/storage-drivers/filesystem.d.ts.map +1 -1
- package/build/src/database/record/attachments/storage-drivers/filesystem.js +28 -3
- package/build/src/database/record/attachments/storage-drivers/native.d.ts +2 -6
- package/build/src/database/record/attachments/storage-drivers/native.d.ts.map +1 -1
- package/build/src/database/record/attachments/storage-drivers/native.js +14 -3
- package/build/src/database/record/attachments/storage-drivers/s3.d.ts +2 -6
- package/build/src/database/record/attachments/storage-drivers/s3.d.ts.map +1 -1
- package/build/src/database/record/attachments/storage-drivers/s3.js +29 -8
- package/build/src/database/record/attachments/store.d.ts +27 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +144 -44
- package/build/src/database/table-data/table-foreign-key.d.ts +9 -1
- package/build/src/database/table-data/table-foreign-key.d.ts.map +1 -1
- package/build/src/database/table-data/table-foreign-key.js +9 -2
- package/build/src/environment-handlers/base.d.ts +2 -5
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +2 -2
- package/build/src/environment-handlers/node/attachment-path-source.d.ts +64 -0
- package/build/src/environment-handlers/node/attachment-path-source.d.ts.map +1 -0
- package/build/src/environment-handlers/node/attachment-path-source.js +116 -0
- package/build/src/environment-handlers/node.d.ts +12 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +24 -4
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/database/drivers/base-table.js +4 -3
- package/src/database/drivers/base.js +28 -0
- package/src/database/drivers/index-metadata.js +28 -0
- package/src/database/drivers/mssql/column.js +2 -34
- package/src/database/drivers/mssql/columns-index.js +37 -0
- package/src/database/drivers/mssql/table.js +55 -11
- package/src/database/drivers/mysql/sql/alter-table.js +13 -0
- package/src/database/drivers/pgsql/column.js +2 -27
- package/src/database/drivers/pgsql/columns-index.js +37 -0
- package/src/database/drivers/pgsql/table.js +61 -10
- package/src/database/drivers/sqlite/sql/alter-table.js +18 -0
- package/src/database/migration/index.js +92 -3
- package/src/database/query/alter-table-base.js +20 -1
- package/src/database/record/attachments/normalize-input.js +38 -10
- package/src/database/record/attachments/storage-drivers/filesystem.js +33 -2
- package/src/database/record/attachments/storage-drivers/native.js +17 -2
- package/src/database/record/attachments/storage-drivers/s3.js +27 -7
- package/src/database/record/attachments/store.js +159 -43
- package/src/database/table-data/table-foreign-key.js +9 -1
- package/src/environment-handlers/base.js +1 -1
- package/src/environment-handlers/node/attachment-path-source.js +144 -0
- package/src/environment-handlers/node.js +28 -3
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* AbortSignal-driven MySQL/MariaDB query cancellation for raw, model, and cross-tenant aggregate queries (see [docs/database-query-cancellation.md](docs/database-query-cancellation.md))
|
|
38
38
|
* Optional built-in debug endpoint for inspecting server and database connection state (see [docs/debug-endpoint.md](docs/debug-endpoint.md))
|
|
39
39
|
* Optional built-in API manifest endpoint describing every registered frontend-model resource as human- and machine-readable JSON (see [docs/api-manifest-endpoint.md](docs/api-manifest-endpoint.md))
|
|
40
|
+
* Backend record attachments with filesystem, S3, native callback, and bounded Node path-input persistence (see [docs/attachments.md](docs/attachments.md))
|
|
40
41
|
|
|
41
42
|
# Setup
|
|
42
43
|
|
|
@@ -521,6 +522,9 @@ Task.hasOneAttachment("descriptionFile")
|
|
|
521
522
|
Task.hasOneAttachment("archivedPdf", {driver: "s3"})
|
|
522
523
|
```
|
|
523
524
|
|
|
525
|
+
See [Backend record attachments](docs/attachments.md) for the complete input,
|
|
526
|
+
storage-driver, lifecycle, and path-security contracts.
|
|
527
|
+
|
|
524
528
|
You can also pass a driver class or instance directly on the attachment:
|
|
525
529
|
|
|
526
530
|
```js
|
|
@@ -538,6 +542,10 @@ await task.descriptionFile().attach({
|
|
|
538
542
|
content: "my file content",
|
|
539
543
|
filename: "file.doc"
|
|
540
544
|
})
|
|
545
|
+
await task.archivedPdf().attach({
|
|
546
|
+
path: "/var/app/uploads/archive.pdf",
|
|
547
|
+
contentType: "application/pdf"
|
|
548
|
+
})
|
|
541
549
|
const descriptionFileUrl = await task.descriptionFile().url()
|
|
542
550
|
await task.update({
|
|
543
551
|
descriptionFile: {
|
|
@@ -605,7 +613,25 @@ new Configuration({
|
|
|
605
613
|
```
|
|
606
614
|
|
|
607
615
|
Then `{path: "..."}`
|
|
608
|
-
inputs are only accepted when the
|
|
616
|
+
inputs are only accepted when the path resolves inside one of the allowed
|
|
617
|
+
prefixes and the once-opened handle identifies a regular file. Its exact byte
|
|
618
|
+
size comes from that handle's stat snapshot. The filesystem driver copies it
|
|
619
|
+
with a bounded, backpressured stream and the S3 driver sends a Node `Readable`;
|
|
620
|
+
on current nullable schemas neither driver first materializes the whole file as
|
|
621
|
+
a Buffer or Base64 value. Legacy schemas with a non-null `content_base64`
|
|
622
|
+
column instead materialize the opened snapshot once before driver persistence
|
|
623
|
+
and reuse those exact bytes for storage and the database Base64. Path
|
|
624
|
+
replacement cannot switch the opened source, truncation is rejected, later
|
|
625
|
+
appends are ignored, and the source is closed after persistence.
|
|
626
|
+
|
|
627
|
+
The native driver's documented `write({contentBase64, ...})` callback remains
|
|
628
|
+
source-compatible. For path input only, that driver reads and Base64-encodes the
|
|
629
|
+
opened source after driver selection on current schemas; legacy path input
|
|
630
|
+
arrives pre-materialized with the same Base64 written to the database. Existing
|
|
631
|
+
Buffer, string, browser, and `UploadedFile` inputs keep their in-memory
|
|
632
|
+
behavior. See
|
|
633
|
+
[docs/attachments.md](docs/attachments.md#normalized-storage-driver-input) for
|
|
634
|
+
the normalized input passed to custom drivers.
|
|
609
635
|
|
|
610
636
|
For frontend models, configure `resourceConfig().attachments` and use:
|
|
611
637
|
|
|
@@ -1148,6 +1174,8 @@ export default class CreateEvents extends Migration {
|
|
|
1148
1174
|
}
|
|
1149
1175
|
```
|
|
1150
1176
|
|
|
1177
|
+
To reverse an `addReference("tasks", "project", {foreignKey: true, type: "uuid"})` migration, use `await this.removeReference("tasks", "project")` in `down()`. It removes the generated foreign key, index, and column; see [removing references and foreign keys](docs/database-migrations.md#removing-references-and-foreign-keys) for custom-column and foreign-key-only cases.
|
|
1178
|
+
|
|
1151
1179
|
Migrations that must be rerunnable can guard changes with `tableExists(...)`, `columnExists(table, column)` and `indexExists(table, index)` — a missing table yields `false` rather than throwing. See [docs/database-migrations.md](docs/database-migrations.md#guarding-schema-changes-in-rerunnable-migrations).
|
|
1152
1180
|
|
|
1153
1181
|
## Run migrations from the command line
|
|
@@ -1180,6 +1208,8 @@ Evaluate inline JavaScript (Rails-style runner) with initialized app/database co
|
|
|
1180
1208
|
npx velocious runner "const users = await db.query('SELECT COUNT(*) AS count FROM users'); console.log(users[0].count)"
|
|
1181
1209
|
```
|
|
1182
1210
|
|
|
1211
|
+
Successful CLI commands exit with status `0`. If command execution rejects, Velocious marks the process failed and always runs final database cleanup. Successful cleanup preserves and rethrows the original command error unchanged; if cleanup also fails, an `AggregateError` retains the command error first and as its `cause`, followed by the cleanup error. Cleanup-only failures also exit nonzero. These failures therefore remain nonzero even when application code installs an `uncaughtException` listener that reports and consumes the error. See [CLI process exit behavior](docs/cli.md#process-exit-behavior).
|
|
1212
|
+
|
|
1183
1213
|
By default, migrations write `db/structure-<identifier>.sql` files for each database in non-test environments. Test skips these automatic writes unless you explicitly opt in. Configure allow/deny lists in your configuration:
|
|
1184
1214
|
|
|
1185
1215
|
```js
|
package/bin/velocious.js
CHANGED
|
@@ -39,10 +39,37 @@ const cli = new Cli({
|
|
|
39
39
|
processArgs
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
+
let commandError
|
|
43
|
+
let commandFailed = false
|
|
44
|
+
|
|
42
45
|
try {
|
|
43
46
|
await cli.execute()
|
|
44
|
-
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
commandError = error
|
|
49
|
+
commandFailed = true
|
|
50
|
+
process.exitCode = 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let cleanupError
|
|
54
|
+
let cleanupFailed = false
|
|
55
|
+
|
|
56
|
+
try {
|
|
45
57
|
await configuration.closeDatabaseConnections()
|
|
58
|
+
} catch (error) {
|
|
59
|
+
cleanupError = error
|
|
60
|
+
cleanupFailed = true
|
|
61
|
+
process.exitCode = 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (commandFailed && cleanupFailed) {
|
|
65
|
+
throw new AggregateError(
|
|
66
|
+
[commandError, cleanupError],
|
|
67
|
+
"Velocious CLI command execution and database cleanup both failed",
|
|
68
|
+
{cause: commandError}
|
|
69
|
+
)
|
|
46
70
|
}
|
|
47
71
|
|
|
72
|
+
if (commandFailed) throw commandError
|
|
73
|
+
if (cleanupFailed) throw cleanupError
|
|
74
|
+
|
|
48
75
|
process.exit(0)
|
package/build/bin/velocious.js
CHANGED
|
@@ -39,10 +39,37 @@ const cli = new Cli({
|
|
|
39
39
|
processArgs
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
+
let commandError
|
|
43
|
+
let commandFailed = false
|
|
44
|
+
|
|
42
45
|
try {
|
|
43
46
|
await cli.execute()
|
|
44
|
-
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
commandError = error
|
|
49
|
+
commandFailed = true
|
|
50
|
+
process.exitCode = 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let cleanupError
|
|
54
|
+
let cleanupFailed = false
|
|
55
|
+
|
|
56
|
+
try {
|
|
45
57
|
await configuration.closeDatabaseConnections()
|
|
58
|
+
} catch (error) {
|
|
59
|
+
cleanupError = error
|
|
60
|
+
cleanupFailed = true
|
|
61
|
+
process.exitCode = 1
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (commandFailed && cleanupFailed) {
|
|
65
|
+
throw new AggregateError(
|
|
66
|
+
[commandError, cleanupError],
|
|
67
|
+
"Velocious CLI command execution and database cleanup both failed",
|
|
68
|
+
{cause: commandError}
|
|
69
|
+
)
|
|
46
70
|
}
|
|
47
71
|
|
|
72
|
+
if (commandFailed) throw commandError
|
|
73
|
+
if (cleanupFailed) throw cleanupError
|
|
74
|
+
|
|
48
75
|
process.exit(0)
|
|
@@ -104,13 +104,15 @@ export default class VelociousDatabaseDriversBaseTable {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
for (const foreignKey of await this.getForeignKeys()) {
|
|
107
|
-
|
|
107
|
+
const tableDataForeignKey = foreignKey.getTableDataForeignKey()
|
|
108
|
+
|
|
109
|
+
tableData.addForeignKey(tableDataForeignKey)
|
|
108
110
|
|
|
109
111
|
const tableDataColumn = tableDataColumns.find((tableDataColumn) => tableDataColumn.getName() == foreignKey.getColumnName())
|
|
110
112
|
|
|
111
113
|
if (!tableDataColumn) throw new Error(`Couldn't find table data column for foreign key: ${foreignKey.getColumnName()}`)
|
|
112
114
|
|
|
113
|
-
tableDataColumn.setForeignKey(
|
|
115
|
+
tableDataColumn.setForeignKey(tableDataForeignKey)
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
for (const index of await this.getIndexes()) {
|
|
@@ -153,4 +155,3 @@ export default class VelociousDatabaseDriversBaseTable {
|
|
|
153
155
|
return await this.getDriver().query(sql)
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
|
-
|
|
@@ -224,6 +224,34 @@ export default class VelociousDatabaseDriversBase {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Runs remove foreign key.
|
|
229
|
+
* @param {string} tableName - Table name.
|
|
230
|
+
* @param {import("./base-foreign-key.js").default} foreignKeyMetadata - Foreign key metadata.
|
|
231
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
232
|
+
*/
|
|
233
|
+
async removeForeignKey(tableName, foreignKeyMetadata) {
|
|
234
|
+
this._assertNotReadOnly()
|
|
235
|
+
|
|
236
|
+
const tableForeignKey = new TableForeignKey({
|
|
237
|
+
columnName: foreignKeyMetadata.getColumnName(),
|
|
238
|
+
dropForeignKey: true,
|
|
239
|
+
name: foreignKeyMetadata.getName(),
|
|
240
|
+
referencedColumnName: foreignKeyMetadata.getReferencedColumnName(),
|
|
241
|
+
referencedTableName: foreignKeyMetadata.getReferencedTableName(),
|
|
242
|
+
tableName
|
|
243
|
+
})
|
|
244
|
+
const tableData = new TableData(tableName)
|
|
245
|
+
|
|
246
|
+
tableData.addForeignKey(tableForeignKey)
|
|
247
|
+
|
|
248
|
+
const alterTableSQLs = await this.alterTableSQLs(tableData)
|
|
249
|
+
|
|
250
|
+
for (const alterTableSQL of alterTableSQLs) {
|
|
251
|
+
await this.query(alterTableSQL)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
227
255
|
/**
|
|
228
256
|
* Runs alter table sqls.
|
|
229
257
|
* @abstract
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { forcedBoolean, forcedString } from "typanic"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* IndexMetadataType type.
|
|
7
|
+
* @typedef {object} IndexMetadataType
|
|
8
|
+
* @property {string} column_name - Index column name.
|
|
9
|
+
* @property {string} index_name - Index name.
|
|
10
|
+
* @property {boolean} is_primary_key - Whether the index is primary.
|
|
11
|
+
* @property {boolean} is_unique - Whether the index is unique.
|
|
12
|
+
* @property {string} table_name - Table name.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Normalizes one untrusted database index metadata row.
|
|
17
|
+
* @param {import("./base.js").QueryRowType} row - Database index metadata row.
|
|
18
|
+
* @returns {IndexMetadataType} - Validated index metadata.
|
|
19
|
+
*/
|
|
20
|
+
export function normalizeIndexMetadataRow(row) {
|
|
21
|
+
return {
|
|
22
|
+
column_name: forcedString(row.column_name, "index column_name"),
|
|
23
|
+
index_name: forcedString(row.index_name, "index index_name"),
|
|
24
|
+
is_primary_key: forcedBoolean(row.is_primary_key, "index is_primary_key"),
|
|
25
|
+
is_unique: forcedBoolean(row.is_unique, "index is_unique"),
|
|
26
|
+
table_name: forcedString(row.table_name, "index table_name")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import BaseColumn from "../base-column.js"
|
|
4
|
-
import ColumnsIndex from "./columns-index.js"
|
|
5
4
|
import {digg} from "diggerize"
|
|
6
5
|
|
|
7
6
|
export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
|
|
@@ -19,39 +18,9 @@ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
|
|
|
19
18
|
getAutoIncrement() { return digg(this, "data", "isIdentity") === 1 }
|
|
20
19
|
|
|
21
20
|
async getIndexes() {
|
|
22
|
-
const
|
|
23
|
-
const sql = `
|
|
24
|
-
SELECT
|
|
25
|
-
sys.tables.name AS TableName,
|
|
26
|
-
sys.columns.name AS ColumnName,
|
|
27
|
-
sys.indexes.name AS index_name,
|
|
28
|
-
sys.indexes.type_desc AS IndexType,
|
|
29
|
-
sys.index_columns.is_included_column AS IsIncludedColumn,
|
|
30
|
-
sys.indexes.is_unique,
|
|
31
|
-
sys.indexes.is_primary_key,
|
|
32
|
-
sys.indexes.is_unique_constraint
|
|
33
|
-
FROM sys.indexes
|
|
34
|
-
INNER JOIN sys.index_columns ON sys.indexes.object_id = sys.index_columns.object_id AND sys.indexes.index_id = sys.index_columns.index_id
|
|
35
|
-
INNER JOIN sys.columns ON sys.index_columns.object_id = sys.columns.object_id AND sys.index_columns.column_id = sys.columns.column_id
|
|
36
|
-
INNER JOIN sys.tables ON sys.indexes.object_id = sys.tables.object_id
|
|
37
|
-
WHERE
|
|
38
|
-
sys.tables.name = ${options.quote(this.getTable().getName())} AND
|
|
39
|
-
sys.columns.name = ${options.quote(this.getName())}
|
|
40
|
-
ORDER BY
|
|
41
|
-
sys.indexes.name,
|
|
42
|
-
sys.index_columns.key_ordinal
|
|
43
|
-
`
|
|
21
|
+
const indexes = await this.getTable().getIndexes()
|
|
44
22
|
|
|
45
|
-
|
|
46
|
-
const indexes = []
|
|
47
|
-
|
|
48
|
-
for (const row of rows) {
|
|
49
|
-
const index = new ColumnsIndex(this.getTable(), row)
|
|
50
|
-
|
|
51
|
-
indexes.push(index)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return indexes
|
|
23
|
+
return indexes.filter((index) => index.getColumnNames().includes(this.getName()))
|
|
55
24
|
}
|
|
56
25
|
|
|
57
26
|
getDefault() { return digg(this, "data", "COLUMN_DEFAULT") }
|
|
@@ -71,4 +40,3 @@ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
|
|
|
71
40
|
getPrimaryKey() { return digg(this, "data", "isIdentity") === 1 }
|
|
72
41
|
getType() { return digg(this, "data", "DATA_TYPE") }
|
|
73
42
|
}
|
|
74
|
-
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import BaseColumnsIndex from "../base-columns-index.js"
|
|
4
|
+
import TableIndex from "../../table-data/table-index.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MssqlColumnsIndexDataType type.
|
|
8
|
+
* @typedef {object} MssqlColumnsIndexDataType
|
|
9
|
+
* @property {string[]} columnNames - Ordered index column names.
|
|
10
|
+
* @property {string} index_name - Index name.
|
|
11
|
+
* @property {boolean} is_primary_key - Whether the index is primary.
|
|
12
|
+
* @property {boolean} is_unique - Whether the index is unique.
|
|
13
|
+
* @property {string} table_name - Table name.
|
|
14
|
+
*/
|
|
4
15
|
|
|
5
16
|
export default class VelociousDatabaseDriversMssqlColumnsIndex extends BaseColumnsIndex {
|
|
17
|
+
/**
|
|
18
|
+
* Runs constructor.
|
|
19
|
+
* @param {import("../base-table.js").default} table - Table.
|
|
20
|
+
* @param {MssqlColumnsIndexDataType} data - Grouped index metadata.
|
|
21
|
+
*/
|
|
22
|
+
constructor(table, data) {
|
|
23
|
+
super(table, data)
|
|
24
|
+
this.indexData = data
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Runs get column names.
|
|
29
|
+
* @returns {string[]} - Ordered index column names.
|
|
30
|
+
*/
|
|
31
|
+
getColumnNames() { return this.indexData.columnNames }
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Runs get table data index.
|
|
35
|
+
* @returns {TableIndex} - Table-data index.
|
|
36
|
+
*/
|
|
37
|
+
getTableDataIndex() {
|
|
38
|
+
return new TableIndex(this.getColumnNames(), {
|
|
39
|
+
name: this.getName(),
|
|
40
|
+
unique: this.isUnique()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
6
43
|
}
|
|
@@ -5,6 +5,51 @@ import Column from "./column.js"
|
|
|
5
5
|
import ColumnsIndex from "./columns-index.js"
|
|
6
6
|
import {digg} from "diggerize"
|
|
7
7
|
import ForeignKey from "./foreign-key.js"
|
|
8
|
+
import { normalizeIndexMetadataRow } from "../index-metadata.js"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* MssqlGroupedIndexDataType type.
|
|
12
|
+
* @typedef {object} MssqlGroupedIndexDataType
|
|
13
|
+
* @property {string[]} columnNames - Ordered index column names.
|
|
14
|
+
* @property {string} index_name - Index name.
|
|
15
|
+
* @property {boolean} is_primary_key - Whether the index is primary.
|
|
16
|
+
* @property {boolean} is_unique - Whether the index is unique.
|
|
17
|
+
* @property {string} table_name - Table name.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Groups ordered SQL Server index rows into one metadata value per index.
|
|
22
|
+
* @param {import("../index-metadata.js").IndexMetadataType[]} indexRows - Ordered index metadata rows.
|
|
23
|
+
* @returns {MssqlGroupedIndexDataType[]} - Grouped index metadata.
|
|
24
|
+
*/
|
|
25
|
+
export function groupMssqlIndexRows(indexRows) {
|
|
26
|
+
/** @type {Map<string, MssqlGroupedIndexDataType>} */
|
|
27
|
+
const indexDataByName = new Map()
|
|
28
|
+
/** @type {MssqlGroupedIndexDataType[]} */
|
|
29
|
+
const groupedIndexData = []
|
|
30
|
+
|
|
31
|
+
for (const indexRow of indexRows) {
|
|
32
|
+
const existingIndexData = indexDataByName.get(indexRow.index_name)
|
|
33
|
+
|
|
34
|
+
if (existingIndexData) {
|
|
35
|
+
existingIndexData.columnNames.push(indexRow.column_name)
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const indexData = {
|
|
40
|
+
columnNames: [indexRow.column_name],
|
|
41
|
+
index_name: indexRow.index_name,
|
|
42
|
+
is_primary_key: indexRow.is_primary_key,
|
|
43
|
+
is_unique: indexRow.is_unique,
|
|
44
|
+
table_name: indexRow.table_name
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
indexDataByName.set(indexRow.index_name, indexData)
|
|
48
|
+
groupedIndexData.push(indexData)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return groupedIndexData
|
|
52
|
+
}
|
|
8
53
|
|
|
9
54
|
export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
|
|
10
55
|
/**
|
|
@@ -43,7 +88,7 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
|
|
|
43
88
|
return await this.getDriver()._cachedTableSchemaMetadata(this.getName(), "foreignKeys", async () => {
|
|
44
89
|
const sql = `
|
|
45
90
|
SELECT
|
|
46
|
-
fk.name AS
|
|
91
|
+
fk.name AS CONSTRAINT_NAME,
|
|
47
92
|
tp.name AS ParentTable,
|
|
48
93
|
ref.name AS ReferencedTable,
|
|
49
94
|
cp.name AS ParentColumn,
|
|
@@ -63,7 +108,7 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
|
|
|
63
108
|
ON fkc.referenced_object_id = cref.object_id
|
|
64
109
|
AND fkc.referenced_column_id = cref.column_id
|
|
65
110
|
WHERE tp.name = ${this.driver.quote(this.getName())}
|
|
66
|
-
ORDER BY
|
|
111
|
+
ORDER BY CONSTRAINT_NAME, ParentTable, ReferencedTable;
|
|
67
112
|
`
|
|
68
113
|
|
|
69
114
|
const foreignKeyRows = await this.driver.query(sql)
|
|
@@ -84,20 +129,18 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
|
|
|
84
129
|
const options = this.getOptions()
|
|
85
130
|
const sql = `
|
|
86
131
|
SELECT
|
|
87
|
-
sys.tables.name AS
|
|
88
|
-
sys.columns.name AS
|
|
132
|
+
sys.tables.name AS table_name,
|
|
133
|
+
sys.columns.name AS column_name,
|
|
89
134
|
sys.indexes.name AS index_name,
|
|
90
|
-
sys.indexes.type_desc AS IndexType,
|
|
91
|
-
sys.index_columns.is_included_column AS IsIncludedColumn,
|
|
92
135
|
sys.indexes.is_unique,
|
|
93
|
-
sys.indexes.is_primary_key
|
|
94
|
-
sys.indexes.is_unique_constraint
|
|
136
|
+
sys.indexes.is_primary_key
|
|
95
137
|
FROM sys.indexes
|
|
96
138
|
INNER JOIN sys.index_columns ON sys.indexes.object_id = sys.index_columns.object_id AND sys.indexes.index_id = sys.index_columns.index_id
|
|
97
139
|
INNER JOIN sys.columns ON sys.index_columns.object_id = sys.columns.object_id AND sys.index_columns.column_id = sys.columns.column_id
|
|
98
140
|
INNER JOIN sys.tables ON sys.indexes.object_id = sys.tables.object_id
|
|
99
141
|
WHERE
|
|
100
|
-
sys.tables.name = ${options.quote(this.getName())}
|
|
142
|
+
sys.tables.name = ${options.quote(this.getName())} AND
|
|
143
|
+
sys.index_columns.is_included_column = 0
|
|
101
144
|
ORDER BY
|
|
102
145
|
sys.indexes.name,
|
|
103
146
|
sys.index_columns.key_ordinal
|
|
@@ -105,9 +148,10 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
|
|
|
105
148
|
|
|
106
149
|
const rows = await this.getDriver().query(sql)
|
|
107
150
|
const indexes = []
|
|
151
|
+
const indexRows = rows.map((row) => normalizeIndexMetadataRow(row))
|
|
108
152
|
|
|
109
|
-
for (const
|
|
110
|
-
const index = new ColumnsIndex(this,
|
|
153
|
+
for (const indexData of groupMssqlIndexRows(indexRows)) {
|
|
154
|
+
const index = new ColumnsIndex(this, indexData)
|
|
111
155
|
|
|
112
156
|
indexes.push(index)
|
|
113
157
|
}
|
|
@@ -4,6 +4,19 @@ import AlterTableBase from "../../../query/alter-table-base.js"
|
|
|
4
4
|
import TableColumn from "../../../table-data/table-column.js"
|
|
5
5
|
|
|
6
6
|
export default class VelociousDatabaseConnectionDriversMysqlSqlAlterTable extends AlterTableBase {
|
|
7
|
+
/**
|
|
8
|
+
* Runs get drop foreign key sql.
|
|
9
|
+
* @param {import("../../../table-data/table-foreign-key.js").default} foreignKey - Foreign key to drop.
|
|
10
|
+
* @returns {string} - SQL fragment that removes the foreign key.
|
|
11
|
+
*/
|
|
12
|
+
getDropForeignKeySQL(foreignKey) {
|
|
13
|
+
const name = foreignKey.getName()
|
|
14
|
+
|
|
15
|
+
if (!name) throw new Error(`Cannot remove unnamed foreign key on ${foreignKey.getTableName()}.${foreignKey.getColumnName()}`)
|
|
16
|
+
|
|
17
|
+
return `DROP FOREIGN KEY ${this.getOptions().quoteIndexName(name)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
/**
|
|
8
21
|
* Builds MySQL ALTER TABLE statements, adding indexes atomically with columns.
|
|
9
22
|
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import BaseColumn from "../base-column.js"
|
|
4
|
-
import ColumnsIndex from "./columns-index.js"
|
|
5
4
|
import {digg} from "diggerize"
|
|
6
5
|
|
|
7
6
|
export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumn {
|
|
@@ -25,33 +24,9 @@ export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumn {
|
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
async getIndexes() {
|
|
28
|
-
const
|
|
27
|
+
const indexes = await this.getTable().getIndexes()
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
SELECT
|
|
32
|
-
pg_attribute.attname AS column_name,
|
|
33
|
-
pg_index.indexrelid::regclass as index_name,
|
|
34
|
-
pg_class.relnamespace::regnamespace as schema_name,
|
|
35
|
-
pg_class.relname as table_name,
|
|
36
|
-
pg_index.indisprimary as is_primary_key,
|
|
37
|
-
pg_index.indisunique as is_unique
|
|
38
|
-
FROM pg_index
|
|
39
|
-
JOIN pg_class ON pg_class.oid = pg_index.indrelid
|
|
40
|
-
JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = ANY(pg_index.indkey)
|
|
41
|
-
WHERE
|
|
42
|
-
pg_attribute.attname = ${options.quote(this.getName())} AND
|
|
43
|
-
pg_class.relname = ${options.quote(this.getTable().getName())}
|
|
44
|
-
`)
|
|
45
|
-
|
|
46
|
-
const indexes = []
|
|
47
|
-
|
|
48
|
-
for (const indexRow of indexesRows) {
|
|
49
|
-
const columnsIndex = new ColumnsIndex(this.getTable(), indexRow)
|
|
50
|
-
|
|
51
|
-
indexes.push(columnsIndex)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return indexes
|
|
29
|
+
return indexes.filter((index) => index.getColumnNames().includes(this.getName()))
|
|
55
30
|
}
|
|
56
31
|
|
|
57
32
|
getDefault() {
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import BaseColumnsIndex from "../base-columns-index.js"
|
|
4
|
+
import TableIndex from "../../table-data/table-index.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PgsqlColumnsIndexDataType type.
|
|
8
|
+
* @typedef {object} PgsqlColumnsIndexDataType
|
|
9
|
+
* @property {string[]} columnNames - Ordered index column names.
|
|
10
|
+
* @property {string} index_name - Index name.
|
|
11
|
+
* @property {boolean} is_primary_key - Whether the index is primary.
|
|
12
|
+
* @property {boolean} is_unique - Whether the index is unique.
|
|
13
|
+
* @property {string} table_name - Table name.
|
|
14
|
+
*/
|
|
4
15
|
|
|
5
16
|
export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumnsIndex {
|
|
17
|
+
/**
|
|
18
|
+
* Runs constructor.
|
|
19
|
+
* @param {import("../base-table.js").default} table - Table.
|
|
20
|
+
* @param {PgsqlColumnsIndexDataType} data - Grouped index metadata.
|
|
21
|
+
*/
|
|
22
|
+
constructor(table, data) {
|
|
23
|
+
super(table, data)
|
|
24
|
+
this.indexData = data
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Runs get column names.
|
|
29
|
+
* @returns {string[]} - Ordered index column names.
|
|
30
|
+
*/
|
|
31
|
+
getColumnNames() { return this.indexData.columnNames }
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Runs get table data index.
|
|
35
|
+
* @returns {TableIndex} - Table-data index.
|
|
36
|
+
*/
|
|
37
|
+
getTableDataIndex() {
|
|
38
|
+
return new TableIndex(this.getColumnNames(), {
|
|
39
|
+
name: this.getName(),
|
|
40
|
+
unique: this.isUnique()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
6
43
|
}
|
|
@@ -4,6 +4,51 @@ import BaseTable from "../base-table.js"
|
|
|
4
4
|
import Column from "./column.js"
|
|
5
5
|
import ColumnsIndex from "./columns-index.js"
|
|
6
6
|
import ForeignKey from "./foreign-key.js"
|
|
7
|
+
import { normalizeIndexMetadataRow } from "../index-metadata.js"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* PgsqlGroupedIndexDataType type.
|
|
11
|
+
* @typedef {object} PgsqlGroupedIndexDataType
|
|
12
|
+
* @property {string[]} columnNames - Ordered index column names.
|
|
13
|
+
* @property {string} index_name - Index name.
|
|
14
|
+
* @property {boolean} is_primary_key - Whether the index is primary.
|
|
15
|
+
* @property {boolean} is_unique - Whether the index is unique.
|
|
16
|
+
* @property {string} table_name - Table name.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Groups ordered PostgreSQL index rows into one metadata value per index.
|
|
21
|
+
* @param {import("../index-metadata.js").IndexMetadataType[]} indexRows - Ordered index metadata rows.
|
|
22
|
+
* @returns {PgsqlGroupedIndexDataType[]} - Grouped index metadata.
|
|
23
|
+
*/
|
|
24
|
+
export function groupPgsqlIndexRows(indexRows) {
|
|
25
|
+
/** @type {Map<string, PgsqlGroupedIndexDataType>} */
|
|
26
|
+
const indexDataByName = new Map()
|
|
27
|
+
/** @type {PgsqlGroupedIndexDataType[]} */
|
|
28
|
+
const groupedIndexData = []
|
|
29
|
+
|
|
30
|
+
for (const indexRow of indexRows) {
|
|
31
|
+
const existingIndexData = indexDataByName.get(indexRow.index_name)
|
|
32
|
+
|
|
33
|
+
if (existingIndexData) {
|
|
34
|
+
existingIndexData.columnNames.push(indexRow.column_name)
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const indexData = {
|
|
39
|
+
columnNames: [indexRow.column_name],
|
|
40
|
+
index_name: indexRow.index_name,
|
|
41
|
+
is_primary_key: indexRow.is_primary_key,
|
|
42
|
+
is_unique: indexRow.is_unique,
|
|
43
|
+
table_name: indexRow.table_name
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
indexDataByName.set(indexRow.index_name, indexData)
|
|
47
|
+
groupedIndexData.push(indexData)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return groupedIndexData
|
|
51
|
+
}
|
|
7
52
|
|
|
8
53
|
export default class VelociousDatabaseDriversPgsqlTable extends BaseTable {
|
|
9
54
|
/**
|
|
@@ -100,23 +145,29 @@ export default class VelociousDatabaseDriversPgsqlTable extends BaseTable {
|
|
|
100
145
|
|
|
101
146
|
const indexesRows = await this.getDriver().query(`
|
|
102
147
|
SELECT
|
|
103
|
-
|
|
104
|
-
pg_index.indexrelid::regclass
|
|
105
|
-
pg_class.
|
|
106
|
-
|
|
107
|
-
pg_index.
|
|
108
|
-
pg_index.indisunique as is_unique
|
|
148
|
+
index_attribute.attname AS column_name,
|
|
149
|
+
pg_index.indexrelid::regclass AS index_name,
|
|
150
|
+
pg_class.relname AS table_name,
|
|
151
|
+
pg_index.indisprimary AS is_primary_key,
|
|
152
|
+
pg_index.indisunique AS is_unique
|
|
109
153
|
FROM pg_index
|
|
110
154
|
JOIN pg_class ON pg_class.oid = pg_index.indrelid
|
|
111
|
-
JOIN
|
|
155
|
+
JOIN LATERAL unnest(pg_index.indkey) WITH ORDINALITY AS index_columns(attribute_number, ordinal_position) ON true
|
|
156
|
+
JOIN pg_attribute AS index_attribute ON index_attribute.attrelid = pg_class.oid AND index_attribute.attnum = index_columns.attribute_number
|
|
112
157
|
WHERE
|
|
113
|
-
pg_class.relname = ${options.quote(this.getName())}
|
|
158
|
+
pg_class.relname = ${options.quote(this.getName())} AND
|
|
159
|
+
index_columns.ordinal_position <= pg_index.indnkeyatts
|
|
160
|
+
ORDER BY
|
|
161
|
+
pg_index.indexrelid,
|
|
162
|
+
index_columns.ordinal_position
|
|
114
163
|
`)
|
|
115
164
|
|
|
116
165
|
const indexes = []
|
|
117
166
|
|
|
118
|
-
|
|
119
|
-
|
|
167
|
+
const indexRows = indexesRows.map((indexRow) => normalizeIndexMetadataRow(indexRow))
|
|
168
|
+
|
|
169
|
+
for (const indexData of groupPgsqlIndexRows(indexRows)) {
|
|
170
|
+
const columnsIndex = new ColumnsIndex(this, indexData)
|
|
120
171
|
|
|
121
172
|
indexes.push(columnsIndex)
|
|
122
173
|
}
|