velocious 1.0.40 → 1.0.42

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.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -1
  3. package/peak_flow.yml +8 -4
  4. package/spec/cli/commands/db/create-spec.js +1 -0
  5. package/spec/cli/commands/db/migrate-spec.js +28 -11
  6. package/spec/cli/commands/test/test-files-finder-spec.js +5 -4
  7. package/spec/database/record/create-spec.js +6 -0
  8. package/spec/database/record/destroy-spec.js +6 -3
  9. package/spec/database/record/find-spec.js +3 -1
  10. package/spec/database/record/query-spec.js +7 -3
  11. package/spec/database/record/translation-fallbacks-spec.js +1 -1
  12. package/spec/database/record/update-spec.js +2 -1
  13. package/spec/database/record/validations-spec.js +10 -6
  14. package/spec/database/transactions-spec.js +7 -5
  15. package/spec/dummy/index.js +18 -29
  16. package/spec/dummy/src/config/configuration.example.js +3 -2
  17. package/spec/dummy/src/config/configuration.peakflow.mariadb.js +3 -7
  18. package/spec/dummy/src/config/configuration.peakflow.mssql.js +3 -2
  19. package/spec/dummy/src/config/configuration.peakflow.pgsql.js +3 -7
  20. package/spec/dummy/src/config/configuration.peakflow.sqlite.js +3 -7
  21. package/spec/dummy/src/config/testing.js +34 -0
  22. package/spec/dummy/src/database/migrations/20250912183605-create-users.js +15 -0
  23. package/spec/dummy/src/database/migrations/20250912183606-create-authentication-tokens.js +15 -0
  24. package/spec/dummy/src/models/authentication-token.js +8 -0
  25. package/spec/dummy/src/models/task.js +2 -2
  26. package/spec/dummy/src/models/user.js +15 -0
  27. package/spec/dummy/src/routes/projects/controller.js +7 -1
  28. package/spec/http-server/client-spec.js +1 -1
  29. package/spec/http-server/get-spec.js +1 -1
  30. package/spec/http-server/post-spec.js +21 -8
  31. package/spec/http-server/root-get-spec.js +1 -1
  32. package/src/cli/commands/db/create.js +11 -8
  33. package/src/cli/commands/db/drop.js +19 -0
  34. package/src/cli/commands/db/migrate.js +1 -1
  35. package/src/cli/commands/db/reset.js +7 -1
  36. package/src/cli/commands/test.js +10 -4
  37. package/src/configuration.js +27 -6
  38. package/src/controller.js +7 -19
  39. package/src/database/drivers/base-column.js +22 -0
  40. package/src/database/drivers/base-columns-index.js +34 -0
  41. package/src/database/drivers/base-table.js +43 -0
  42. package/src/database/drivers/base.js +44 -16
  43. package/src/database/drivers/mssql/column.js +43 -2
  44. package/src/database/drivers/mssql/columns-index.js +9 -0
  45. package/src/database/drivers/mssql/index.js +26 -14
  46. package/src/database/drivers/mssql/table.js +16 -1
  47. package/src/database/drivers/mysql/column.js +47 -2
  48. package/src/database/drivers/mysql/columns-index.js +10 -0
  49. package/src/database/drivers/mysql/index.js +5 -8
  50. package/src/database/drivers/mysql/table.js +3 -1
  51. package/src/database/drivers/pgsql/column.js +37 -2
  52. package/src/database/drivers/pgsql/columns-index.js +4 -0
  53. package/src/database/drivers/pgsql/index.js +6 -5
  54. package/src/database/drivers/pgsql/table.js +3 -1
  55. package/src/database/drivers/sqlite/base.js +6 -4
  56. package/src/database/drivers/sqlite/column.js +46 -2
  57. package/src/database/drivers/sqlite/columns-index.js +22 -0
  58. package/src/database/drivers/sqlite/connection-sql-js.js +1 -1
  59. package/src/database/drivers/sqlite/table.js +3 -1
  60. package/src/database/migrator.js +27 -7
  61. package/src/database/query/create-index-base.js +10 -1
  62. package/src/database/query/create-table-base.js +56 -2
  63. package/src/database/query/drop-table-base.js +8 -2
  64. package/src/database/table-data/index.js +2 -1
  65. package/src/database/use-database.js +1 -1
  66. package/src/http-server/client/request-buffer/index.js +9 -5
  67. package/src/http-server/client/request.js +6 -6
  68. package/src/routes/base-route.js +11 -0
  69. package/src/routes/namespace-route.js +24 -0
  70. package/src/routes/resolver.js +1 -1
  71. package/src/templates/configuration.js +1 -1
  72. package/src/testing/test-runner.js +86 -26
  73. package/src/testing/test.js +155 -7
  74. package/src/utils/with-tracked-stack.js +5 -3
