@strapi/database 4.0.0-beta.0

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 (52) hide show
  1. package/LICENSE +22 -0
  2. package/examples/connections.js +36 -0
  3. package/examples/data.sqlite +0 -0
  4. package/examples/docker-compose.yml +29 -0
  5. package/examples/index.js +73 -0
  6. package/examples/models.js +341 -0
  7. package/examples/typings.ts +17 -0
  8. package/lib/dialects/dialect.js +45 -0
  9. package/lib/dialects/index.js +28 -0
  10. package/lib/dialects/mysql/index.js +51 -0
  11. package/lib/dialects/mysql/schema-inspector.js +203 -0
  12. package/lib/dialects/postgresql/index.js +49 -0
  13. package/lib/dialects/postgresql/schema-inspector.js +229 -0
  14. package/lib/dialects/sqlite/index.js +74 -0
  15. package/lib/dialects/sqlite/schema-inspector.js +151 -0
  16. package/lib/entity-manager.js +886 -0
  17. package/lib/entity-repository.js +110 -0
  18. package/lib/errors.js +14 -0
  19. package/lib/fields.d.ts +9 -0
  20. package/lib/fields.js +232 -0
  21. package/lib/index.d.ts +146 -0
  22. package/lib/index.js +60 -0
  23. package/lib/lifecycles/index.d.ts +50 -0
  24. package/lib/lifecycles/index.js +66 -0
  25. package/lib/lifecycles/subscribers/index.d.ts +9 -0
  26. package/lib/lifecycles/subscribers/models-lifecycles.js +19 -0
  27. package/lib/lifecycles/subscribers/timestamps.js +65 -0
  28. package/lib/metadata/index.js +219 -0
  29. package/lib/metadata/relations.js +488 -0
  30. package/lib/migrations/index.d.ts +9 -0
  31. package/lib/migrations/index.js +69 -0
  32. package/lib/migrations/storage.js +49 -0
  33. package/lib/query/helpers/index.js +10 -0
  34. package/lib/query/helpers/join.js +95 -0
  35. package/lib/query/helpers/order-by.js +70 -0
  36. package/lib/query/helpers/populate.js +652 -0
  37. package/lib/query/helpers/search.js +84 -0
  38. package/lib/query/helpers/transform.js +84 -0
  39. package/lib/query/helpers/where.js +322 -0
  40. package/lib/query/index.js +7 -0
  41. package/lib/query/query-builder.js +348 -0
  42. package/lib/schema/__tests__/schema-diff.test.js +181 -0
  43. package/lib/schema/builder.js +352 -0
  44. package/lib/schema/diff.js +376 -0
  45. package/lib/schema/index.d.ts +49 -0
  46. package/lib/schema/index.js +95 -0
  47. package/lib/schema/schema.js +209 -0
  48. package/lib/schema/storage.js +75 -0
  49. package/lib/types/index.d.ts +6 -0
  50. package/lib/types/index.js +34 -0
  51. package/lib/utils/content-types.js +41 -0
  52. package/package.json +39 -0
