velocious 1.0.46 → 1.0.47

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "velocious": "bin/velocious.js"
4
4
  },
5
5
  "name": "velocious",
6
- "version": "1.0.46",
6
+ "version": "1.0.47",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
9
  "test": "VELOCIOUS_TEST_DIR=../ cd spec/dummy && npx velocious test",
@@ -43,9 +43,9 @@ describe("Cli - Commands - db:migrate", () => {
43
43
  // It creates unique indexes
44
44
  const authenticationTokensTable = await dbs.default.getTableByName("authentication_tokens")
45
45
  const tokenColumn = await authenticationTokensTable.getColumnByName("token")
46
- const tokenIndex = await tokenColumn.getIndexByName("index_on_token")
46
+ const tokenIndex = await tokenColumn.getIndexByName("index_on_authentication_tokens_token")
47
47
 
48
- expect(tokenIndex.getName()).toEqual("index_on_token")
48
+ expect(tokenIndex.getName()).toEqual("index_on_authentication_tokens_token")
49
49
  expect(tokenIndex.isPrimaryKey()).toBeFalse()
50
50
  expect(tokenIndex.isUnique()).toBeTrue()
51
51
 
@@ -64,8 +64,6 @@ describe("Cli - Commands - db:migrate", () => {
64
64
  }
65
65
  })
66
66
 
67
-
68
-
69
67
  expect(projectForeignKey.getTableName()).toEqual("tasks")
70
68
  expect(projectForeignKey.getColumnName()).toEqual("project_id")
71
69
  expect(projectForeignKey.getReferencedTableName()).toEqual("projects")
@@ -84,7 +82,15 @@ describe("Cli - Commands - db:migrate", () => {
84
82
  ]
85
83
  )
86
84
 
87
- expect(uniqunize(defaultSchemaMigrations.sort())).toEqual(["20230728075328", "20230728075329", "20250605133926", "20250903112845", "20250912183605", "20250912183606"])
85
+ expect(uniqunize(defaultSchemaMigrations.sort())).toEqual([
86
+ "20230728075328",
87
+ "20230728075329",
88
+ "20250605133926",
89
+ "20250903112845",
90
+ "20250912183605",
91
+ "20250912183606",
92
+ "20250915085450"
93
+ ])
88
94
  } else {
89
95
  expect(tablesResult.sort()).toEqual(
90
96
  [
@@ -99,7 +105,7 @@ describe("Cli - Commands - db:migrate", () => {
99
105
  ]
100
106
  )
101
107
 
102
- expect(defaultSchemaMigrations.sort()).toEqual(["20230728075328", "20230728075329", "20250605133926", "20250903112845", "20250912183605", "20250912183606"])
108
+ expect(defaultSchemaMigrations.sort()).toEqual(["20230728075328", "20230728075329", "20250605133926", "20250903112845", "20250912183605", "20250912183606", "20250915085450"])
103
109
  }
104
110
  })
105
111
  })