@@ -1,6 +1,6 @@
1
- import DatabaseRecord from "../../../../src/database/record/index.js"
1
+ import Record from "../../../../src/database/record/index.js"
2
2
 
3
- class Task extends DatabaseRecord {
3
+ class Task extends Record {
4
4
  }
5
5
 
6
6
  Task.belongsTo("project")
@@ -0,0 +1,15 @@
1
+ import Record from "../../../../src/database/record/index.js"
2
+ import UserModule from "../../../../src/database/record/user-module.js"
3
+
4
+ class User extends Record {
5
+ }
6
+
7
+ User.hasMany("authenticationTokens" , {dependent: "destroy"})
8
+
9
+ const userModule = new UserModule({
10
+ secretKey: "02e383b7-aad1-437c-b1e1-17c0240ad851"
11
+ })
12
+
13
+ userModule.attachTo(User)
14
+
15
+ export default User
@@ -22,6 +22,12 @@ export default class ProjectsController extends Controller {
22
22
 
23
23
  await project.save()
24
24
 
25
- this.render({json: {status: "success"}})
25
+ this.render({json: {
26
+ project: {
27
+ id: project.id(),
28
+ name: project.name()
29
+ },
30
+ status: "success"
31
+ }})
26
32
  }
27
33
  }
@@ -3,7 +3,7 @@ import Client from "../../src/http-server/client/index.js"
3
3
  import {digg} from "diggerize"
4
4
  import dummyConfiguration from "../dummy/src/config/configuration.js"
5
5
 
