velocious 1.0.39 → 1.0.41

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 (71) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -1
  3. package/peak_flow.yml +14 -6
  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 +9 -0
  8. package/spec/database/record/destroy-spec.js +6 -3
  9. package/spec/database/record/find-spec.js +4 -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 +16 -4
  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 -12
  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 +62 -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 +26 -3
  38. package/src/database/drivers/base-column.js +22 -0
  39. package/src/database/drivers/base-columns-index.js +34 -0
  40. package/src/database/drivers/base-table.js +43 -0
  41. package/src/database/drivers/base.js +12 -16
  42. package/src/database/drivers/mssql/column.js +43 -2
  43. package/src/database/drivers/mssql/columns-index.js +9 -0
  44. package/src/database/drivers/mssql/index.js +26 -14
  45. package/src/database/drivers/mssql/table.js +16 -1
  46. package/src/database/drivers/mysql/column.js +47 -2
  47. package/src/database/drivers/mysql/columns-index.js +10 -0
  48. package/src/database/drivers/mysql/index.js +5 -8
  49. package/src/database/drivers/mysql/table.js +3 -1
  50. package/src/database/drivers/pgsql/column.js +37 -2
  51. package/src/database/drivers/pgsql/columns-index.js +4 -0
  52. package/src/database/drivers/pgsql/index.js +6 -5
  53. package/src/database/drivers/pgsql/table.js +3 -1
  54. package/src/database/drivers/sqlite/base.js +6 -4
  55. package/src/database/drivers/sqlite/column.js +46 -2
  56. package/src/database/drivers/sqlite/columns-index.js +22 -0
  57. package/src/database/drivers/sqlite/connection-sql-js.js +1 -1
  58. package/src/database/drivers/sqlite/table.js +3 -1
  59. package/src/database/migrator.js +27 -7
  60. package/src/database/query/create-index-base.js +10 -1
  61. package/src/database/query/create-table-base.js +56 -2
  62. package/src/database/query/drop-table-base.js +8 -2
  63. package/src/database/record/index.js +16 -5
  64. package/src/database/record/validators/presence.js +1 -1
  65. package/src/database/table-data/index.js +2 -1
  66. package/src/database/use-database.js +1 -1
  67. package/src/routes/resolver.js +1 -1
  68. package/src/templates/configuration.js +1 -1
  69. package/src/testing/test-runner.js +86 -26
  70. package/src/testing/test.js +155 -7
  71. package/src/utils/with-tracked-stack.js +5 -3
@@ -0,0 +1,62 @@
1
+ import dummyConfiguration from "./configuration.js"
2
+
3
+ beforeEach(async ({testArgs}) => {
4
+ const transaction = testArgs.databaseCleaning?.transaction
5
+
6
+ if (transaction === undefined) {
7
+ const dbs = dummyConfiguration.getCurrentConnections()
8
+
9
+ for (const dbIdentifier in dbs) {
10
+ const db = dbs[dbIdentifier]
11
+
12
+ await db.startTransaction()
13
+ }
14
+ }
15
+ })
16
+
17
+ afterEach(async ({testArgs}) => {
18
+ const transaction = testArgs.databaseCleaning?.transaction
19
+ const truncate = testArgs.databaseCleaning?.truncate
20
+
21
+ const dbs = dummyConfiguration.getCurrentConnections()
22
+
23
+ for (const dbIdentifier in dbs) {
24
+ const db = dbs[dbIdentifier]
25
+
26
+ if (transaction === undefined || transaction) {
27
+ await db.rollbackTransaction()
28
+ }
29
+
30
+ if (truncate) {
31
+ await db.withDisabledForeignKeys(async () => {
32
+ let tries = 0
33
+
34
+ while(true) {
35
+ tries++
36
+
37
+ const tables = await db.getTables()
38
+ const truncateErrors = []
39
+
40
+ for (const table of tables) {
41
+ if (table.getName() != "schema_migrations") {
42
+ try {
43
+ await table.truncate({cascade: true})
44
+ } catch (error) {
45
+ console.error(error)
46
+ truncateErrors.push(error)
47
+ }
48
+ }
49
+ }
50
+
51
+ if (truncateErrors.length == 0) {
52
+ break
53
+ } else if (tries <= 5) {
54
+ // Retry
55
+ } else {
56
+ throw truncateErrors[0]
57
+ }
58
+ }
59
+ })
60
+ }
61
+ }
62
+ })
@@ -0,0 +1,15 @@
1
+ import Migration from "../../../../../src/database/migration/index.js"
2
+
3
+ export default class CreateUsers extends Migration {
4
+ async up() {
5
+ await this.createTable("users", (t) => {
6
+ t.string("email", {index: {unique: true}, null: false})
7
+ t.string("encrypted_password", {null: false})
8
+ t.timestamps()
9
+ })
10
+ }
11
+
12
+ async down() {
13
+ await this.dropTable("users")
14
+ }
15
+ }
@@ -0,0 +1,15 @@
1
+ import Migration from "../../../../../src/database/migration/index.js"
2
+
3
+ export default class CreateAuthenticationTokens extends Migration {
4
+ async up() {
5
+ await this.createTable("authentication_tokens", (t) => {
6
+ t.string("token", {default: () => "UUID()", index: {unique: true}})
7
+ t.references("user", {foreignKey: true, null: false})
8
+ t.timestamps()
9
+ })
10
+ }
11
+
12
+ async down() {
13
+ await this.dropTable("authentication_tokens")
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ import Record from "../../../../src/database/record/index.js"
2
+
3
+ class AuthenticationToken extends Record {
4
+ }
5
+
6
+ AuthenticationToken.belongsTo("user")
7
+
8
+ export default AuthenticationToken
@@ -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
 
@@ -103,6 +106,8 @@ export default class VelociousConfiguration {
103
106
  return modelClass
104
107
  }
105
108
 
109
+ getTesting() { return this._testing }
110
+
106
111
  initializeDatabasePool(identifier = "default") {
107
112
  if (!this.database) throw new Error("No 'database' was given")
108
113
  if (this.databasePools[identifier]) throw new Error("DatabasePool has already been initialized")
@@ -180,13 +185,31 @@ export default class VelociousConfiguration {
180
185
  await runRequest()
181
186
  }
182
187
 
183
- async getCurrentConnections() {
188
+ getCurrentConnections() {
184
189
  const dbs = {}
185
190
 
186
191
  for (const identifier of this.getDatabaseIdentifiers()) {
187
- dbs[identifier] = this.getDatabasePool(identifier).getCurrentConnection()
192
+ try {
193
+ dbs[identifier] = this.getDatabasePool(identifier).getCurrentConnection()
194
+ } catch (error) {
195
+ if (error.message == "ID hasn't been set for this async context" || error.message == "A connection hasn't been made yet") {
196
+ // Ignore
197
+ } else {
198
+ throw error
199
+ }
200
+ }
188
201
  }
189
202
 
190
203
  return dbs
191
204
  }
205
+
206
+ async ensureConnections(callback) {
207
+ let dbs = this.getCurrentConnections()
208
+
209
+ if (Object.keys(dbs).length > 0) {
210
+ await callback(dbs)
211
+ } else {
212
+ await this.withConnections(callback)
213
+ }
214
+ }
192
215
  }
@@ -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() {
@@ -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
  }