@proteinjs/db-driver-spanner 1.1.1 → 1.1.3
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/.eslintrc.js +20 -0
- package/.prettierignore +3 -0
- package/.prettierrc +8 -0
- package/CHANGELOG.md +14 -20
- package/LICENSE +21 -0
- package/README.md +29 -29
- package/dist/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/src/SpannerColumnTypeFactory.d.ts.map +1 -1
- package/dist/src/SpannerColumnTypeFactory.js +17 -9
- package/dist/src/SpannerColumnTypeFactory.js.map +1 -1
- package/dist/src/SpannerConfig.d.ts.map +1 -1
- package/dist/src/SpannerDriver.d.ts.map +1 -1
- package/dist/src/SpannerDriver.js +22 -9
- package/dist/src/SpannerDriver.js.map +1 -1
- package/dist/src/SpannerSchemaMetadata.d.ts.map +1 -1
- package/dist/src/SpannerSchemaMetadata.js +25 -14
- package/dist/src/SpannerSchemaMetadata.js.map +1 -1
- package/dist/src/SpannerSchemaOperations.d.ts.map +1 -1
- package/dist/src/SpannerSchemaOperations.js +5 -1
- package/dist/src/SpannerSchemaOperations.js.map +1 -1
- package/dist/test/Crud.test.js.map +1 -1
- package/dist/test/TableManager.test.js.map +1 -1
- package/dist/test/dropTable.d.ts.map +1 -1
- package/dist/test/dropTable.js +7 -2
- package/dist/test/dropTable.js.map +1 -1
- package/generated/index.ts +6 -9
- package/index.ts +1 -1
- package/jest.config.js +9 -18
- package/package.json +46 -40
- package/src/SpannerColumnTypeFactory.ts +23 -12
- package/src/SpannerConfig.ts +6 -6
- package/src/SpannerDriver.ts +125 -107
- package/src/SpannerSchemaMetadata.ts +74 -50
- package/src/SpannerSchemaOperations.ts +51 -26
- package/test/Crud.test.ts +7 -13
- package/test/TableManager.test.ts +12 -17
- package/test/dropTable.ts +16 -7
- package/test/setup.js +1 -1
- package/tsconfig.json +18 -22
package/src/SpannerDriver.ts
CHANGED
@@ -8,111 +8,129 @@ import { SpannerColumnTypeFactory } from './SpannerColumnTypeFactory';
|
|
8
8
|
import { SpannerSchemaMetadata } from './SpannerSchemaMetadata';
|
9
9
|
|
10
10
|
export class SpannerDriver implements DbDriver {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
11
|
+
private static SPANNER: Spanner;
|
12
|
+
private static SPANNER_INSTANCE: Instance;
|
13
|
+
private static SPANNER_DB: Database;
|
14
|
+
private logger = new Logger(this.constructor.name);
|
15
|
+
private config: SpannerConfig;
|
16
|
+
|
17
|
+
constructor(config: SpannerConfig) {
|
18
|
+
this.config = config;
|
19
|
+
}
|
20
|
+
|
21
|
+
private getSpanner(): Spanner {
|
22
|
+
if (!SpannerDriver.SPANNER) {
|
23
|
+
if (this.config.spannerOptions) {
|
24
|
+
SpannerDriver.SPANNER = new Spanner(
|
25
|
+
Object.assign({ projectId: this.config.projectId }, this.config.spannerOptions)
|
26
|
+
);
|
27
|
+
} else {
|
28
|
+
SpannerDriver.SPANNER = new Spanner({ projectId: this.config.projectId });
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
return SpannerDriver.SPANNER;
|
33
|
+
}
|
34
|
+
|
35
|
+
private getSpannerInstance(): Instance {
|
36
|
+
if (!SpannerDriver.SPANNER_INSTANCE) {
|
37
|
+
SpannerDriver.SPANNER_INSTANCE = this.getSpanner().instance(this.config.instanceName);
|
38
|
+
}
|
39
|
+
|
40
|
+
return SpannerDriver.SPANNER_INSTANCE;
|
41
|
+
}
|
42
|
+
|
43
|
+
private getSpannerDb(): Database {
|
44
|
+
if (!SpannerDriver.SPANNER_DB) {
|
45
|
+
SpannerDriver.SPANNER_DB = this.getSpannerInstance().database(this.config.databaseName);
|
46
|
+
}
|
47
|
+
|
48
|
+
return SpannerDriver.SPANNER_DB;
|
49
|
+
}
|
50
|
+
|
51
|
+
getDbName() {
|
52
|
+
return this.config.databaseName;
|
53
|
+
}
|
54
|
+
|
55
|
+
getTableManager(): TableManager {
|
56
|
+
const columnTypeFactory = new SpannerColumnTypeFactory();
|
57
|
+
const schemaOperations = new SpannerSchemaOperations(this);
|
58
|
+
const schemaMetadata = new SpannerSchemaMetadata(this, false);
|
59
|
+
return new TableManager(this, columnTypeFactory, schemaOperations, schemaMetadata);
|
60
|
+
}
|
61
|
+
|
62
|
+
async createDbIfNotExists(): Promise<void> {
|
63
|
+
if (await this.dbExists(this.getDbName())) {
|
64
|
+
return;
|
65
|
+
}
|
66
|
+
|
67
|
+
await this.getSpannerInstance().createDatabase(this.getDbName());
|
68
|
+
}
|
69
|
+
|
70
|
+
private async dbExists(databaseName: string): Promise<boolean> {
|
71
|
+
const [exists] = await this.getSpannerInstance().database(databaseName).exists();
|
72
|
+
return exists;
|
73
|
+
}
|
74
|
+
|
75
|
+
async runQuery(generateStatement: (config: DbDriverStatementConfig) => Statement): Promise<any[]> {
|
76
|
+
const { sql, namedParams } = generateStatement({
|
77
|
+
useParams: true,
|
78
|
+
useNamedParams: true,
|
79
|
+
prefixTablesWithDb: false,
|
80
|
+
});
|
81
|
+
try {
|
82
|
+
this.logger.debug(`Executing query: ${sql}`);
|
83
|
+
const [rows] = await this.getSpannerDb().run({ sql, params: namedParams?.params });
|
84
|
+
return rows.map((row) => row.toJSON());
|
85
|
+
// return JSON.parse(JSON.stringify((await this.getSpannerDb().run({ sql, params: namedParams?.params }))[0]));
|
86
|
+
} catch (error: any) {
|
87
|
+
this.logger.error(
|
88
|
+
`Failed when executing query: ${sql}\nparams: ${JSON.stringify(namedParams, null, 2)}\nreason: ${error.details}`
|
89
|
+
);
|
90
|
+
throw error;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Execute a write operation.
|
96
|
+
*
|
97
|
+
* @returns number of affected rows
|
98
|
+
*/
|
99
|
+
async runDml(generateStatement: (config: DbDriverStatementConfig) => Statement): Promise<number> {
|
100
|
+
const { sql, namedParams } = generateStatement({
|
101
|
+
useParams: true,
|
102
|
+
useNamedParams: true,
|
103
|
+
prefixTablesWithDb: false,
|
104
|
+
});
|
105
|
+
try {
|
106
|
+
return await this.getSpannerDb().runTransactionAsync(async (transaction) => {
|
107
|
+
this.logger.debug(`Executing dml: ${sql}`);
|
108
|
+
const [rowCount] = await transaction.runUpdate({
|
109
|
+
sql,
|
110
|
+
params: namedParams?.params,
|
111
|
+
});
|
112
|
+
await transaction.commit();
|
113
|
+
return rowCount;
|
114
|
+
});
|
115
|
+
} catch (error: any) {
|
116
|
+
this.logger.error(
|
117
|
+
`Failed when executing dml: ${sql}\nparams: ${JSON.stringify(namedParams, null, 2)}\nreason: ${error.details}`
|
118
|
+
);
|
119
|
+
throw error;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
* Execute a schema write operation.
|
125
|
+
*/
|
126
|
+
async runUpdateSchema(sql: string): Promise<void> {
|
127
|
+
try {
|
128
|
+
this.logger.debug(`Executing schema update: ${sql}`);
|
129
|
+
const [operation] = await this.getSpannerDb().updateSchema(sql);
|
112
130
|
await operation.promise();
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
}
|
131
|
+
} catch (error: any) {
|
132
|
+
this.logger.error(`Failed when executing schema update: ${sql}\nreason: ${error.details}`);
|
133
|
+
throw error;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
@@ -2,58 +2,80 @@ import { ParameterizationConfig, QueryBuilder, SchemaMetadata, Table } from '@pr
|
|
2
2
|
|
3
3
|
export class SpannerSchemaMetadata extends SchemaMetadata {
|
4
4
|
async tableExists(table: Table<any>): Promise<boolean> {
|
5
|
-
|
6
|
-
const generateStatement = (config: ParameterizationConfig) => qb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
7
|
-
const results = await this.dbDriver.runQuery(generateStatement);
|
8
|
-
return results.length > 0;
|
9
|
-
}
|
10
|
-
|
11
|
-
async getColumnMetadata(table: Table<any>): Promise<{[columnName: string]: { type: string, isNullable: boolean }}> {
|
12
|
-
const qb = QueryBuilder.fromObject({
|
13
|
-
'TABLE_NAME': table.name
|
14
|
-
}, 'COLUMNS');
|
5
|
+
const qb = new QueryBuilder('TABLES').condition({ field: 'TABLE_NAME', operator: '=', value: table.name });
|
15
6
|
const generateStatement = (config: ParameterizationConfig) => qb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
const results = await this.dbDriver.runQuery(generateStatement);
|
8
|
+
return results.length > 0;
|
9
|
+
}
|
10
|
+
|
11
|
+
async getColumnMetadata(table: Table<any>): Promise<{ [columnName: string]: { type: string; isNullable: boolean } }> {
|
12
|
+
const qb = QueryBuilder.fromObject(
|
13
|
+
{
|
14
|
+
TABLE_NAME: table.name,
|
15
|
+
},
|
16
|
+
'COLUMNS'
|
17
|
+
);
|
18
|
+
const generateStatement = (config: ParameterizationConfig) => qb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
19
|
+
const results = await this.dbDriver.runQuery(generateStatement);
|
20
|
+
const columnMetadata: { [columnName: string]: { type: string; isNullable: boolean } } = {};
|
21
|
+
for (const row of results) {
|
19
22
|
columnMetadata[row['COLUMN_NAME']] = { type: row['SPANNER_TYPE'], isNullable: row['IS_NULLABLE'] === 'YES' };
|
20
|
-
|
23
|
+
}
|
24
|
+
|
21
25
|
return columnMetadata;
|
22
26
|
}
|
23
27
|
|
24
28
|
async getPrimaryKey(table: Table<any>): Promise<string[]> {
|
25
|
-
|
29
|
+
const indexes = await this.getIndexes(table);
|
26
30
|
return indexes['PRIMARY_KEY'];
|
27
31
|
}
|
28
32
|
|
29
|
-
async getForeignKeys(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
async getForeignKeys(
|
34
|
+
table: Table<any>
|
35
|
+
): Promise<{ [columnName: string]: { referencedTableName: string; referencedColumnName: string } }> {
|
36
|
+
const tableConstraintsQb = QueryBuilder.fromObject(
|
37
|
+
{
|
38
|
+
TABLE_NAME: table.name,
|
39
|
+
CONSTRAINT_TYPE: 'FOREIGN KEY',
|
40
|
+
},
|
41
|
+
'TABLE_CONSTRAINTS'
|
42
|
+
);
|
43
|
+
const generateTableConstraintsStatement = (config: ParameterizationConfig) =>
|
44
|
+
tableConstraintsQb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
45
|
+
const tableConstraints = await this.dbDriver.runQuery(generateTableConstraintsStatement);
|
46
|
+
const foreignKeys: { [columnName: string]: { referencedTableName: string; referencedColumnName: string } } = {};
|
37
47
|
for (const tableConstraint of tableConstraints) {
|
38
|
-
const referentialConstraintsQb = QueryBuilder.fromObject(
|
39
|
-
|
40
|
-
|
41
|
-
|
48
|
+
const referentialConstraintsQb = QueryBuilder.fromObject(
|
49
|
+
{
|
50
|
+
CONSTRAINT_NAME: tableConstraint['CONSTRAINT_NAME'],
|
51
|
+
},
|
52
|
+
'REFERENTIAL_CONSTRAINTS'
|
53
|
+
);
|
54
|
+
const generateReferentialConstraintsStatement = (config: ParameterizationConfig) =>
|
55
|
+
referentialConstraintsQb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
42
56
|
const referentialConstraints = await this.dbDriver.runQuery(generateReferentialConstraintsStatement);
|
43
57
|
const referencedTableConstraintName = referentialConstraints[0]['UNIQUE_CONSTRAINT_NAME'];
|
44
|
-
|
45
|
-
const referenceTableConstraintsQb = QueryBuilder.fromObject(
|
46
|
-
|
47
|
-
|
48
|
-
|
58
|
+
|
59
|
+
const referenceTableConstraintsQb = QueryBuilder.fromObject(
|
60
|
+
{
|
61
|
+
CONSTRAINT_NAME: referencedTableConstraintName,
|
62
|
+
},
|
63
|
+
'TABLE_CONSTRAINTS'
|
64
|
+
);
|
65
|
+
const generateReferenceTableConstraintsStatement = (config: ParameterizationConfig) =>
|
66
|
+
referenceTableConstraintsQb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
49
67
|
const referenceTableConstraints = await this.dbDriver.runQuery(generateReferenceTableConstraintsStatement);
|
50
68
|
const referencedTableName = referenceTableConstraints[0]['TABLE_NAME'];
|
51
69
|
|
52
|
-
const keyColumnUsageQb = QueryBuilder.fromObject(
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
70
|
+
const keyColumnUsageQb = QueryBuilder.fromObject(
|
71
|
+
{
|
72
|
+
TABLE_NAME: table.name,
|
73
|
+
CONSTRAINT_NAME: tableConstraint['CONSTRAINT_NAME'],
|
74
|
+
},
|
75
|
+
'KEY_COLUMN_USAGE'
|
76
|
+
);
|
77
|
+
const generateKeyColumnUsageStatement = (config: ParameterizationConfig) =>
|
78
|
+
keyColumnUsageQb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
57
79
|
const keyColumnUsages = await this.dbDriver.runQuery(generateKeyColumnUsageStatement);
|
58
80
|
const referencingColumnName = keyColumnUsages[0]['COLUMN_NAME'];
|
59
81
|
|
@@ -62,7 +84,7 @@ export class SpannerSchemaMetadata extends SchemaMetadata {
|
|
62
84
|
referencedColumnName: 'id',
|
63
85
|
};
|
64
86
|
}
|
65
|
-
|
87
|
+
|
66
88
|
return foreignKeys;
|
67
89
|
}
|
68
90
|
|
@@ -70,34 +92,36 @@ export class SpannerSchemaMetadata extends SchemaMetadata {
|
|
70
92
|
const indexes = await this.getIndexes(table);
|
71
93
|
const uniqueColumns: string[] = [];
|
72
94
|
for (const indexName in indexes) {
|
73
|
-
if (!indexName.endsWith('_unique'))
|
95
|
+
if (!indexName.endsWith('_unique')) {
|
74
96
|
continue;
|
75
|
-
|
97
|
+
}
|
98
|
+
|
76
99
|
const indexedColumns = indexes[indexName];
|
77
100
|
uniqueColumns.push(...indexedColumns);
|
78
101
|
}
|
79
|
-
|
102
|
+
|
80
103
|
return uniqueColumns;
|
81
104
|
}
|
82
105
|
|
83
|
-
async getIndexes(table: Table<any>): Promise<{[keyName: string]: string[]}> {
|
106
|
+
async getIndexes(table: Table<any>): Promise<{ [keyName: string]: string[] }> {
|
84
107
|
const qb = new QueryBuilder('INDEX_COLUMNS')
|
85
108
|
.condition({ field: 'TABLE_NAME', operator: '=', value: table.name })
|
86
|
-
.sort([{ field: 'ORDINAL_POSITION', desc: false }])
|
87
|
-
;
|
109
|
+
.sort([{ field: 'ORDINAL_POSITION', desc: false }]);
|
88
110
|
const generateStatement = (config: ParameterizationConfig) => qb.toSql({ dbName: 'INFORMATION_SCHEMA', ...config });
|
89
|
-
|
111
|
+
const results: any[] = await this.dbDriver.runQuery(generateStatement);
|
90
112
|
const indexes: { [keyName: string]: string[] } = {};
|
91
113
|
for (const row of results) {
|
92
|
-
if (!row['INDEX_NAME'])
|
114
|
+
if (!row['INDEX_NAME']) {
|
93
115
|
continue;
|
116
|
+
}
|
94
117
|
|
95
|
-
if (!indexes[row['INDEX_NAME']])
|
118
|
+
if (!indexes[row['INDEX_NAME']]) {
|
96
119
|
indexes[row['INDEX_NAME']] = [];
|
97
|
-
|
120
|
+
}
|
121
|
+
|
98
122
|
indexes[row['INDEX_NAME']].push(row['COLUMN_NAME']);
|
99
123
|
}
|
100
|
-
|
124
|
+
|
101
125
|
return indexes;
|
102
126
|
}
|
103
|
-
}
|
127
|
+
}
|
@@ -1,33 +1,39 @@
|
|
1
1
|
import { Logger } from '@proteinjs/util';
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
Table,
|
4
|
+
SchemaOperations,
|
5
|
+
TableChanges,
|
6
|
+
StatementFactory,
|
7
|
+
AlterTableParams,
|
8
|
+
StatementUtil,
|
9
|
+
getColumnByName,
|
10
|
+
} from '@proteinjs/db';
|
3
11
|
import { SpannerDriver } from './SpannerDriver';
|
4
12
|
import { SpannerColumnTypeFactory } from './SpannerColumnTypeFactory';
|
5
13
|
|
6
14
|
export class SpannerSchemaOperations implements SchemaOperations {
|
7
15
|
private logger = new Logger(this.constructor.name);
|
8
16
|
|
9
|
-
constructor(
|
10
|
-
private spannerDriver: SpannerDriver
|
11
|
-
){}
|
17
|
+
constructor(private spannerDriver: SpannerDriver) {}
|
12
18
|
|
13
19
|
async createTable(table: Table<any>) {
|
14
|
-
const indexes: { name?: string
|
15
|
-
for (
|
20
|
+
const indexes: { name?: string; columns: string | string[]; unique?: boolean }[] = [];
|
21
|
+
for (const index of table.indexes) {
|
16
22
|
indexes.push({ name: index.name, columns: index.columns as string[] });
|
17
23
|
}
|
18
24
|
|
19
|
-
const serializedColumns: { name: string
|
20
|
-
const foreignKeys: { table: string
|
25
|
+
const serializedColumns: { name: string; type: string; nullable?: boolean }[] = [];
|
26
|
+
const foreignKeys: { table: string; column: string; referencedByColumn: string }[] = [];
|
21
27
|
for (const columnPropertyName in table.columns) {
|
22
28
|
const column = table.columns[columnPropertyName];
|
23
29
|
const columnType = new SpannerColumnTypeFactory().getType(column);
|
24
30
|
serializedColumns.push({ name: column.name, type: columnType, nullable: column.options?.nullable });
|
25
|
-
this.logger.info(`[${table.name}] Creating column: ${column.name} (${column.constructor.name})`)
|
31
|
+
this.logger.info(`[${table.name}] Creating column: ${column.name} (${column.constructor.name})`);
|
26
32
|
if (column.options?.unique?.unique) {
|
27
33
|
indexes.push({ name: column.options.unique.indexName, columns: column.name, unique: true });
|
28
34
|
this.logger.info(`[${table.name}.${column.name}] Adding unique constraint`);
|
29
35
|
}
|
30
|
-
|
36
|
+
|
31
37
|
if (column.options?.references) {
|
32
38
|
foreignKeys.push({ table: column.options.references.table, column: 'id', referencedByColumn: column.name });
|
33
39
|
this.logger.info(`[${table.name}.${column.name}] Adding foreign key -> ${column.options.references.table}.id`);
|
@@ -36,12 +42,16 @@ export class SpannerSchemaOperations implements SchemaOperations {
|
|
36
42
|
const createTableSql = new StatementFactory().createTable(table.name, serializedColumns, 'id', foreignKeys).sql;
|
37
43
|
await this.spannerDriver.runUpdateSchema(createTableSql);
|
38
44
|
|
39
|
-
for (
|
45
|
+
for (const index of indexes) {
|
40
46
|
const createIndexSql = new StatementFactory().createIndex(index, table.name).sql;
|
41
47
|
const indexName = StatementUtil.getIndexName(table.name, index);
|
42
|
-
this.logger.info(
|
48
|
+
this.logger.info(
|
49
|
+
`[${table.name}] Creating index: ${indexName} (${typeof index.columns === 'string' ? index.columns : index.columns.join(', ')})`
|
50
|
+
);
|
43
51
|
await this.spannerDriver.runUpdateSchema(createIndexSql);
|
44
|
-
this.logger.info(
|
52
|
+
this.logger.info(
|
53
|
+
`[${table.name}] Created index: ${indexName} (${typeof index.columns === 'string' ? index.columns : index.columns.join(', ')})`
|
54
|
+
);
|
45
55
|
}
|
46
56
|
}
|
47
57
|
|
@@ -55,7 +65,7 @@ export class SpannerSchemaOperations implements SchemaOperations {
|
|
55
65
|
};
|
56
66
|
const indexesToDrop = tableChanges.indexesToDrop;
|
57
67
|
const indexesToAdd = tableChanges.indexesToCreate;
|
58
|
-
|
68
|
+
for (const columnPropertyName of tableChanges.columnsToCreate) {
|
59
69
|
const column = table.columns[columnPropertyName];
|
60
70
|
const columnType = new SpannerColumnTypeFactory().getType(column);
|
61
71
|
alterParams.columnsToAdd?.push({ name: column.name, type: columnType, nullable: column.options?.nullable });
|
@@ -64,9 +74,13 @@ export class SpannerSchemaOperations implements SchemaOperations {
|
|
64
74
|
indexesToAdd.push({ name: column.options.unique.indexName, columns: column.name, unique: true });
|
65
75
|
this.logger.info(`[${table.name}.${column.name}] Adding unique constraint`);
|
66
76
|
}
|
67
|
-
|
77
|
+
|
68
78
|
if (column.options?.references && tableChanges.columnsWithForeignKeysToCreate.includes(column.name)) {
|
69
|
-
alterParams.foreignKeysToAdd?.push({
|
79
|
+
alterParams.foreignKeysToAdd?.push({
|
80
|
+
table: column.options.references.table,
|
81
|
+
column: 'id',
|
82
|
+
referencedByColumn: column.name,
|
83
|
+
});
|
70
84
|
this.logger.info(`[${table.name}.${column.name}] Adding foreign key -> ${column.options.references.table}.id`);
|
71
85
|
}
|
72
86
|
}
|
@@ -78,7 +92,9 @@ export class SpannerSchemaOperations implements SchemaOperations {
|
|
78
92
|
|
79
93
|
for (const foreignKey of tableChanges.foreignKeysToDrop) {
|
80
94
|
alterParams.foreignKeysToDrop?.push(foreignKey);
|
81
|
-
this.logger.info(
|
95
|
+
this.logger.info(
|
96
|
+
`[${table.name}.${foreignKey.referencedByColumn}] Dropping foreign key -> ${foreignKey.table}.${foreignKey.column}`
|
97
|
+
);
|
82
98
|
}
|
83
99
|
|
84
100
|
for (const columnTypeChange of tableChanges.columnTypeChanges) {
|
@@ -101,22 +117,31 @@ export class SpannerSchemaOperations implements SchemaOperations {
|
|
101
117
|
}
|
102
118
|
|
103
119
|
const alterStatements = new StatementFactory().alterTable(alterParams);
|
104
|
-
for (
|
120
|
+
for (const alterStatement of alterStatements) {
|
105
121
|
await this.spannerDriver.runUpdateSchema(alterStatement.sql);
|
122
|
+
}
|
106
123
|
|
107
|
-
for (
|
124
|
+
for (const index of tableChanges.indexesToDrop) {
|
108
125
|
const dropIndexSql = new StatementFactory().dropIndex(index, table.name).sql;
|
109
|
-
this.logger.info(
|
126
|
+
this.logger.info(
|
127
|
+
`[${table.name}] Dropping index: ${index.name} (${typeof index.columns === 'string' ? index.columns : index.columns.join(', ')})`
|
128
|
+
);
|
110
129
|
await this.spannerDriver.runUpdateSchema(dropIndexSql);
|
111
|
-
this.logger.info(
|
130
|
+
this.logger.info(
|
131
|
+
`[${table.name}] Dropped index: ${index.name} (${typeof index.columns === 'string' ? index.columns : index.columns.join(', ')})`
|
132
|
+
);
|
112
133
|
}
|
113
134
|
|
114
|
-
for (
|
135
|
+
for (const index of tableChanges.indexesToCreate) {
|
115
136
|
const createIndexSql = new StatementFactory().createIndex(index, table.name).sql;
|
116
137
|
const indexName = StatementUtil.getIndexName(table.name, index);
|
117
|
-
this.logger.info(
|
138
|
+
this.logger.info(
|
139
|
+
`[${table.name}] Creating index: ${indexName} (${typeof index.columns === 'string' ? index.columns : index.columns.join(', ')})`
|
140
|
+
);
|
118
141
|
await this.spannerDriver.runUpdateSchema(createIndexSql);
|
119
|
-
this.logger.info(
|
142
|
+
this.logger.info(
|
143
|
+
`[${table.name}] Created index: ${indexName} (${typeof index.columns === 'string' ? index.columns : index.columns.join(', ')})`
|
144
|
+
);
|
120
145
|
}
|
121
|
-
|
122
|
-
}
|
146
|
+
}
|
147
|
+
}
|
package/test/Crud.test.ts
CHANGED
@@ -1,17 +1,11 @@
|
|
1
|
-
import { crudTests } from '@proteinjs/db'
|
2
|
-
import { SpannerDriver } from '../src/SpannerDriver'
|
3
|
-
import { getDropTable } from './dropTable'
|
1
|
+
import { crudTests } from '@proteinjs/db';
|
2
|
+
import { SpannerDriver } from '../src/SpannerDriver';
|
3
|
+
import { getDropTable } from './dropTable';
|
4
4
|
|
5
5
|
const spannerDriver = new SpannerDriver({
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
projectId: 'proteinjs-test',
|
7
|
+
instanceName: 'proteinjs-test',
|
8
|
+
databaseName: 'test',
|
9
9
|
});
|
10
10
|
|
11
|
-
describe(
|
12
|
-
'CRUD Tests',
|
13
|
-
crudTests(
|
14
|
-
spannerDriver,
|
15
|
-
getDropTable(spannerDriver)
|
16
|
-
)
|
17
|
-
);
|
11
|
+
describe('CRUD Tests', crudTests(spannerDriver, getDropTable(spannerDriver)));
|
@@ -1,24 +1,19 @@
|
|
1
|
-
import { tableManagerTests } from '@proteinjs/db'
|
2
|
-
import { SpannerDriver } from '../src/SpannerDriver'
|
1
|
+
import { tableManagerTests } from '@proteinjs/db';
|
2
|
+
import { SpannerDriver } from '../src/SpannerDriver';
|
3
3
|
import { SpannerColumnTypeFactory } from '../src/SpannerColumnTypeFactory';
|
4
4
|
import { getDropTable } from './dropTable';
|
5
5
|
|
6
6
|
const spannerDriver = new SpannerDriver({
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
projectId: 'proteinjs-test',
|
8
|
+
instanceName: 'proteinjs-test',
|
9
|
+
databaseName: 'test',
|
10
10
|
});
|
11
11
|
|
12
12
|
describe(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
alterColumnTypes: true,
|
21
|
-
alterNullableConstraint: true,
|
22
|
-
}
|
23
|
-
)
|
24
|
-
);
|
13
|
+
'Table Manager Tests',
|
14
|
+
tableManagerTests(spannerDriver, getDropTable(spannerDriver), new SpannerColumnTypeFactory().getType, {
|
15
|
+
alterColumnName: true,
|
16
|
+
alterColumnTypes: true,
|
17
|
+
alterNullableConstraint: true,
|
18
|
+
})
|
19
|
+
);
|