@@ -0,0 +1,11 @@
1
+ import Migration from "../../../../../src/database/migration/index.js"
2
+
3
+ export default class ChangeAuthenticationTokensUserIdNullable extends Migration {
4
+ async up() {
5
+ await this.changeColumnNull("authentication_tokens", "user_id", true)
6
+ }
7
+
8
+ async down() {
9
+ await this.changeColumnNull("authentication_tokens", "user_id", false)
10
+ }
11
+ }
@@ -1,3 +1,5 @@
1
+ import TableData, {TableColumn} from "../table-data/index.js"
2
+
1
3
  export default class VelociousDatabaseDriversBaseColumn {
2
4
  async getIndexByName(indexName) {
3
5
  const indexes = await this.getIndexes()
@@ -6,6 +8,23 @@ export default class VelociousDatabaseDriversBaseColumn {
6
8
  return index
7
9
  }
8
10
 
11
+ async changeNullable(nullable) {
12
+ const tableData = new TableData(this.getTable().getName())
13
+ const column = this.getTableDataColumn()
14
+
15
+ column.setNull(nullable)
16
+
17
+ tableData.addColumn(column)
18
+
19
+ const sqls = await this.getDriver().alterTableSql(tableData)
20
+
21
+ console.log({sqls})
22
+
23
+ for (const sql of sqls) {
24
+ await this.getDriver().query(sql)
25
+ }
26
+ }
27
+
9
28
  getDriver() {
10
29
  return this.getTable().getDriver()
11
30
  }
@@ -19,4 +38,14 @@ export default class VelociousDatabaseDriversBaseColumn {
19
38
 
20
39
  return this.table
21
40
  }
41
+
42
+ getTableDataColumn() {
43
+ return new TableColumn(this.getName(), {
44
+ default: this.getDefault(),
45
+ isNewColumn: false,
46
+ maxLength: this.getMaxLength(),
47
+ null: this.getNull(),
48
+ type: this.getType()
49
+ })
50
+ }
22
51
  }
@@ -1,4 +1,5 @@
1
1
  import {digg} from "diggerize"
2
+ import TableData from "../table-data/index.js"
2
3
 
3
4
  export default class VelociousDatabaseDriversBaseTable {
4
5
  async getColumnByName(columnName) {
@@ -18,6 +19,20 @@ export default class VelociousDatabaseDriversBaseTable {
18
19
  return this.getDriver().options()
19
20
  }
20
21
 
22
+ async getTableData() {
23
+ const tableData = new TableData(this.getName())
24
+
25
+ for (const column of await this.getColumns()) {
26
+ tableData.addColumn(column.getTableDataColumn())
27
+ }
28
+
29
+ for (const index of await this.getIndexes()) {
30
+ tableData.addIndex(index.getTableDataIndex())
31
+ }
32
+
33
+ return tableData
34
+ }
35
+
21
36
  async rowsCount() {
22
37
  const result = await this.getDriver().query(`SELECT COUNT(*) AS count FROM ${this.getOptions().quoteTableName(this.getName())}`)
23
38
 
@@ -45,7 +45,29 @@ export default class VelociousDatabaseDriversMssqlColumn extends BaseColumn {
45
45
  return indexes
46
46
  }
47
47
 
48
+ getDefault() {
49
+ return digg(this, "data", "COLUMN_DEFAULT")
50
+ }
51
+
52
+ getMaxLength() {
53
+ return digg(this, "data", "CHARACTER_MAXIMUM_LENGTH")
54
+ }
55
+
48
56
  getName() {
49
57
  return digg(this, "data", "COLUMN_NAME")
50
58
  }
59
+
60
+ getNull() {
61
+ const nullValue = digg(this, "data", "IS_NULLABLE")
62
+
63
+ if (nullValue == "NO") {
64
+ return false
65
+ } else {
66
+ return true
67
+ }
68
+ }
69
+
70
+ getType() {
71
+ return digg(this, "data", "DATA_TYPE")
72
+ }
51
73
  }
@@ -1,3 +1,4 @@
1
+ import AlterTable from "./sql/alter-table.js"
1
2
  import Base from "../base.js"
2
3
  import CreateDatabase from "./sql/create-database.js"
3
4
  import CreateIndex from "./sql/create-index.js"
@@ -32,6 +33,13 @@ export default class VelociousDatabaseDriversMssql extends Base{
32
33
  this.connection = undefined
33
34
  }
34
35
 
36
+ async alterTableSql(tableData) {
37
+ const alterArgs = {tableData, driver: this}
38
+ const alterTable = new AlterTable(alterArgs)
39
+
40
+ return await alterTable.toSqls()
41
+ }
42
+
35
43
  createDatabaseSql(databaseName, args) {
36
44
  const createArgs = Object.assign({databaseName, driver: this}, args)
37
45
  const createDatabase = new CreateDatabase(createArgs)
@@ -47,7 +55,7 @@ export default class VelociousDatabaseDriversMssql extends Base{
47
55
  }
48
56
 
49
57
  createTableSql(tableData) {
50
- const createArgs = Object.assign({tableData, driver: this, indexInCreateTable: false})
58
+ const createArgs = {tableData, driver: this, indexInCreateTable: false}
51
59
  const createTable = new CreateTable(createArgs)
52
60
 
53
61
  return createTable.toSql()
@@ -0,0 +1,4 @@
1
+ import AlterTableBase from "../../../query/alter-table-base.js"
2
+
3
+ export default class VelociousDatabaseConnectionDriversMssqlSqlAlterTable extends AlterTableBase {
4
+ }
@@ -11,7 +11,7 @@ export default class VelociousDatabaseDriversMssqlTable extends BaseTable {
11
11
  }
12
12
 
13
13
  async getColumns() {
14
- const result = await this.driver.query(`SELECT [COLUMN_NAME] FROM [INFORMATION_SCHEMA].[COLUMNS] WHERE [TABLE_NAME] = ${this.driver.quote(this.getName())}`)
14
+ const result = await this.driver.query(`SELECT * FROM [INFORMATION_SCHEMA].[COLUMNS] WHERE [TABLE_NAME] = ${this.driver.quote(this.getName())}`)
15
15
  const columns = []
16
16
 
17
17
  for (const data of result) {
@@ -49,5 +49,38 @@ export default class VelociousDatabaseDriversMysqlColumn extends BaseColumn {
49
49
  return indexes
50
50
  }
51
51
 
52
- getName = () => digg(this, "data", "Field")
52
+ getDefault() { return digg(this, "data", "Default") }
53
+
54
+ getMaxLength() {
55
+ const type = digg(this, "data", "Type")
56
+ const match = type.match(/\((\d+)\)$/)
57
+
58
+ if (match) {
59
+ const maxLength = parseInt(match[1])
60
+
61
+ return maxLength
62
+ }
63
+ }
64
+
65
+ getName() { return digg(this, "data", "Field") }
66
+
67
+ getNull() {
68
+ const nullValue = digg(this, "data", "Null")
69
+
70
+ if (nullValue == "NO") {
71
+ return false
72
+ } else if (nullValue == "YES") {
73
+ return true
74
+ } else {
75
+ throw new Error(`Unknown null value: ${nullValue}`)
76
+ }
77
+ }
78
+
79
+ getType() {
80
+ const type = digg(this, "data", "Type")
81
+ const match = type.match(/^(.+)\((\d+)\)$/)
82
+ const columnType = match[1]
83
+
84
+ return columnType
85
+ }
53
86
  }
@@ -1,3 +1,4 @@
1
+ import AlterTable from "./sql/alter-table.js"
1
2
  import Base from "../base.js"
2
3
  import connectConnection from "./connect-connection.js"
3
4
  import CreateDatabase from "./sql/create-database.js"
@@ -41,6 +42,13 @@ export default class VelociousDatabaseDriversMysql extends Base{
41
42
  return connectArgs
42
43
  }
43
44
 
45
+ async alterTableSql(tableData) {
46
+ const alterArgs = {tableData, driver: this}
47
+ const alterTable = new AlterTable(alterArgs)
48
+
49
+ return await alterTable.toSqls()
50
+ }
51
+
44
52
  createDatabaseSql(databaseName, args) {
45
53
  const createArgs = Object.assign({databaseName, driver: this}, args)
46
54
  const createDatabase = new CreateDatabase(createArgs)
@@ -56,7 +64,7 @@ export default class VelociousDatabaseDriversMysql extends Base{
56
64
  }
57
65
 
58
66
  createTableSql(tableData) {
59
- const createArgs = Object.assign({tableData, driver: this})
67
+ const createArgs = {tableData, driver: this}
60
68
  const createTable = new CreateTable(createArgs)
61
69
 
62
70
  return createTable.toSql()
@@ -0,0 +1,4 @@
1
+ import AlterTableBase from "../../../query/alter-table-base.js"
2
+
3
+ export default class VelociousDatabaseConnectionDriversMysqlSqlAlterTable extends AlterTableBase {
4
+ }
@@ -39,7 +39,29 @@ export default class VelociousDatabaseDriversPgsqlColumn extends BaseColumn {
39
39
  return indexes
40
40
  }
41
41
 
42
+ getDefault() {
43
+ return digg(this, "data", "column_default")
44
+ }
45
+
46
+ getMaxLength() {
47
+ return digg(this, "data", "character_maximum_length")
48
+ }
49
+
42
50
  getName() {
43
51
  return digg(this, "data", "column_name")
44
52
  }
53
+
54
+ getNull() {
55
+ const nullValue = digg(this, "data", "is_nullable")
56
+
57
+ if (nullValue == "NO") {
58
+ return false
59
+ } else {
60
+ return true
61
+ }
62
+ }
63
+
64
+ getType() {
65
+ return digg(this, "data", "data_type")
66
+ }
45
67
  }
@@ -1,5 +1,6 @@
1
+ import AlterTable from "./sql/alter-table.js"
1
2
  import Base from "../base.js"
2
- import {Client, escapeLiteral} from "pg"
3
+ import {Client} from "pg"
3
4
  import CreateDatabase from "./sql/create-database.js"
4
5
  import CreateIndex from "./sql/create-index.js"
5
6
  import CreateTable from "./sql/create-table.js"
@@ -49,6 +50,13 @@ export default class VelociousDatabaseDriversPgsql extends Base{
49
50
  this.connection = undefined
50
51
  }
51
52
 
53
+ async alterTableSql(tableData) {
54
+ const alterArgs = {tableData, driver: this}
55
+ const alterTable = new AlterTable(alterArgs)
56
+
57
+ return await alterTable.toSqls()
58
+ }
59
+
52
60
  createDatabaseSql(databaseName, args) {
53
61
  const createArgs = Object.assign({databaseName, driver: this}, args)
54
62
  const createDatabase = new CreateDatabase(createArgs)
@@ -64,7 +72,7 @@ export default class VelociousDatabaseDriversPgsql extends Base{
64
72
  }
65
73
 
66
74
  createTableSql(tableData) {
67
- const createArgs = Object.assign({tableData, driver: this, indexInCreateTable: false})
75
+ const createArgs = {tableData, driver: this, indexInCreateTable: false}
68
76
  const createTable = new CreateTable(createArgs)
69
77
 
70
78
  return createTable.toSql()
@@ -0,0 +1,4 @@
1
+ import AlterTableBase from "../../../query/alter-table-base.js"
2
+
3
+ export default class VelociousDatabaseConnectionDriversPgsqlSqlAlterTable extends AlterTableBase {
4
+ }
@@ -14,11 +14,11 @@ import Table from "./table.js"
14
14
  import Update from "./sql/update.js"
15
15
 
16
16
  export default class VelociousDatabaseDriversSqliteBase extends Base {
17
- alterTableSql(columnData) {
18
- const createArgs = Object.assign({driver: this}, columnData)
19
- const alterTable = new AlterTable(createArgs)
17
+ async alterTableSql(tableData) {
18
+ const alterArgs = {driver: this, tableData}
19
+ const alterTable = new AlterTable(alterArgs)
20
20
 
21
- return alterTable.toSqls()
21
+ return await alterTable.toSqls()
22
22
  }
23
23
 
24
24
  createIndexSql(indexData) {
@@ -29,7 +29,7 @@ export default class VelociousDatabaseDriversSqliteBase extends Base {
29
29
  }
30
30
 
31
31
  createTableSql(tableData) {
32
- const createArgs = Object.assign({tableData, driver: this, indexInCreateTable: false})
32
+ const createArgs = {tableData, driver: this, indexInCreateTable: false}
33
33
  const createTable = new CreateTable(createArgs)
34
34
 
35
35
  return createTable.toSql()
@@ -1,5 +1,5 @@
1
+ import {digg} from "diggerize"
1
2
  import BaseColumn from "../base-column.js"
2
- import ColumnsIndex from "./columns-index.js"
3
3
 
4
4
  export default class VelociousDatabaseDriversSqliteColumn extends BaseColumn {
5
5
  constructor({column, driver, table}) {
@@ -10,59 +10,51 @@ export default class VelociousDatabaseDriversSqliteColumn extends BaseColumn {
10
10
  }
11
11
 
12
12
  async getIndexes() {
13
- const table = this.getTable()
14
- const rows = await this.getDriver().query(`PRAGMA index_list(${this.getOptions().quoteTableName(table.getName())})`)
15
- const indexes = []
13
+ const indexes = await this.getTable().getIndexes()
14
+ const indexesForColumn = indexes.filter((index) => index.getColumnNames().includes(this.getName()))
16
15
 
17
- for (const row of rows) {
18
- const columnsIndex = new ColumnsIndex(table, row)
19
- const indexMasterData = await this.getDriver().query(`SELECT * FROM sqlite_master WHERE type = 'index' AND name = ${this.getOptions().quote(columnsIndex.getName())}`)
20
-
21
- columnsIndex.columnNames = this._parseColumnsFromSQL(indexMasterData[0].sql)
22
-
23
- indexes.push(columnsIndex)
24
- }
16
+ return indexesForColumn
17
+ }
25
18
 
26
- return indexes
19
+ getDefault() {
20
+ return digg(this, "column", "dflt_value")
27
21
  }
28
22
 
23
+ getName() {
24
+ const name = digg(this, "column", "name")
25
+
26
+ if (!name) throw new Error("No name given for SQLite column")
29
27
 
30
- _parseColumnsFromSQL(sql) {
31
- const columnsSQLMatch = sql.match(/\((.+?)\)/)
32
- const columnsSQL = columnsSQLMatch[1].split(",")
33
- const columnNames = []
28
+ return name
29
+ }
34
30
 
35
- for (const column of columnsSQL) {
36
- const matchTicks = column.match(/`(.+)`/)
37
- const matchQuotes = column.match(/"(.+)"/)
31
+ getMaxLength() {
32
+ const columnType = digg(this, "column", "type")
33
+ const match = columnType.match(/(.*)\((\d+)\)$/)
38
34
 
39
- if (matchTicks) {
40
- columnNames.push(matchTicks[1])
41
- } else if (matchQuotes) {
42
- columnNames.push(matchQuotes[1])
43
- } else{
44
- throw new Error(`Couldn't parse column part: ${column}`)
45
- }
35
+ if (match) {
36
+ return parseInt(match[2])
46
37
  }
47
-
48
- return columnNames
49
38
  }
50
39
 
51
- getName() {
52
- if (!this.column.name) {
53
- throw new Error("No name given for SQLite column")
54
- }
40
+ getNull() {
41
+ const notNullValue = digg(this, "column", "notnull")
55
42
 
56
- return this.column.name
43
+ if (notNullValue === 1) {
44
+ return false
45
+ } else {
46
+ return true
47
+ }
57
48
  }
58
49
 
59
50
  getType() {
60
- const match = this.column.type.match(/(.*)\((\d+)\)$/)
51
+ const columnType = digg(this, "column", "type")
52
+ const match = columnType.match(/(.*)\((\d+)\)$/)
61
53
 
62
54
  if (match) {
63
55
  return match[1].toLowerCase()
64
56
  }
65
57
 
66
- return this.column.type.toLowerCase()
58
+ return columnType.toLowerCase()
67
59
  }
68
60
  }
@@ -1,5 +1,6 @@
1
1
  import BaseColumnsIndex from "../base-columns-index.js"
2
2
  import {digg} from "diggerize"
3
+ import {TableIndex} from "../../table-data/index.js"
3
4
 
4
5
  export default class VelociousDatabaseDriversSqliteColumn extends BaseColumnsIndex {
5
6
  constructor(table, data) {
@@ -8,10 +9,21 @@ export default class VelociousDatabaseDriversSqliteColumn extends BaseColumnsInd
8
9
  this.table = table
9
10
  }
10
11
 
12
+ getColumnNames() {
13
+ return digg(this, "data", "columnNames")
14
+ }
15
+
11
16
  getName() {
12
17
  return digg(this, "data", "name")
13
18
  }
14
19
 
20
+ getTableDataIndex() {
21
+ return new TableIndex(this.getColumnNames(), {
22
+ name: this.getName(),
23
+ unique: this.isUnique()
24
+ })
25
+ }
26
+
15
27
  isPrimaryKey() {
16
28
  return false
17
29
  }
@@ -34,7 +34,7 @@ export default class VelociousDatabaseDriversSqliteNode extends Base {
34
34
  this.connection = undefined
35
35
  }
36
36
 
37
- query = async (sql) => {
37
+ async query(sql) {
38
38
  return await query(this.connection, sql)
39
39
  }
40
40
  }
@@ -1,5 +1,4 @@
1
1
  export default async function query(connection, sql) {
2
- const rows = []
3
2
  let result
4
3
 
5
4
  try {
@@ -13,7 +12,7 @@ export default async function query(connection, sql) {
13
12
 
14
13
  error.message += `\n\n${sqlInErrorMessage}`
15
14
 
16
- throw error
15
+ throw new Error(error.message)
17
16
  }
18
17
 
19
18
  return result
@@ -1,4 +1,75 @@
1
1
  import AlterTableBase from "../../../query/alter-table-base.js"
2
+ import CreateIndexBase from "../../../query/create-index-base.js"
3
+ import {digs} from "diggerize"
4
+ import restArgsError from "../../../../utils/rest-args-error.js"
5
+ import TableData from "../../../table-data/index.js"
2
6
 
3
- export default class VelociousDatabaseConnectionDriversMysqlSqlAlterTable extends AlterTableBase {
7
+ export default class VelociousDatabaseConnectionDriversSqliteSqlAlterTable extends AlterTableBase {
8
+ constructor({driver, tableData, ...restArgs}) {
9
+ restArgsError(restArgs)
10
+
11
+ if (!(tableData instanceof TableData)) throw new Error("Invalid table data was given")
12
+
13
+ super({driver, tableData})
14
+ this.tableData = tableData
15
+ }
16
+
17
+ async toSqls() {
18
+ const {tableData} = digs(this, "tableData")
19
+ const table = await this.getDriver().getTableByName(tableData.getName())
20
+ const currentTableData = await table.getTableData()
21
+ const options = this.getOptions()
22
+ const tableName = tableData.getName()
23
+ const tempTableName = `${tableData.getName()}AlterTableTemp`
24
+ const oldColumnNames = currentTableData.getColumns().filter((column) => !column.isNewColumn()).map((column) => column.getName())
25
+ const oldColumnsSQL = oldColumnNames.map((name) => options.quoteColumnName(name)).join(", ")
26
+
27
+ tableData.setName(tempTableName)
28
+
29
+ const newTableData = new TableData(tempTableName)
30
+
31
+ for (const tableDataColumn of currentTableData.getColumns()) {
32
+ const newTableDataColumn = newTableData.getColumns().find((newTableDataColumn) => newTableDataColumn.getName() == tableDataColumn.getName())
33
+
34
+ newTableData.addColumn(newTableDataColumn || tableDataColumn)
35
+ }
36
+
37
+ const createNewTableSQL = this.getDriver().createTableSql(newTableData)
38
+ const insertSQL = `INSERT INTO ${options.quoteTableName(tempTableName)} (${oldColumnsSQL}) SELECT ${oldColumnsSQL} FROM ${options.quoteTableName(tableName)}`
39
+ const dropTableSQL = `DROP TABLE ${options.quoteTableName(tableName)}`
40
+ const renameTableSQL = `ALTER TABLE ${options.quoteTableName(tempTableName)} RENAME TO ${options.quoteTableName(tableName)}`
41
+ const sqls = []
42
+
43
+ for (const sql of createNewTableSQL) {
44
+ sqls.push(sql)
45
+ }
46
+
47
+ sqls.push(insertSQL)
48
+ sqls.push(dropTableSQL)
49
+ sqls.push(renameTableSQL)
50
+
51
+ for (const tableDataIndex of currentTableData.getIndexes()) {
52
+ const newTableDataIndex = newTableData.getIndexes().find((newTableDataIndex) => newTableDataIndex.getName() == tableDataIndex.getName())
53
+ const actualTableIndex = newTableDataIndex || tableDataIndex
54
+
55
+ newTableData.addIndex(actualTableIndex)
56
+
57
+ console.log({actualTableIndex})
58
+
59
+ const createIndexArgs = {
60
+ columns: actualTableIndex.getColumns(),
61
+ driver: this.getDriver(),
62
+ name: actualTableIndex.getName(),
63
+ tableName,
64
+ unique: actualTableIndex.getUnique()
65
+ }
66
+ const sql = new CreateIndexBase(createIndexArgs).toSql()
67
+
68
+ sqls.push(sql)
69
+ }
70
+
71
+ console.log({sqls})
72
+
73
+ return sqls
74
+ }
4
75
  }
@@ -1,5 +1,6 @@
1
1
  import BaseTable from "../base-table.js"
2
2
  import Column from "./column.js"
3
+ import ColumnsIndex from "./columns-index.js"
3
4
  import ForeignKey from "./foreign-key.js"
4
5
 
5
6
  export default class VelociousDatabaseDriversSqliteTable extends BaseTable {
@@ -35,6 +36,43 @@ export default class VelociousDatabaseDriversSqliteTable extends BaseTable {
35
36
  return foreignKeys
36
37
  }
37
38
 
39
+ async getIndexes() {
40
+ const rows = await this.getDriver().query(`PRAGMA index_list(${this.getOptions().quoteTableName(this.getName())})`)
41
+ const indexes = []
42
+
43
+ for (const row of rows) {
44
+ const columnsIndex = new ColumnsIndex(this, row)
45
+ const indexMasterData = await this.getDriver().query(`SELECT * FROM sqlite_master WHERE type = 'index' AND name = ${this.getOptions().quote(columnsIndex.getName())}`)
46
+
47
+ columnsIndex.data.columnNames = this._parseColumnsFromSQL(indexMasterData[0].sql)
48
+
49
+ indexes.push(columnsIndex)
50
+ }
51
+
52
+ return indexes
53
+ }
54
+
55
+ _parseColumnsFromSQL(sql) {
56
+ const columnsSQLMatch = sql.match(/\((.+?)\)/)
57
+ const columnsSQL = columnsSQLMatch[1].split(",")
58
+ const columnNames = []
59
+
60
+ for (const column of columnsSQL) {
61
+ const matchTicks = column.match(/`(.+)`/)
62
+ const matchQuotes = column.match(/"(.+)"/)
63
+
64
+ if (matchTicks) {
65
+ columnNames.push(matchTicks[1])
66
+ } else if (matchQuotes) {
67
+ columnNames.push(matchQuotes[1])
68
+ } else{
69
+ throw new Error(`Couldn't parse column part: ${column}`)
70
+ }
71
+ }
72
+
73
+ return columnNames
74
+ }
75
+
38
76
  getName() {
39
77
  if (!this.row.name) {
40
78
  throw new Error("No name given for SQLite table")
@@ -30,11 +30,11 @@ export default class VelociousDatabaseMigration {
30
30
 
31
31
  async addColumn(tableName, columnName, columnType, args) {
32
32
  const tableColumnArgs = Object.assign({type: columnType}, args)
33
+ const tableData = new TableData(tableName)
34
+
35
+ tableData.addColumn(columnName, tableColumnArgs)
33
36
 
34
- const sqls = this._db.alterTableSql({
35
- columns: [new TableColumn(columnName, tableColumnArgs)],
36
- tableName
37
- })
37
+ const sqls = await this._db.alterTableSql(tableData)
38
38
 
39
39
  for (const sql of sqls) {
40
40
  await this._db.query(sql)
@@ -80,6 +80,13 @@ export default class VelociousDatabaseMigration {
80
80
  }
81
81
  }
82
82
 
83
+ async changeColumnNull(tableName, columnName, nullable) {
84
+ const table = await this.getDriver().getTableByName(tableName)
85
+ const column = await table.getColumnByName(columnName)
86
+
87
+ await column.changeNullable(nullable)
88
+ }
89
+
83
90
  async createTable(tableName, arg1, arg2) {
84
91
  let args
85
92
  let callback
@@ -1,26 +1,46 @@
1
+ import {digs} from "diggerize"
1
2
  import QueryBase from "./base.js"
2
3
  import restArgsError from "../../utils/rest-args-error.js"
4
+ import TableData from "../table-data/index.js"
3
5
 
4
6
  export default class VelociousDatabaseQueryAlterTableBase extends QueryBase {
5
- constructor({columns, driver, tableName, ...restArgs}) {
7
+ constructor({driver, tableData, ...restArgs}) {
6
8
  restArgsError(restArgs)
7
9
 
10
+ if (!(tableData instanceof TableData)) throw new Error("Invalid table data was given")
11
+
8
12
  super({driver})
9
- this.columns = columns
10
- this.tableName = tableName
13
+ this.tableData = tableData
11
14
  }
12
15
 
13
16
  toSqls() {
17
+ const databaseType = this.getDriver().getType()
14
18
  const sqls = []
19
+ const {tableData} = digs(this, "tableData")
20
+ const options = this.getOptions()
21
+
22
+ let sql = `ALTER TABLE ${options.quoteTableName(tableData.getName())} `
23
+ let columnsCount = 0
15
24
 
16
- for (const column of this.columns) {
17
- let sql = `ALTER TABLE ${this.getOptions().quoteTableName(this.tableName)} ADD `
25
+ for (const column of tableData.getColumns()) {
26
+ if (columnsCount > 0) sql += ", "
18
27
 
19
- sql += this.getOptions().quoteColumnName(column.getName())
28
+ if (column.isNewColumn()) {
29
+ sql += "ADD "
30
+ } else {
31
+ if (databaseType == "mssql" || databaseType == "pgsql") {
32
+ sql += "ALTER COLUMN "
33
+ } else {
34
+ sql += "MODIFY "
35
+ }
36
+ }
20
37
 
21
- sqls.push(sql)
38
+ sql += column.getSQL({driver: this.getDriver(), forAlterTable: true})
39
+ columnsCount++
22
40
  }
23
41
 
42
+ sqls.push(sql)
43
+
24
44
  return sqls
25
45
  }
26
46
  }
@@ -1,10 +1,13 @@
1
1
  import CreateIndexBase from "./create-index-base.js"
2
- import * as inflection from "inflection"
2
+ import {digs} from "diggerize"
3
3
  import QueryBase from "./base.js"
4
4
  import restArgsError from "../../utils/rest-args-error.js"
5
+ import TableData from "../table-data/index.js"
5
6
 
6
7
  export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
7
8
  constructor({driver, ifNotExists, indexInCreateTable = true, tableData}) {
9
+ if (!(tableData instanceof TableData)) throw new Error("Invalid table data was given")
10
+
8
11
  super({driver})
9
12
  this.ifNotExists = ifNotExists
10
13
  this.indexInCreateTable = indexInCreateTable
@@ -15,7 +18,7 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
15
18
  const databaseType = this.getDatabaseType()
16
19
  const driver = this.getDriver()
17
20
  const options = this.getOptions()
18
- const {tableData} = this
21
+ const {tableData} = digs(this, "tableData")
19
22
  const sqls = []
20
23
  const ifNotExists = this.ifNotExists || tableData.getIfNotExists()
21
24
  let sql = ""
@@ -35,86 +38,9 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
35
38
  for (const column of tableData.getColumns()) {
36
39
  columnCount++
37
40
 
38
- let maxlength = column.getMaxLength()
39
- let type = column.getType().toUpperCase()
40
-
41
- if (type == "DATETIME" && databaseType == "pgsql") {
42
- type = "TIMESTAMP"
43
- }
44
-
45
- if (type == "STRING") {
46
- type = "VARCHAR"
47
- maxlength ||= 255
48
- }
49
-
50
- if (databaseType == "mssql" && type == "BOOLEAN") {
51
- type = "BIT"
52
- } else if (databaseType == "mssql" && type == "UUID") {
53
- type = "VARCHAR"
54
- maxlength ||= 36
55
- }
56
-
57
- if (databaseType == "sqlite" && column.getAutoIncrement() && column.getPrimaryKey()) {
58
- type = "INTEGER"
59
- }
60
-
61
- if (databaseType == "pgsql" && column.getAutoIncrement() && column.getPrimaryKey()) {
62
- type = "SERIAL"
63
- }
64
-
65
41
  if (columnCount > 1) sql += ", "
66
42
 
67
- sql += `${options.quoteColumnName(column.getName())} ${type}`
68
-
69
- if (maxlength !== undefined) sql += `(${maxlength})`
70
-
71
- if (column.getAutoIncrement() && driver.shouldSetAutoIncrementWhenPrimaryKey()) {
72
- if (databaseType == "mssql") {
73
- sql += " IDENTITY"
74
- } else if (databaseType == "pgsql") {
75
- if (column.getAutoIncrement() && column.getPrimaryKey()) {
76
- // Do nothing
77
- } else {
78
- throw new Error("pgsql auto increment must be primary key")
79
- }
80
- } else {
81
- sql += " AUTO_INCREMENT"
82
- }
83
- }
84
-
85
- if (typeof column.getDefault() == "function") {
86
- const defaultValue = column.getDefault()()
87
-
88
- sql += ` DEFAULT (`
89
-
90
- if (databaseType == "pgsql" && defaultValue == "UUID()") {
91
- sql += "gen_random_uuid()"
92
- } else if (databaseType == "mssql" && defaultValue == "UUID()") {
93
- sql += "NEWID()"
94
- } else {
95
- sql += defaultValue
96
- }
97
-
98
- sql += ")"
99
- } else if (column.getDefault()) {
100
- sql += ` DEFAULT ${options.quote(column.getDefault())}`
101
- }
102
-
103
- if (column.getPrimaryKey()) sql += " PRIMARY KEY"
104
- if (column.getNull() === false) sql += " NOT NULL"
105
-
106
- if (column.getForeignKey()) {
107
- let foreignKeyTable, foreignKeyColumn
108
-
109
- if (column.getForeignKey() === true) {
110
- foreignKeyColumn = "id"
111
- foreignKeyTable = inflection.pluralize(column.getName().replace(/_id$/, ""))
112
- } else {
113
- throw new Error(`Unknown foreign key type given: ${column.getForeignKey()} (${typeof column.getForeignKey()})`)
114
- }
115
-
116
- sql += ` REFERENCES ${options.quoteTableName(foreignKeyTable)}(${options.quoteColumnName(foreignKeyColumn)})`
117
- }
43
+ sql += column.getSQL({driver, forAlterTable: false})
118
44
  }
119
45
 
120
46
  if (this.indexInCreateTable) {
@@ -145,7 +71,7 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
145
71
  for (const column of tableData.getColumns()) {
146
72
  if (!column.getIndex()) continue
147
73
 
148
- const indexName = `index_on_${column.getName()}`
74
+ const indexName = `index_on_${tableData.getName()}_${column.getName()}`
149
75
 
150
76
  sql += ","
151
77
 
@@ -174,6 +100,7 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
174
100
  const createIndexArgs = {
175
101
  columns: index.getColumns(),
176
102
  driver: this.getDriver(),
103
+ name: index.getName(),
177
104
  tableName: tableData.getName(),
178
105
  unique: index.getUnique()
179
106
  }
@@ -185,7 +112,7 @@ export default class VelociousDatabaseQueryCreateTableBase extends QueryBase {
185
112
  for (const column of tableData.getColumns()) {
186
113
  if (!column.getIndex()) continue
187
114
 
188
- const indexName = `index_on_${column.getName()}`
115
+ const indexName = `index_on_${tableData.getName()}_${column.getName()}`
189
116
  const {unique, ...restIndexArgs} = column.getIndex()
190
117
 
191
118
  restArgsError(restIndexArgs)
@@ -1,9 +1,14 @@
1
+ import * as inflection from "inflection"
1
2
  import restArgsError from "../../utils/rest-args-error.js"
2
3
 
3
4
  class TableColumn {
4
5
  constructor(name, args) {
5
6
  if (args) {
6
- const {autoIncrement, default: columnDefault, foreignKey, index, maxLength, name, null: argsNull, primaryKey, type, ...restArgs} = args
7
+ const {autoIncrement, default: columnDefault, foreignKey, index, isNewColumn, maxLength, name, null: argsNull, primaryKey, type, ...restArgs} = args
8
+
9
+ if (Object.keys(args).length == 0) {
10
+ throw new Error("Empty args given")
11
+ }
7
12
 
8
13
  restArgsError(restArgs)
9
14
  }
@@ -12,15 +17,106 @@ class TableColumn {
12
17
  this.name = name
13
18
  }
14
19
 
15
- getAutoIncrement = () => this.args?.autoIncrement
16
- getDefault = () => this.args?.default
17
- getForeignKey = () => this.args?.foreignKey
18
- getIndex = () => this.args?.index
19
- getMaxLength = () => this.args?.maxLength
20
- getName = () => this.name
21
- getNull = () => this.args?.null
22
- getPrimaryKey = () => this.args?.primaryKey
23
- getType = () => this.args?.type
20
+ getAutoIncrement() { return this.args?.autoIncrement }
21
+ getDefault() { return this.args?.default }
22
+ getForeignKey() { return this.args?.foreignKey }
23
+ getIndex() { return this.args?.index }
24
+ getMaxLength() { return this.args?.maxLength }
25
+ getName() { return this.name }
26
+ getNull() { return this.args?.null }
27
+ setNull(nullable) { this.args.null = nullable }
28
+ getPrimaryKey() { return this.args?.primaryKey }
29
+ getType() { return this.args?.type }
30
+ isNewColumn() { return this.args?.isNewColumn }
31
+
32
+ getSQL({forAlterTable, driver}) {
33
+ const databaseType = driver.getType()
34
+ const options = driver.options()
35
+ let maxlength = this.getMaxLength()
36
+ let type = this.getType().toUpperCase()
37
+
38
+ if (type == "DATETIME" && databaseType == "pgsql") {
39
+ type = "TIMESTAMP"
40
+ }
41
+
42
+ if (type == "STRING") {
43
+ type = "VARCHAR"
44
+ maxlength ||= 255
45
+ }
46
+
47
+ if (databaseType == "mssql" && type == "BOOLEAN") {
48
+ type = "BIT"
49
+ } else if (databaseType == "mssql" && type == "UUID") {
50
+ type = "VARCHAR"
51
+ maxlength ||= 36
52
+ }
53
+
54
+ if (databaseType == "sqlite" && this.getAutoIncrement() && this.getPrimaryKey()) {
55
+ type = "INTEGER"
56
+ }
57
+
58
+ if (databaseType == "pgsql" && this.getAutoIncrement() && this.getPrimaryKey()) {
59
+ type = "SERIAL"
60
+ }
61
+
62
+ let sql = `${options.quoteColumnName(this.getName())} `
63
+
64
+ if (databaseType == "pgsql" && forAlterTable) sql += "TYPE "
65
+
66
+ sql += type
67
+
68
+ if (maxlength !== undefined && maxlength !== null) sql += `(${maxlength})`
69
+
70
+ if (this.getAutoIncrement() && driver.shouldSetAutoIncrementWhenPrimaryKey()) {
71
+ if (databaseType == "mssql") {
72
+ sql += " IDENTITY"
73
+ } else if (databaseType == "pgsql") {
74
+ if (this.getAutoIncrement() && this.getPrimaryKey()) {
75
+ // Do nothing
76
+ } else {
77
+ throw new Error("pgsql auto increment must be primary key")
78
+ }
79
+ } else {
80
+ sql += " AUTO_INCREMENT"
81
+ }
82
+ }
83
+
84
+ if (typeof this.getDefault() == "function") {
85
+ const defaultValue = this.getDefault()()
86
+
87
+ sql += ` DEFAULT (`
88
+
89
+ if (databaseType == "pgsql" && defaultValue == "UUID()") {
90
+ sql += "gen_random_uuid()"
91
+ } else if (databaseType == "mssql" && defaultValue == "UUID()") {
92
+ sql += "NEWID()"
93
+ } else {
94
+ sql += defaultValue
95
+ }
96
+
97
+ sql += ")"
98
+ } else if (this.getDefault()) {
99
+ sql += ` DEFAULT ${options.quote(this.getDefault())}`
100
+ }
101
+
102
+ if (this.getPrimaryKey()) sql += " PRIMARY KEY"
103
+ if (this.getNull() === false) sql += " NOT NULL"
104
+
105
+ if (this.getForeignKey()) {
106
+ let foreignKeyTable, foreignKeyColumn
107
+
108
+ if (this.getForeignKey() === true) {
109
+ foreignKeyColumn = "id"
110
+ foreignKeyTable = inflection.pluralize(this.getName().replace(/_id$/, ""))
111
+ } else {
112
+ throw new Error(`Unknown foreign key type given: ${this.getForeignKey()} (${typeof this.getForeignKey()})`)
113
+ }
114
+
115
+ sql += ` REFERENCES ${options.quoteTableName(foreignKeyTable)}(${options.quoteColumnName(foreignKeyColumn)})`
116
+ }
117
+
118
+ return sql
119
+ }
24
120
  }
25
121
 
26
122
  class TableIndex {
@@ -35,9 +131,9 @@ class TableIndex {
35
131
  this.columns = columns
36
132
  }
37
133
 
38
- getColumns = () => this.columns
39
- getName = () => this.args.name
40
- getUnique = () => Boolean(this.args.unique)
134
+ getColumns() { return this.columns }
135
+ getName() { return this.args.name }
136
+ getUnique() { return Boolean(this.args.unique) }
41
137
  }
42
138
 
43
139
  class TableReference {
@@ -47,7 +143,7 @@ class TableReference {
47
143
  }
48
144
  }
49
145
 
50
- export {TableColumn}
146
+ export {TableColumn, TableIndex}
51
147
 
52
148
  export default class TableData {
53
149
  _columns = []
@@ -55,34 +151,42 @@ export default class TableData {
55
151
  _references = []
56
152
 
57
153
  constructor(name, args = {}) {
154
+ if (!name) throw new Error(`Invalid table name: ${name}`)
155
+
58
156
  this.args = args
59
157
  this._name = name
60
158
  }
61
159
 
62
- _defineColumn(name, args = {}) {
63
- const column = new TableColumn(name, args)
160
+ addColumn(name, args = {}) {
161
+ if (name instanceof TableColumn) {
162
+ this._columns.push(name)
163
+ } else {
164
+ const column = new TableColumn(name, args)
64
165
 
65
- this._columns.push(column)
166
+ this._columns.push(column)
167
+ }
66
168
  }
67
169
 
68
- getColumns = () => this._columns
69
- getName = () => this._name
70
- getIfNotExists = () => this.args.ifNotExists
71
- getIndexes = () => this._indexes
72
- getReferences = () => this._references
73
-
74
- bigint = (name, args = {}) => this._defineColumn(name, Object.assign({type: "bigint"}, args))
75
- blob = (name, args = {}) => this._defineColumn(name, Object.assign({type: "blob"}, args))
76
- boolean = (name, args) => this._defineColumn(name, Object.assign({type: "boolean"}, args))
77
- datetime = (name, args) => this._defineColumn(name, Object.assign({type: "datetime"}, args))
78
- integer = (name, args = {}) => this._defineColumn(name, Object.assign({type: "integer"}, args))
79
- tinyint = (name, args = {}) => this._defineColumn(name, Object.assign({type: "tinyint"}, args))
170
+ addIndex(index) { this._indexes.push(index) }
171
+ getColumns() { return this._columns }
172
+ getName() { return this._name }
173
+ setName(newName) { this._name = newName }
174
+ getIfNotExists() { return this.args.ifNotExists }
175
+ getIndexes() { return this._indexes }
176
+ getReferences() { return this._references }
177
+
178
+ bigint(name, args = {}) { this.addColumn(name, Object.assign({isNewColumn: true, type: "bigint"}, args)) }
179
+ blob(name, args = {}) { this.addColumn(name, Object.assign({isNewColumn: true, type: "blob"}, args)) }
180
+ boolean(name, args) { this.addColumn(name, Object.assign({isNewColumn: true, type: "boolean"}, args)) }
181
+ datetime(name, args) { this.addColumn(name, Object.assign({isNewColumn: true, type: "datetime"}, args)) }
182
+ integer(name, args = {}) { this.addColumn(name, Object.assign({isNewColumn: true, type: "integer"}, args)) }
183
+ tinyint(name, args = {}) { this.addColumn(name, Object.assign({isNewColumn: true, type: "tinyint"}, args)) }
80
184
 
81
185
  references(name, args = {}) {
82
186
  const columnName = `${name}_id`
83
- const indexName = `index_on_${columnName}`
187
+ const indexName = `index_on_${this.getName()}_${columnName}`
84
188
  const reference = new TableReference(name, args)
85
- const columnArgs = Object.assign({type: "bigint"}, args)
189
+ const columnArgs = Object.assign({isNewColumn: true, type: "bigint"}, args)
86
190
  const column = new TableColumn(columnName, columnArgs)
87
191
  const index = new TableIndex([column], {name: indexName})
88
192
 
@@ -91,13 +195,13 @@ export default class TableData {
91
195
  this._references.push(reference)
92
196
  }
93
197
 
94
- string = (name, args) => this._defineColumn(name, Object.assign({type: "string"}, args))
95
- text = (name, args) => this._defineColumn(name, Object.assign({type: "text"}, args))
198
+ string(name, args) { this.addColumn(name, Object.assign({isNewColumn: true, type: "string"}, args)) }
199
+ text(name, args) { this.addColumn(name, Object.assign({isNewColumn: true, type: "text"}, args)) }
96
200
 
97
201
  timestamps(args = {}) {
98
202
  this.datetime("created_at", args)
99
203
  this.datetime("updated_at", args)
100
204
  }
101
205
 
102
- uuid = (name, args) => this._defineColumn(name, Object.assign({type: "uuid"}, args))
206
+ uuid(name, args) { this.addColumn(name, Object.assign({isNewColumn: true, type: "uuid"}, args)) }
103
207
  }