leoric 2.3.0 → 2.4.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.
@@ -1,119 +0,0 @@
1
- 'use strict';
2
-
3
- const SqlString = require('sqlstring');
4
-
5
- const schema = require('../abstract/schema');
6
- const Attribute = require('./attribute');
7
- const { heresql } = require('../../utils/string');
8
-
9
- /**
10
- * - https://www.postgresql.org/docs/9.1/sql-altertable.html
11
- * @param {string} columnName
12
- * @param {Object} attribute
13
- */
14
- function formatAlterColumns(driver, columnName, attribute) {
15
- const { allowNull, type, defaultValue } = attribute;
16
- const sets = [
17
- `TYPE ${type.toSqlString()}`,
18
- allowNull ? 'DROP NOT NULL' : 'SET NOT NULL',
19
- defaultValue == null
20
- ? 'DROP DEFAULT'
21
- : `SET DEFAULT ${SqlString.escape(defaultValue)}`,
22
- ];
23
-
24
- return sets.map(entry => `ALTER COLUMN ${driver.escapeId(columnName)} ${entry}`);
25
- }
26
-
27
- function formatAddColumn(driver, columnName, attribute) {
28
- return `ADD COLUMN ${attribute.toSqlString()}`;
29
- }
30
-
31
- module.exports = {
32
- ...schema,
33
-
34
- async querySchemaInfo(database, tables) {
35
- tables = [].concat(tables);
36
- const text = heresql(`
37
- SELECT columns.*,
38
- constraints.constraint_type
39
- FROM information_schema.columns AS columns
40
- LEFT JOIN information_schema.key_column_usage AS usage
41
- ON columns.table_catalog = usage.table_catalog
42
- AND columns.table_name = usage.table_name
43
- AND columns.column_name = usage.column_name
44
- LEFT JOIN information_schema.table_constraints AS constraints
45
- ON usage.constraint_name = constraints.constraint_name
46
- WHERE columns.table_catalog = $1 AND columns.table_name = ANY($2)
47
- `);
48
-
49
- const { pool } = this;
50
- const { rows } = await pool.query(text, [database, tables]);
51
- const schemaInfo = {};
52
-
53
- for (const row of rows) {
54
- const tableName = row.table_name;
55
- const columns = schemaInfo[tableName] || (schemaInfo[tableName] = []);
56
- let { data_type: dataType, character_maximum_length: length } = row;
57
-
58
- if (dataType === 'character varying') dataType = 'varchar';
59
- if (dataType === 'timestamp without time zone') dataType = 'timestamp';
60
-
61
- let columnDefault = row.column_default;
62
- if (/^NULL::/i.test(columnDefault)) columnDefault = null;
63
- if (dataType === 'boolean') columnDefault = columnDefault === 'true';
64
-
65
- const primaryKey = row.constraint_type === 'PRIMARY KEY';
66
- const precision = row.datetime_precision;
67
-
68
- columns.push({
69
- columnName: row.column_name,
70
- columnType: length > 0 ? `${dataType}(${length})` : dataType,
71
- defaultValue: primaryKey ? null : columnDefault,
72
- dataType,
73
- allowNull: row.is_nullable !== 'NO',
74
- // https://www.postgresql.org/docs/9.5/infoschema-table-constraints.html
75
- primaryKey,
76
- unique: row.constraint_type === 'UNIQUE',
77
- datetimePrecision: precision === 6 ? null : precision,
78
- });
79
- }
80
-
81
- return schemaInfo;
82
- },
83
-
84
- async alterTable(table, changes) {
85
- const { escapeId } = this;
86
- const chunks = [ `ALTER TABLE ${escapeId(table)}` ];
87
- const actions = Object.keys(changes).map(name => {
88
- const attribute = new Attribute(name, changes[name]);
89
- const { columnName } = attribute;;
90
- return attribute.modify
91
- ? formatAlterColumns(this, columnName, attribute).join(', ')
92
- : formatAddColumn(this, columnName, attribute);
93
- });
94
- chunks.push(actions.join(', '));
95
- await this.query(chunks.join(' '));
96
- },
97
-
98
- async changeColumn(table, column, params) {
99
- const { escapeId } = this;
100
- const attribute = new Attribute(column, params);
101
- const { columnName } = attribute;
102
- const alterColumns = formatAlterColumns(this, columnName, attribute);
103
- const sql = heresql(`ALTER TABLE ${escapeId(table)} ${alterColumns.join(', ')}`);
104
- await this.query(sql);
105
- },
106
-
107
- /**
108
- * Truncate table
109
- * @param {string} table the name of the table to truncate
110
- * @param {Object} [opts={}] extra truncation options
111
- * @param {object} [opts.restartIdentity] restart sequences owned by the table
112
- */
113
- async truncateTable(table, opts = {}) {
114
- const { escapeId } = this;
115
- const chunks = [ `TRUNCATE TABLE ${escapeId(table)}` ];
116
- if (opts.restartIdentity) chunks.push('RESTART IDENTITY');
117
- await this.query(chunks.join(' '));
118
- },
119
- };
@@ -1,203 +0,0 @@
1
- 'use strict';
2
-
3
- const debug = require('debug')('leoric');
4
- const SqlString = require('sqlstring');
5
-
6
- const { parseExpr } = require('../../expr');
7
- const { heresql } = require('../../utils/string');
8
- const schema = require('../abstract/schema');
9
- const Attribute = require('./attribute');
10
-
11
- /**
12
- * Schema altering commands other than RENAME COLUMN or ADD COLUMN
13
- * - https://www.sqlite.org/lang_altertable.html
14
- * @param {string} table
15
- * @param {Object} attributes the changed attributes
16
- */
17
- async function alterTableWithChangeColumn(driver, table, changes) {
18
- const { escapeId } = driver;
19
- const schemaInfo = await driver.querySchemaInfo(null, table);
20
- const columns = schemaInfo[table];
21
-
22
- const changeMap = changes.reduce((result, entry) => {
23
- result[entry.columnName] = entry;
24
- return result;
25
- }, {});
26
-
27
- const newAttributes = [];
28
- for (const column of columns) {
29
- const { columnName } = column;
30
- const change = changeMap[columnName];
31
- if (!change || !change.remove) {
32
- newAttributes.push(Object.assign(column, change));
33
- }
34
- }
35
-
36
- for (const attribute of changes) {
37
- if (!attribute.modify && !attribute.remove) {
38
- newAttributes.push(attribute);
39
- }
40
- }
41
-
42
- const newColumns = [];
43
- for (const attribute of newAttributes) {
44
- const { columnName, defaultValue } = attribute;
45
- const change = changeMap[columnName];
46
- if (!change || change.modify) {
47
- newColumns.push(escapeId(columnName));
48
- } else {
49
- newColumns.push(SqlString.escape(defaultValue));
50
- }
51
- }
52
-
53
- const connection = await driver.getConnection();
54
- await connection.query('BEGIN');
55
- try {
56
- const newTable = `new_${table}`;
57
- await driver.createTable(newTable, newAttributes, { connection });
58
- await connection.query(heresql(`
59
- INSERT INTO ${escapeId(newTable)}
60
- SELECT ${newColumns.join(', ')}
61
- FROM ${escapeId(table)}
62
- `));
63
- await connection.query(`DROP TABLE ${escapeId(table)}`);
64
- await connection.query(heresql(`
65
- ALTER TABLE ${escapeId(newTable)}
66
- RENAME TO ${escapeId(table)}
67
- `));
68
- await connection.query('COMMIT');
69
- } catch (err) {
70
- await connection.query('ROLLBACK');
71
- throw err;
72
- } finally {
73
- await connection.release();
74
- }
75
- }
76
-
77
- // eslint-disable-next-line no-unused-vars
78
- function parseDefaultValue(text, type) {
79
- if (typeof text !== 'string') return text;
80
- if (type === 'boolean') return text === 'true';
81
-
82
- try {
83
- const ast = parseExpr(text);
84
- if (ast.type === 'literal') {
85
- return ast.value;
86
- }
87
- } catch (err) {
88
- debug('[parseDefaultValue] [%s] %s', text, err);
89
- }
90
-
91
- return text;
92
- }
93
-
94
- module.exports = {
95
- ...schema,
96
-
97
- async querySchemaInfo(database, tables) {
98
- tables = [].concat(tables);
99
-
100
- const queries = tables.map(table => {
101
- return this.query(`PRAGMA table_info(${this.escapeId(table)})`);
102
- });
103
- const results = await Promise.all(queries);
104
- const schemaInfo = {};
105
- const rColumnType = /^(\w+)(?:\(([^)]+)\))?/i;
106
- const rDateType = /(?:date|datetime|timestamp)/i;
107
-
108
- for (let i = 0; i < tables.length; i++) {
109
- const table = tables[i];
110
- const { rows } = results[i];
111
- const columns = rows.map(({ name, type, notnull, dflt_value, pk }) => {
112
- const columnType = type.toLowerCase();
113
- const [, dataType, precision ] = columnType.match(rColumnType);
114
- const primaryKey = pk === 1;
115
-
116
- const result = {
117
- columnName: name,
118
- columnType,
119
- defaultValue: parseDefaultValue(dflt_value, type),
120
- dataType: dataType,
121
- allowNull: primaryKey ? false : notnull == 0,
122
- primaryKey,
123
- datetimePrecision: rDateType.test(dataType) ? parseInt(precision, 10) : null,
124
- };
125
- return result;
126
- });
127
- if (columns.length > 0) schemaInfo[table] = columns;
128
- }
129
-
130
- return schemaInfo;
131
- },
132
-
133
- async createTable(table, attributes, opts = {}) {
134
- const { escapeId } = this;
135
- const chunks = [ `CREATE TABLE ${escapeId(table)}` ];
136
- const columns = Object.keys(attributes).map(name => {
137
- const attribute = new Attribute(name, attributes[name]);
138
- return attribute.toSqlString();
139
- });
140
- chunks.push(`(${columns.join(', ')})`);
141
- await this.query(chunks.join(' '), [], opts);
142
- },
143
-
144
- async alterTable(table, changes) {
145
- const { escapeId } = this;
146
- const chunks = [ `ALTER TABLE ${escapeId(table)}` ];
147
- const attributes = Object.keys(changes).map(name => {
148
- return new Attribute(name, changes[name]);
149
- });
150
-
151
- // SQLite doesn't support altering column attributes with MODIFY COLUMN and adding a PRIMARY KEY column
152
- if (attributes.some(entry => entry.modify || entry.primaryKey)) {
153
- await alterTableWithChangeColumn(this, table, attributes);
154
- return;
155
- }
156
-
157
- // SQLite can only add one column a time
158
- // - https://www.sqlite.org/lang_altertable.html
159
- for (const attribute of attributes) {
160
- await this.query(chunks.concat(`ADD COLUMN ${attribute.toSqlString()}`).join(' '));
161
- }
162
- },
163
-
164
- async addColumn(table, name, params) {
165
- const { escapeId } = this;
166
- const attribute = new Attribute(name, params);
167
- const sql = heresql(`
168
- ALTER TABLE ${escapeId(table)}
169
- ADD COLUMN ${attribute.toSqlString()}
170
- `);
171
- await this.query(sql);
172
- },
173
-
174
- async changeColumn(table, name, params) {
175
- const attribute = new Attribute(name, params);
176
- const schemaInfo = await this.querySchemaInfo(null, table);
177
- const columns = schemaInfo[table];
178
-
179
- for (const entry of columns) {
180
- if (entry.columnName === attribute.columnName) {
181
- Object.assign(entry, attribute, { modify: true });
182
- }
183
- }
184
-
185
- await this.alterTable(table, columns);
186
- },
187
-
188
- async removeColumn(table, name) {
189
- const attribute = new Attribute(name);
190
- attribute.remove = true;
191
- const changes = [ attribute ];
192
- await alterTableWithChangeColumn(this, table, changes);
193
- },
194
-
195
- /**
196
- * SQLite has only got implicit table truncation.
197
- * - https://sqlite.org/lang_delete.html#the_truncate_optimization
198
- */
199
- async truncateTable(table) {
200
- const { escapeId } = this;
201
- await this.query(`DELETE FROM ${escapeId(table)}`);
202
- }
203
- };