6
- describe("http server - client", () => {
6
+ describe("http server - client", {databaseCleaning: {transaction: false, truncate: true}}, () => {
7
7
  it("spawns a request for each that it is fed", async () => {
8
8
  await dummyConfiguration.initialize()
9
9
 
@@ -1,7 +1,7 @@
1
1
  import fetch from "node-fetch"
2
2
  import Dummy from "../dummy/index.js"
3
3
 
4
- describe("HttpServer", () => {
4
+ describe("HttpServer - get", {databaseCleaning: {transaction: false, truncate: true}}, () => {
5
5
  it("handles get requests", async () => {
6
6
  await Dummy.run(async () => {
7
7
  for (let i = 0; i <= 5; i++) {
@@ -1,10 +1,12 @@
1
+ import {digg} from "diggerize"
1
2
  import fetch from "node-fetch"
2
3
  import querystring from "querystring"
4
+ import wait from "awaitery/src/wait.js"
3
5
 
4
6
  import Dummy from "../dummy/index.js"
5
7
  import Project from "../dummy/src/models/project.js"
6
8
 
7
- describe("HttpServer", () => {
9
+ describe("HttpServer - post", {databaseCleaning: {transaction: false, truncate: true}}, () => {
8
10
  it("handles post requests", async () => {
9
11
  await Dummy.run(async () => {
10
12
  for (let i = 0; i <= 5; i++) {
@@ -20,10 +22,13 @@ describe("HttpServer", () => {
20
22
  method: "POST"
21
23
  }
22
24
  )
23
- const text = await response.text()
24
- const createdProject = await Project.preload({translations: true}).last()
25
+ const data = await response.json()
26
+ const projectID = digg(data, "project", "id")
27
+
28
+ await wait(100) // Wait a bit to ensure the database connections are in sync
29
+
30
+ const createdProject = await Project.preload({translations: true}).find(projectID)
25
31
 
26
- expect(text).toEqual('{"status":"success"}')
27
32
  expect(createdProject.name()).toEqual("Test create project")
28
33
  }
29
34
  })
@@ -44,10 +49,14 @@ describe("HttpServer", () => {
44
49
  method: "POST"
45
50
  }
46
51
  )
47
- const text = await response.text()
52
+ const data = await response.json()
53
+
54
+ expect(data.status).toEqual("success")
55
+
56
+ await wait(100) // Wait a bit to ensure the database connections are in sync
57
+
48
58
  const createdProject = await Project.preload({translations: true}).last()
49
59
 
50
- expect(text).toEqual('{"status":"success"}')
51
60
  expect(createdProject.name()).toEqual("Test create project")
52
61
  }
53
62
  })
@@ -67,10 +76,14 @@ describe("HttpServer", () => {
67
76
  method: "POST"
68
77
  }
69
78
  )
70
- const text = await response.text()
79
+ const data = await response.json()
80
+
81
+ expect(data.status).toEqual("success")
82
+
83
+ await wait(100) // Wait a bit to ensure the database connections are in sync
84
+
71
85
  const createdProject = await Project.preload({translations: true}).last()
72
86
 
73
- expect(text).toEqual('{"status":"success"}')
74
87
  expect(createdProject.name()).toEqual("Test create project")
75
88
  }
76
89
  })
@@ -1,7 +1,7 @@
1
1
  import fetch from "node-fetch"
2
2
  import Dummy from "../dummy/index.js"
3
3
 
4
- describe("HttpServer", () => {
4
+ describe("HttpServer - root get", {databaseCleaning: {transaction: false, truncate: true}}, () => {
5
5
  it("handles root get requests", async () => {
6
6
  await Dummy.run(async () => {
7
7
  for (let i = 0; i <= 5; i++) {
@@ -7,21 +7,22 @@ export default class DbCreate extends BaseCommand{
7
7
  async execute() {
8
8
  for (const databaseIdentifier of this.configuration.getDatabaseIdentifiers()) {
9
9
  const databaseType = this.configuration.getDatabaseType(databaseIdentifier)
10
-
11
- this.databasePool = this.configuration.getDatabasePool(databaseIdentifier)
12
- this.newConfiguration = incorporate({}, this.databasePool.getConfiguration())
10
+ const databasePool = this.configuration.getDatabasePool(databaseIdentifier)
11
+ const newConfiguration = incorporate({}, databasePool.getConfiguration())
12
+ const DriverClass = digg(newConfiguration, "driver")
13
13
 
14
14
  if (this.args.testing) this.result = []
15
15
 
16
16
  // Use a database known to exist. Since we are creating the database, it shouldn't actually exist which would make connecting fail.
17
- this.newConfiguration.database = this.newConfiguration.useDatabase || "mysql"
17
+ newConfiguration.database = newConfiguration.useDatabase || "mysql"
18
18
 
19
19
  // Login can fail because given db name doesn't exist, which it might not because we are trying to create it right now.
20
- if (databaseType == "mssql" && this.newConfiguration.sqlConfig?.database) {
21
- delete this.newConfiguration.sqlConfig.database
20
+ if (databaseType == "mssql" && newConfiguration.sqlConfig?.database) {
21
+ delete newConfiguration.sqlConfig.database
22
22
  }
23
23
 
24
- this.databaseConnection = await this.databasePool.spawnConnectionWithConfiguration(this.newConfiguration)
24
+ this.databaseConnection = new DriverClass(newConfiguration, this.configuration)
25
+
25
26
  await this.databaseConnection.connect()
26
27
 
27
28
  try {
@@ -31,7 +32,9 @@ export default class DbCreate extends BaseCommand{
31
32
 
32
33
  await this.createSchemaMigrationsTable()
33
34
  } finally {
34
- await this.databaseConnection.close()
35
+ if (databaseType != "mssql") {
36
+ await this.databaseConnection.close()
37
+ }
35
38
  }
36
39
 
37
40
  if (this.args.testing) return this.result
@@ -0,0 +1,19 @@
1
+ import BaseCommand from "../../base-command.js"
2
+ import FilesFinder from "../../../database/migrator/files-finder.js"
3
+ import Migrator from "../../../database/migrator.js"
4
+
5
+ export default class DbDrop extends BaseCommand {
6
+ async execute() {
7
+ const environment = this.configuration.getEnvironment()
8
+
9
+ if (environment != "development" && environment != "test") {
10
+ throw new Error(`This command should only be executed on development and test environments and not: ${environment}`)
11
+ }
12
+
13
+ this.migrator = new Migrator({configuration: this.configuration})
14
+
15
+ await this.configuration.ensureConnections(async () => {
16
+ await this.migrator.dropDatabase()
17
+ })
18
+ }
19
+ }
@@ -11,7 +11,7 @@ export default class DbMigrate extends BaseCommand {
11
11
 
12
12
  this.migrator = new Migrator({configuration: this.configuration})
13
13
 
14
- await this.configuration.withConnections(async () => {
14
+ await this.configuration.ensureConnections(async () => {
15
15
  await this.migrator.prepare()
16
16
  await this.migrator.migrateFiles(files, async (importPath) => await import(importPath))
17
17
  })
@@ -4,6 +4,12 @@ import Migrator from "../../../database/migrator.js"
4
4
 
5
5
  export default class DbReset extends BaseCommand {
6
6
  async execute() {
7
+ const environment = this.configuration.getEnvironment()
8
+
9
+ if (environment != "development" && environment != "test") {
10
+ throw new Error(`This command should only be executed on development and test environments and not: ${environment}`)
11
+ }
12
+
7
13
  const projectPath = this.configuration.getDirectory()
8
14
  const migrationsPath = `${projectPath}/src/database/migrations`
9
15
  const filesFinder = new FilesFinder({path: migrationsPath})
@@ -11,7 +17,7 @@ export default class DbReset extends BaseCommand {
11
17
 
12
18
  this.migrator = new Migrator({configuration: this.configuration})
13
19
 
14
- await this.configuration.withConnections(async () => {
20
+ await this.configuration.ensureConnections(async () => {
15
21
  await this.migrator.reset()
16
22
  await this.migrator.prepare()
17
23
  await this.migrator.migrateFiles(files, async (importPath) => await import(importPath))
@@ -2,19 +2,25 @@ import BaseCommand from "../base-command.js"
2
2
  import TestFilesFinder from "../../testing/test-files-finder.js"
3
3
  import TestRunner from "../../testing/test-runner.js"
4
4
 
5
- export default class VelociousCliCommandsInit extends BaseCommand {
5
+ export default class VelociousCliCommandsTest extends BaseCommand {
6
6
  async execute() {
7
- const testFilesFinder = new TestFilesFinder({directory: this.directory(), processArgs: this.processArgs})
7
+ const testFilesFinder = new TestFilesFinder({directory: `${this.directory()}/..`, processArgs: this.processArgs})
8
8
  const testFiles = await testFilesFinder.findTestFiles()
9
9
  const testRunner = new TestRunner({configuration: this.configuration, testFiles})
10
10
 
11
+ await testRunner.prepare()
12
+
13
+ if (testRunner.getTestsCount() === 0) {
14
+ throw new Error("No tests has been found")
15
+ }
16
+
11
17
  await testRunner.run()
12
18
 
13
19
  if (testRunner.isFailed()) {
14
- console.error(`Test run failed with ${testRunner.failedTests} failed tests and ${testRunner.successfulTests} successfull`)
20
+ console.error(`\nTest run failed with ${testRunner.getFailedTests()} failed tests and ${testRunner.getSuccessfulTests()} successfull`)
15
21
  process.exit(1)
16
22
  } else {
17
- console.log(`Test run succeeded with ${testRunner.successfulTests} successful tests`)
23
+ console.log(`\nTest run succeeded with ${testRunner.getSuccessfulTests()} successful tests`)
18
24
  process.exit(0)
19
25
  }
20
26
  }
@@ -9,7 +9,7 @@ export default class VelociousConfiguration {
9
9
  return this.velociousConfiguration
10
10
  }
11
11
 
12
- constructor({cors, database, debug, directory, environment, initializeModels, locale, localeFallbacks, locales, ...restArgs}) {
12
+ constructor({cors, database, debug, directory, environment, initializeModels, locale, localeFallbacks, locales, testing, ...restArgs}) {
13
13
  restArgsError(restArgs)
14
14
 
15
15
  this.cors = cors
@@ -24,9 +24,12 @@ export default class VelociousConfiguration {
24
24
  this.localeFallbacks = localeFallbacks
25
25
  this.locales = locales
26
26
  this.modelClasses = {}
27
+ this._testing = testing
27
28
  }
28
29
 
29
30
  getDatabaseConfiguration() {
31
+ if (!this.database) throw new Error("No database configuration")
32
+
30
33
  return digg(this, "database", this.getEnvironment())
31
34
  }
32
35
 
@@ -74,9 +77,7 @@ export default class VelociousConfiguration {
74
77
  return this._directory
75
78
  }
76
79
 
77
- getEnvironment() {
78
- return digg(this, "_environment")
79
- }
80
+ getEnvironment() { return digg(this, "_environment") }
80
81
 
81
82
  getLocaleFallbacks = () => this.localeFallbacks
82
83
  setLocaleFallbacks(newLocaleFallbacks) {
@@ -103,6 +104,8 @@ export default class VelociousConfiguration {
103
104
  return modelClass
104
105
  }
105
106
 
107
+ getTesting() { return this._testing }
108
+
106
109
  initializeDatabasePool(identifier = "default") {
107
110
  if (!this.database) throw new Error("No 'database' was given")
108
111
  if (this.databasePools[identifier]) throw new Error("DatabasePool has already been initialized")
@@ -180,13 +183,31 @@ export default class VelociousConfiguration {
180
183
  await runRequest()
181
184
  }
182
185
 
183
- async getCurrentConnections() {
186
+ getCurrentConnections() {
184
187
  const dbs = {}
185
188
 
186
189
  for (const identifier of this.getDatabaseIdentifiers()) {
187
- dbs[identifier] = this.getDatabasePool(identifier).getCurrentConnection()
190
+ try {
191
+ dbs[identifier] = this.getDatabasePool(identifier).getCurrentConnection()
192
+ } catch (error) {
193
+ if (error.message == "ID hasn't been set for this async context" || error.message == "A connection hasn't been made yet") {
194
+ // Ignore
195
+ } else {
196
+ throw error
197
+ }
198
+ }
188
199
  }
189
200
 
190
201
  return dbs
191
202
  }
203
+
204
+ async ensureConnections(callback) {
205
+ let dbs = this.getCurrentConnections()
206
+
207
+ if (Object.keys(dbs).length > 0) {
208
+ await callback(dbs)
209
+ } else {
210
+ await this.withConnections(callback)
211
+ }
212
+ }
192
213
  }
package/src/controller.js CHANGED
@@ -32,17 +32,10 @@ export default class VelociousController {
32
32
  this._viewPath = viewPath
33
33
  }
34
34
 
35
- getAction() {
36
- return this._action
37
- }
38
-
39
- getParams() {
40
- return this._params
41
- }
42
-
43
- getRequest() {
44
- return this._request
45
- }
35
+ getAction() { return this._action }
36
+ getConfiguration() { return this._configuration }
37
+ getParams() { return this._params }
38
+ getRequest() { return this._request }
46
39
 
47
40
  async _runBeforeCallbacks() {
48
41
  await this.logger.debug("_runBeforeCallbacks")
@@ -74,7 +67,7 @@ export default class VelociousController {
74
67
  await this.logger.debug("After runBeforeCallbacks")
75
68
  }
76
69
 
77
- params = () => this._params
70
+ params() { return this._params }
78
71
 
79
72
  render({json, status, ...restArgs} = {}) {
80
73
  restArgsError(restArgs)
@@ -120,11 +113,6 @@ export default class VelociousController {
120
113
  throw new Error("renderText stub")
121
114
  }
122
115
 
123
- request() {
124
- return this._request
125
- }
126
-
127
- response() {
128
- return this._response
129
- }
116
+ request() { return this._request }
117
+ response() { return this._response }
130
118
  }
@@ -0,0 +1,22 @@
1
+ export default class VelociousDatabaseDriversBaseColumn {
2
+ async getIndexByName(indexName) {
3
+ const indexes = await this.getIndexes()
4
+ const index = indexes.find((index) => index.getName() == indexName)
5
+
6
+ return index
7
+ }
8
+
9
+ getDriver() {
10
+ return this.getTable().getDriver()
11
+ }
12
+
13
+ getOptions() {
14
+ return this.getDriver().options()
15
+ }
16
+
17
+ getTable() {
18
+ if (!this.table) throw new Error("No table set on column")
19
+
20
+ return this.table
21
+ }
22
+ }
@@ -0,0 +1,34 @@
1
+ import { digg } from "diggerize"
2
+
3
+ export default class VelociousDatabaseDriversBaseColumnsIndex {
4
+ constructor(table, data) {
5
+ this.data = data
6
+ this.table = table
7
+ }
8
+
9
+ getDriver() {
10
+ return this.getTable().getDriver()
11
+ }
12
+
13
+ getName() {
14
+ return digg(this, "data", "index_name")
15
+ }
16
+
17
+ getOptions() {
18
+ return this.getDriver().options()
19
+ }
20
+
21
+ getTable() {
22
+ if (!this.table) throw new Error("No table set on column")
23
+
24
+ return this.table
25
+ }
26
+
27
+ isPrimaryKey() {
28
+ return digg(this, "data", "is_primary_key")
29
+ }
30
+
31
+ isUnique() {
32
+ return digg(this, "data", "is_unique")
33
+ }
34
+ }
@@ -0,0 +1,43 @@
1
+ import {digg} from "diggerize"
2
+
3
+ export default class VelociousDatabaseDriversBaseTable {
4
+ async getColumnByName(columnName) {
5
+ const columnes = await this.getColumns()
6
+ const column = columnes.find((column) => column.getName() == columnName)
7
+
8
+ return column
9
+ }
10
+
11
+ getDriver() {
12
+ if (!this.driver) throw new Error("No driver set on table")
13
+
14
+ return this.driver
15
+ }
16
+
17
+ getOptions() {
18
+ return this.getDriver().options()
19
+ }
20
+
21
+ async rowsCount() {
22
+ const result = await this.getDriver().query(`SELECT COUNT(*) AS count FROM ${this.getOptions().quoteTableName(this.getName())}`)
23
+
24
+ return digg(result, 0, "count")
25
+ }
26
+
27
+ async truncate(args) {
28
+ const databaseType = this.getDriver().getType()
29
+ let sql
30
+
31
+ if (databaseType == "sqlite") {
32
+ sql = `DELETE FROM ${this.getOptions().quoteTableName(this.getName())}`
33
+ } else {
34
+ sql = `TRUNCATE TABLE ${this.getOptions().quoteTableName(this.getName())}`
35
+
36
+ if (args?.cascade && databaseType == "pgsql") {
37
+ sql += " CASCADE"
38
+ }
39
+ }
40
+
41
+ await this.getDriver().query(sql)
42
+ }
43
+ }
@@ -38,7 +38,11 @@ export default class VelociousDatabaseDriversBase {
38
38
  return this._args
39
39
  }
40
40
 
41
- getConfiguration = () => this.configuration
41
+ getConfiguration() {
42
+ if (!this.configuration) throw new Error("No configuration set")
43
+
44
+ return this.configuration
45
+ }
42
46
 
43
47
  getIdSeq() {
44
48
  return this.idSeq
@@ -48,11 +52,11 @@ export default class VelociousDatabaseDriversBase {
48
52
  throw new Error(`${this.constructor.name}#getTables not implemented`)
49
53
  }
50
54
 
51
- async getTableByName(name) {
55
+ async getTableByName(name, args) {
52
56
  const tables = await this.getTables()
53
57
  const table = tables.find((table) => table.getName() == name)
54
58
 
55
- if (!table) throw new Error(`Couldn't find a table by that name: ${name}`)
59
+ if (!table && args?.throwError !== false) throw new Error(`Couldn't find a table by that name: ${name}`)
56
60
 
57
61
  return table
58
62
  }
@@ -135,16 +139,7 @@ export default class VelociousDatabaseDriversBase {
135
139
 
136
140
  if (this._transactionsCount == 0) {
137
141
  this.logger.debug("Start transaction")
138
- this._transactionsCount++
139
-
140
- try {
141
- await this.startTransaction()
142
- } catch (error) {
143
- this._transactionsCount--
144
-
145
- throw error
146
- }
147
-
142
+ await this.startTransaction()
148
143
  transactionStarted = true
149
144
  } else {
150
145
  this.logger.debug("Start savepoint", savePointName)
@@ -165,7 +160,6 @@ export default class VelociousDatabaseDriversBase {
165
160
  if (transactionStarted) {
166
161
  this.logger.debug("Commit transaction")
167
162
  await this.commitTransaction()
168
- this._transactionsCount--
169
163
  }
170
164
  } catch (error) {
171
165
  this.logger.debug("Transaction error", error.message)
@@ -178,7 +172,6 @@ export default class VelociousDatabaseDriversBase {
178
172
  if (transactionStarted) {
179
173
  this.logger.debug("Rollback transaction")
180
174
  await this.rollbackTransaction()
181
- this._transactionsCount--
182
175
  }
183
176
 
184
177
  throw error
@@ -188,15 +181,18 @@ export default class VelociousDatabaseDriversBase {
188
181
  }
189
182
 
190
183
  async startTransaction() {
191
- return await this.query("BEGIN TRANSACTION")
184
+ await this.query("BEGIN TRANSACTION")
185
+ this._transactionsCount++
192
186
  }
193
187
 
194
188
  async commitTransaction() {
195
189
  await this.query("COMMIT")
190
+ this._transactionsCount--
196
191
  }
197
192
 
198
193
  async rollbackTransaction() {
199
194
  await this.query("ROLLBACK")
195
+ this._transactionsCount--
200
196
  }
201
197
 
202
198
  generateSavePointName() {
@@ -215,6 +211,38 @@ export default class VelociousDatabaseDriversBase {
215
211
  await this.query(`ROLLBACK TO SAVEPOINT ${savePointName}`)
216
212
  }
217
213
 
214
+ async truncateAllTables() {
215
+ await this.withDisabledForeignKeys(async () => {
216
+ let tries = 0
217
+
218
+ while(tries <= 5) {
219
+ tries++
220
+
221
+ const tables = await this.getTables()
222
+ const truncateErrors = []
223
+
224
+ for (const table of tables) {
225
+ if (table.getName() != "schema_migrations") {
226
+ try {
227
+ await table.truncate({cascade: true})
228
+ } catch (error) {
229
+ console.error(error)
230
+ truncateErrors.push(error)
231
+ }
232
+ }
233
+ }
234
+
235
+ if (truncateErrors.length == 0) {
236
+ break
237
+ } else if (tries <= 5) {
238
+ // Retry
239
+ } else {
240
+ throw truncateErrors[0]
241
+ }
242
+ }
243
+ })
244
+ }
245
+
218
246
  async update(...args) {
219
247
  const sql = this.updateSql(...args)
220
248
 
@@ -1,10 +1,51 @@
1
+ import BaseColumn from "../base-column.js"
2
+ import ColumnsIndex from "./columns-index.js"
1
3
  import {digg} from "diggerize"
2
4
 
3
- export default class VelociousDatabaseDriversMssqlColumn {
5
+ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
4
6
  constructor(table, data) {
7
+ super()
5
8
  this.data = data
6
9
  this.table = table
7
10
  }
8
11
 
9
- getName = () => digg(this, "data", "COLUMN_NAME")
12
+ async getIndexes() {
13
+ const options = this.getOptions()
14
+ const sql = `
15
+ SELECT
16
+ sys.tables.name AS TableName,
17
+ sys.columns.name AS ColumnName,
18
+ sys.indexes.name AS index_name,
19
+ sys.indexes.type_desc AS IndexType,
20
+ sys.index_columns.is_included_column AS IsIncludedColumn,
21
+ sys.indexes.is_unique,
22
+ sys.indexes.is_primary_key,
23
+ sys.indexes.is_unique_constraint
24
+ FROM sys.indexes
25
+ 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
26
+ INNER JOIN sys.columns ON sys.index_columns.object_id = sys.columns.object_id AND sys.index_columns.column_id = sys.columns.column_id
27
+ INNER JOIN sys.tables ON sys.indexes.object_id = sys.tables.object_id
28
+ WHERE
29
+ sys.tables.name = ${options.quote(this.getTable().getName())} AND
30
+ sys.columns.name = ${options.quote(this.getName())}
31
+ ORDER BY
32
+ sys.indexes.name,
33
+ sys.index_columns.key_ordinal
34
+ `
35
+
36
+ const rows = await this.getDriver().query(sql)
37
+ const indexes = []
38
+
39
+ for (const row of rows) {
40
+ const index = new ColumnsIndex(this.getTable(), row)
41
+
42
+ indexes.push(index)
43
+ }
44
+
45
+ return indexes
46
+ }
47
+
48
+ getName() {
49
+ return digg(this, "data", "COLUMN_NAME")
50
+ }
10
51
  }
@@ -0,0 +1,9 @@
1
+ import BaseColumnsIndex from "../base-columns-index.js"
2
+
3
+ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumnsIndex {
4
+ constructor(table, data) {
5
+ super()
6
+ this.data = data
7
+ this.table = table
8
+ }
9
+ }