@@ -0,0 +1,203 @@
1
+ 'use strict';
2
+
3
+ const SQL_QUERIES = {
4
+ TABLE_LIST: /* sql */ `
5
+ SELECT
6
+ t.table_name as table_name
7
+ FROM information_schema.tables t
8
+ WHERE table_type = 'BASE TABLE'
9
+ AND table_schema = schema();
10
+ `,
11
+ LIST_COLUMNS: /* sql */ `
12
+ SELECT
13
+ c.data_type as data_type,
14
+ c.column_name as column_name,
15
+ c.character_maximum_length as character_maximum_length,
16
+ c.column_default as column_default,
17
+ c.is_nullable as is_nullable,
18
+ c.column_type as column_type,
19
+ c.column_key as column_key
20
+ FROM information_schema.columns c
21
+ WHERE table_schema = database()
22
+ AND table_name = ?;
23
+ `,
24
+ INDEX_LIST: /* sql */ `
25
+ show index from ??;
26
+ `,
27
+ FOREIGN_KEY_LIST: /* sql */ `
28
+ SELECT
29
+ tc.constraint_name as constraint_name,
30
+ kcu.column_name as column_name,
31
+ kcu.referenced_table_name as referenced_table_name,
32
+ kcu.referenced_column_name as referenced_column_name,
33
+ rc.update_rule as on_update,
34
+ rc.delete_rule as on_delete
35
+ FROM information_schema.table_constraints tc
36
+ INNER JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
37
+ INNER JOIN information_schema.referential_constraints AS rc ON kcu.constraint_name = rc.constraint_name
38
+ WHERE constraint_type = 'FOREIGN KEY'
39
+ AND tc.table_schema = database()
40
+ AND tc.table_name = ?;
41
+
42
+ `,
43
+ };
44
+
45
+ const toStrapiType = column => {
46
+ const rootType = column.data_type.toLowerCase().match(/[^(), ]+/)[0];
47
+
48
+ switch (rootType) {
49
+ case 'int': {
50
+ if (column.column_key === 'PRI') {
51
+ return { type: 'increments', args: [{ primary: true }], unsigned: false };
52
+ }
53
+
54
+ return { type: 'integer' };
55
+ }
56
+ case 'decimal': {
57
+ return { type: 'decimal', args: [10, 2] };
58
+ }
59
+ case 'double': {
60
+ return { type: 'double' };
61
+ }
62
+ case 'bigint': {
63
+ return { type: 'bigInteger' };
64
+ }
65
+ case 'enum': {
66
+ return { type: 'enum' };
67
+ }
68
+ case 'tinyint': {
69
+ return { type: 'boolean' };
70
+ }
71
+ case 'longtext': {
72
+ return { type: 'text', args: ['longtext'] };
73
+ }
74
+ case 'varchar': {
75
+ return { type: 'string', args: [column.character_maximum_length] };
76
+ }
77
+ case 'datetime': {
78
+ return { type: 'datetime', args: [{ useTz: false, precision: 6 }] };
79
+ }
80
+ case 'date': {
81
+ return { type: 'date' };
82
+ }
83
+ case 'time': {
84
+ return { type: 'time', args: [{ precision: 3 }] };
85
+ }
86
+ case 'timestamp': {
87
+ return { type: 'timestamp', args: [{ useTz: false, precision: 6 }] };
88
+ }
89
+ case 'json': {
90
+ return { type: 'jsonb' };
91
+ }
92
+ default: {
93
+ return { type: 'specificType', args: [column.data_type] };
94
+ }
95
+ }
96
+ };
97
+
98
+ class MysqlSchemaInspector {
99
+ constructor(db) {
100
+ this.db = db;
101
+ }
102
+
103
+ async getSchema() {
104
+ const schema = { tables: [] };
105
+
106
+ const tables = await this.getTables();
107
+
108
+ schema.tables = await Promise.all(
109
+ tables.map(async tableName => {
110
+ const columns = await this.getColumns(tableName);
111
+ const indexes = await this.getIndexes(tableName);
112
+ const foreignKeys = await this.getForeignKeys(tableName);
113
+
114
+ return {
115
+ name: tableName,
116
+ columns,
117
+ indexes,
118
+ foreignKeys,
119
+ };
120
+ })
121
+ );
122
+
123
+ return schema;
124
+ }
125
+
126
+ getDatabaseSchema() {
127
+ return this.db.connection.client.connectionSettings.schema || 'public';
128
+ }
129
+
130
+ async getTables() {
131
+ const [rows] = await this.db.connection.raw(SQL_QUERIES.TABLE_LIST);
132
+
133
+ return rows.map(row => row.table_name);
134
+ }
135
+
136
+ async getColumns(tableName) {
137
+ const [rows] = await this.db.connection.raw(SQL_QUERIES.LIST_COLUMNS, [tableName]);
138
+
139
+ return rows.map(row => {
140
+ const { type, args = [], ...rest } = toStrapiType(row);
141
+
142
+ return {
143
+ type,
144
+ args,
145
+ defaultTo: row.column_default,
146
+ name: row.column_name,
147
+ notNullable: row.is_nullable === 'NO',
148
+ unsigned: row.column_type.endsWith(' unsigned'),
149
+ ...rest,
150
+ };
151
+ });
152
+ }
153
+
154
+ async getIndexes(tableName) {
155
+ const [rows] = await this.db.connection.raw(SQL_QUERIES.INDEX_LIST, [tableName]);
156
+
157
+ const ret = {};
158
+
159
+ for (const index of rows) {
160
+ if (index.Column_name === 'id') {
161
+ continue;
162
+ }
163
+
164
+ if (!ret[index.Key_name]) {
165
+ ret[index.Key_name] = {
166
+ columns: [index.Column_name],
167
+ name: index.Key_name,
168
+ type: !index.Non_unique ? 'unique' : null,
169
+ };
170
+ } else {
171
+ ret[index.Key_name].columns.push(index.Column_name);
172
+ }
173
+ }
174
+
175
+ return Object.values(ret);
176
+ }
177
+
178
+ async getForeignKeys(tableName) {
179
+ const [rows] = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_LIST, [tableName]);
180
+
181
+ const ret = {};
182
+
183
+ for (const fk of rows) {
184
+ if (!ret[fk.constraint_name]) {
185
+ ret[fk.constraint_name] = {
186
+ name: fk.constraint_name,
187
+ columns: [fk.column_name],
188
+ referencedColumns: [fk.referenced_column_name],
189
+ referencedTable: fk.referenced_table_name,
190
+ onUpdate: fk.on_update.toUpperCase(),
191
+ onDelete: fk.on_delete.toUpperCase(),
192
+ };
193
+ } else {
194
+ ret[fk.constraint_name].columns.push(fk.column_name);
195
+ ret[fk.constraint_name].referencedColumns.push(fk.referenced_column_name);
196
+ }
197
+ }
198
+
199
+ return Object.values(ret);
200
+ }
201
+ }
202
+
203
+ module.exports = MysqlSchemaInspector;
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const errors = require('../../errors');
4
+ const { Dialect } = require('../dialect');
5
+ const PostgresqlSchemaInspector = require('./schema-inspector');
6
+
7
+ class PostgresDialect extends Dialect {
8
+ constructor(db) {
9
+ super(db);
10
+
11
+ this.schemaInspector = new PostgresqlSchemaInspector(db);
12
+ }
13
+
14
+ useReturning() {
15
+ return true;
16
+ }
17
+
18
+ initialize() {
19
+ this.db.connection.client.driver.types.setTypeParser(1700, 'text', parseFloat);
20
+ }
21
+
22
+ usesForeignKeys() {
23
+ return true;
24
+ }
25
+
26
+ getSqlType(type) {
27
+ switch (type) {
28
+ case 'timestamp': {
29
+ return 'datetime';
30
+ }
31
+ default: {
32
+ return type;
33
+ }
34
+ }
35
+ }
36
+
37
+ transformErrors(error) {
38
+ switch (error.code) {
39
+ case '23502': {
40
+ throw new errors.NotNullConstraint({ column: error.column });
41
+ }
42
+ default: {
43
+ super.transformErrors(error);
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ module.exports = PostgresDialect;
@@ -0,0 +1,229 @@
1
+ 'use strict';
2
+
3
+ const SQL_QUERIES = {
4
+ TABLE_LIST: /* sql */ `
5
+ SELECT *
6
+ FROM information_schema.tables
7
+ WHERE table_schema = ? AND table_type = 'BASE TABLE';
8
+ `,
9
+ LIST_COLUMNS: /* sql */ `
10
+ SELECT data_type, column_name, character_maximum_length, column_default, is_nullable
11
+ FROM information_schema.columns
12
+ WHERE table_schema = ?
13
+ AND table_name = ?;
14
+ `,
15
+ INDEX_LIST: /* sql */ `
16
+ select
17
+ ix.indexrelid,
18
+ i.relname as index_name,
19
+ a.attname as column_name,
20
+ ix.indisunique as is_unique,
21
+ ix.indisprimary as is_primary
22
+ from
23
+ pg_class t,
24
+ pg_namespace s,
25
+ pg_class i,
26
+ pg_index ix,
27
+ pg_attribute a
28
+ where
29
+ t.oid = ix.indrelid
30
+ and i.oid = ix.indexrelid
31
+ and a.attrelid = t.oid
32
+ and a.attnum = ANY(ix.indkey)
33
+ and t.relkind = 'r'
34
+ and t.relnamespace = s.oid
35
+ and s.nspname = ?
36
+ and t.relname = ?;
37
+ `,
38
+ FOREIGN_KEY_LIST: /* sql */ `
39
+ SELECT
40
+ tco."constraint_name" as constraint_name,
41
+ kcu."column_name" as column_name,
42
+ rel_kcu."table_name" as foreign_table,
43
+ rel_kcu."column_name" as fk_column_name,
44
+ rco.update_rule as on_update,
45
+ rco.delete_rule as on_delete
46
+ FROM information_schema.table_constraints tco
47
+ JOIN information_schema.key_column_usage kcu
48
+ ON tco.constraint_schema = kcu.constraint_schema
49
+ AND tco.constraint_name = kcu.constraint_name
50
+ JOIN information_schema.referential_constraints rco
51
+ ON tco.constraint_schema = rco.constraint_schema
52
+ AND tco.constraint_name = rco.constraint_name
53
+ JOIN information_schema.key_column_usage rel_kcu
54
+ ON rco.unique_constraint_schema = rel_kcu.constraint_schema
55
+ AND rco.unique_constraint_name = rel_kcu.constraint_name
56
+ AND kcu.ordinal_position = rel_kcu.ordinal_position
57
+ WHERE
58
+ tco.constraint_type = 'FOREIGN KEY'
59
+ AND tco.constraint_schema = ?
60
+ AND tco.table_name = ?
61
+ ORDER BY kcu.table_schema, kcu.table_name, kcu.ordinal_position, kcu.constraint_name;
62
+ `,
63
+ };
64
+
65
+ const toStrapiType = column => {
66
+ const rootType = column.data_type.toLowerCase().match(/[^(), ]+/)[0];
67
+
68
+ switch (rootType) {
69
+ case 'integer': {
70
+ // find a way to figure out the increments
71
+ return { type: 'integer' };
72
+ }
73
+ case 'text': {
74
+ return { type: 'text', args: ['longtext'] };
75
+ }
76
+ case 'boolean': {
77
+ return { type: 'boolean' };
78
+ }
79
+ case 'character': {
80
+ return { type: 'string', args: [column.character_maximum_length] };
81
+ }
82
+ case 'timestamp': {
83
+ return { type: 'datetime', args: [{ useTz: false, precision: 6 }] };
84
+ }
85
+ case 'date': {
86
+ return { type: 'date' };
87
+ }
88
+ case 'time': {
89
+ return { type: 'time', args: [{ precision: 3 }] };
90
+ }
91
+ case 'numeric': {
92
+ return { type: 'decimal', args: [10, 2] };
93
+ }
94
+ case 'real':
95
+ case 'double': {
96
+ return { type: 'double' };
97
+ }
98
+ case 'bigint': {
99
+ return { type: 'bigInteger' };
100
+ }
101
+ case 'jsonb': {
102
+ return { type: 'jsonb' };
103
+ }
104
+ default: {
105
+ return { type: 'specificType', args: [column.data_type] };
106
+ }
107
+ }
108
+ };
109
+
110
+ class PostgresqlSchemaInspector {
111
+ constructor(db) {
112
+ this.db = db;
113
+ }
114
+
115
+ async getSchema() {
116
+ const schema = { tables: [] };
117
+
118
+ const tables = await this.getTables();
119
+
120
+ schema.tables = await Promise.all(
121
+ tables.map(async tableName => {
122
+ const columns = await this.getColumns(tableName);
123
+ const indexes = await this.getIndexes(tableName);
124
+ const foreignKeys = await this.getForeignKeys(tableName);
125
+
126
+ return {
127
+ name: tableName,
128
+ columns,
129
+ indexes,
130
+ foreignKeys,
131
+ };
132
+ })
133
+ );
134
+
135
+ return schema;
136
+ }
137
+
138
+ getDatabaseSchema() {
139
+ return this.db.connection.client.connectionSettings.schema || 'public';
140
+ }
141
+
142
+ async getTables() {
143
+ const { rows } = await this.db.connection.raw(SQL_QUERIES.TABLE_LIST, [
144
+ this.getDatabaseSchema(),
145
+ ]);
146
+
147
+ return rows.map(row => row.table_name);
148
+ }
149
+
150
+ async getColumns(tableName) {
151
+ const { rows } = await this.db.connection.raw(SQL_QUERIES.LIST_COLUMNS, [
152
+ this.getDatabaseSchema(),
153
+ tableName,
154
+ ]);
155
+
156
+ return rows.map(row => {
157
+ const { type, args = [], ...rest } = toStrapiType(row);
158
+
159
+ const defaultTo =
160
+ row.column_default && row.column_default.includes('nextval(') ? null : row.column_default;
161
+
162
+ return {
163
+ type,
164
+ args,
165
+ defaultTo,
166
+ name: row.column_name,
167
+ notNullable: row.is_nullable === 'NO',
168
+ unsigned: false,
169
+ ...rest,
170
+ };
171
+ });
172
+ }
173
+
174
+ async getIndexes(tableName) {
175
+ const { rows } = await this.db.connection.raw(SQL_QUERIES.INDEX_LIST, [
176
+ this.getDatabaseSchema(),
177
+ tableName,
178
+ ]);
179
+
180
+ const ret = {};
181
+
182
+ for (const index of rows) {
183
+ if (index.column_name === 'id') {
184
+ continue;
185
+ }
186
+
187
+ if (!ret[index.indexrelid]) {
188
+ ret[index.indexrelid] = {
189
+ columns: [index.column_name],
190
+ name: index.index_name,
191
+ type: index.is_primary ? 'primary' : index.is_unique ? 'unique' : null,
192
+ };
193
+ } else {
194
+ ret[index.indexrelid].columns.push(index.column_name);
195
+ }
196
+ }
197
+
198
+ return Object.values(ret);
199
+ }
200
+
201
+ async getForeignKeys(tableName) {
202
+ const { rows } = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_LIST, [
203
+ this.getDatabaseSchema(),
204
+ tableName,
205
+ ]);
206
+
207
+ const ret = {};
208
+
209
+ for (const fk of rows) {
210
+ if (!ret[fk.constraint_name]) {
211
+ ret[fk.constraint_name] = {
212
+ name: fk.constraint_name,
213
+ columns: [fk.column_name],
214
+ referencedColumns: [fk.fk_column_name],
215
+ referencedTable: fk.foreign_table,
216
+ onUpdate: fk.on_update.toUpperCase(),
217
+ onDelete: fk.on_delete.toUpperCase(),
218
+ };
219
+ } else {
220
+ ret[fk.constraint_name].columns.push(fk.column_name);
221
+ ret[fk.constraint_name].referencedColumns.push(fk.fk_column_name);
222
+ }
223
+ }
224
+
225
+ return Object.values(ret);
226
+ }
227
+ }
228
+
229
+ module.exports = PostgresqlSchemaInspector;
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fse = require('fs-extra');
5
+
6
+ const errors = require('../../errors');
7
+ const { Dialect } = require('../dialect');
8
+ const SqliteSchmeaInspector = require('./schema-inspector');
9
+
10
+ class SqliteDialect extends Dialect {
11
+ constructor(db) {
12
+ super(db);
13
+
14
+ this.schemaInspector = new SqliteSchmeaInspector(db);
15
+ }
16
+
17
+ configure() {
18
+ this.db.config.connection.connection.filename = path.resolve(
19
+ this.db.config.connection.connection.filename
20
+ );
21
+
22
+ const dbDir = path.dirname(this.db.config.connection.connection.filename);
23
+
24
+ fse.ensureDirSync(dbDir);
25
+ }
26
+
27
+ async initialize() {
28
+ await this.db.connection.raw('pragma foreign_keys = on');
29
+ }
30
+
31
+ canAlterConstraints() {
32
+ return false;
33
+ }
34
+
35
+ getSqlType(type) {
36
+ switch (type) {
37
+ // FIXME: enum must be dealt separately
38
+ case 'enum': {
39
+ return 'text';
40
+ }
41
+ case 'double':
42
+ case 'decimal': {
43
+ return 'float';
44
+ }
45
+ case 'timestamp': {
46
+ return 'datetime';
47
+ }
48
+ default: {
49
+ return type;
50
+ }
51
+ }
52
+ }
53
+
54
+ async startSchemaUpdate() {
55
+ await this.db.connection.raw(`pragma foreign_keys = off`);
56
+ }
57
+
58
+ async endSchemaUpdate() {
59
+ await this.db.connection.raw(`pragma foreign_keys = on`);
60
+ }
61
+
62
+ transformErrors(error) {
63
+ switch (error.errno) {
64
+ case 19: {
65
+ throw new errors.NotNullConstraint(); // TODO: extract column name
66
+ }
67
+ default: {
68
+ super.transformErrors(error);
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ module.exports = SqliteDialect;
@@ -0,0 +1,151 @@
1
+ 'use strict';
2
+
3
+ const SQL_QUERIES = {
4
+ TABLE_LIST: `select name from sqlite_master where type = 'table' and name NOT LIKE 'sqlite%'`,
5
+ TABLE_INFO: `pragma table_info(??)`,
6
+ INDEX_LIST: 'pragma index_list(??)',
7
+ INDEX_INFO: 'pragma index_info(??)',
8
+ FOREIGN_KEY_LIST: 'pragma foreign_key_list(??)',
9
+ };
10
+
11
+ const toStrapiType = column => {
12
+ const { type } = column;
13
+
14
+ const rootType = type.toLowerCase().match(/[^(), ]+/)[0];
15
+
16
+ switch (rootType) {
17
+ case 'integer': {
18
+ if (column.pk) {
19
+ return { type: 'increments', args: [{ primary: true }] };
20
+ }
21
+
22
+ return { type: 'integer' };
23
+ }
24
+ case 'float': {
25
+ return { type: 'float', args: [10, 2] };
26
+ }
27
+ case 'bigint': {
28
+ return { type: 'bigInteger' };
29
+ }
30
+ case 'varchar': {
31
+ const length = type.slice(8, type.length - 1);
32
+
33
+ return { type: 'string', args: [Number(length)] };
34
+ }
35
+ case 'text': {
36
+ return { type: 'text', args: ['longtext'] };
37
+ }
38
+ case 'json': {
39
+ return { type: 'jsonb' };
40
+ }
41
+ case 'boolean': {
42
+ return { type: 'boolean' };
43
+ }
44
+ case 'datetime': {
45
+ return { type: 'datetime', args: [{ useTz: false, precision: 6 }] };
46
+ }
47
+ case 'date': {
48
+ return { type: 'date' };
49
+ }
50
+ case 'time': {
51
+ return { type: 'time', args: [{ precision: 3 }] };
52
+ }
53
+ default: {
54
+ return { type: 'specificType', args: [column.data_type] };
55
+ }
56
+ }
57
+ };
58
+
59
+ class SqliteSchemaInspector {
60
+ constructor(db) {
61
+ this.db = db;
62
+ }
63
+
64
+ async getSchema() {
65
+ const schema = { tables: [] };
66
+ const tables = await this.getTables();
67
+
68
+ for (const tableName of tables) {
69
+ const columns = await this.getColumns(tableName);
70
+ const indexes = await this.getIndexes(tableName);
71
+ const foreignKeys = await this.getForeignKeys(tableName);
72
+
73
+ schema.tables.push({
74
+ name: tableName,
75
+ columns,
76
+ indexes,
77
+ foreignKeys,
78
+ });
79
+ }
80
+
81
+ return schema;
82
+ }
83
+
84
+ async getTables() {
85
+ const rows = await this.db.connection.raw(SQL_QUERIES.TABLE_LIST);
86
+
87
+ return rows.map(row => row.name);
88
+ }
89
+
90
+ async getColumns(tableName) {
91
+ const rows = await this.db.connection.raw(SQL_QUERIES.TABLE_INFO, [tableName]);
92
+
93
+ return rows.map(row => {
94
+ const { type, args = [], ...rest } = toStrapiType(row);
95
+
96
+ return {
97
+ type,
98
+ args,
99
+ name: row.name,
100
+ defaultTo: row.dflt_value,
101
+ notNullable: row.notnull !== null ? Boolean(row.notnull) : null,
102
+ unsigned: false,
103
+ ...rest,
104
+ };
105
+ });
106
+ }
107
+
108
+ async getIndexes(tableName) {
109
+ const indexes = await this.db.connection.raw(SQL_QUERIES.INDEX_LIST, [tableName]);
110
+
111
+ const ret = [];
112
+
113
+ for (const index of indexes.filter(index => !index.name.startsWith('sqlite_'))) {
114
+ const res = await this.db.connection.raw(SQL_QUERIES.INDEX_INFO, [index.name]);
115
+
116
+ ret.push({
117
+ columns: res.map(row => row.name),
118
+ name: index.name,
119
+ type: index.unique ? 'unique' : null,
120
+ });
121
+ }
122
+
123
+ return ret;
124
+ }
125
+
126
+ async getForeignKeys(tableName) {
127
+ const fks = await this.db.connection.raw(SQL_QUERIES.FOREIGN_KEY_LIST, [tableName]);
128
+
129
+ const ret = {};
130
+
131
+ for (const fk of fks) {
132
+ if (!ret[fk.id]) {
133
+ ret[fk.id] = {
134
+ // TODO: name, // find name
135
+ columns: [fk.from],
136
+ referencedColumns: [fk.to],
137
+ referencedTable: fk.table,
138
+ onUpdate: fk.on_update.toUpperCase(),
139
+ onDelete: fk.on_delete.toUpperCase(),
140
+ };
141
+ } else {
142
+ ret[fk.id].columns.push(fk.from);
143
+ ret[fk.id].referencedColumns.push(fk.to);
144
+ }
145
+ }
146
+
147
+ return Object.values(ret);
148
+ }
149
+ }
150
+
151
+ module.exports = SqliteSchemaInspector;