-
Notifications
You must be signed in to change notification settings - Fork 1
Migrations #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Migrations #13
Conversation
|
@nazrhom, status checks have failed for this PR. Please make appropriate changes and recommit. |
test/sbvr/test.coffee
Outdated
| migration = AbstractSQLCompiler.postgres.diffSchemas(srcSchema, dstSchema) | ||
| console.log('GOT MIGRATION', migration) | ||
| catch e | ||
| throw e |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might as well drop the try/catch altogether if you're just going to rethrow :P
test/sbvr/test.coffee
Outdated
| catch e | ||
| throw e | ||
| for i in [0...Math.max(migration.length, expectation.length)] | ||
| expect(migration[i]).to.equal(expectation[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be able to be
expect(migration).to.deep.equal(expectation)and avoid the need for the loop
test/sbvr/test.coffee
Outdated
| dstSchema = LF2AbstractSQLTranslator(dstLf, 'Process') | ||
|
|
||
| migration = AbstractSQLCompiler.postgres.diffSchemas(srcSchema, dstSchema) | ||
| console.log('GOT MIGRATION', migration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug info?
| ''', [ ''' | ||
| ALTER TABLE "pilot-can fly-plane" | ||
| RENAME TO "plane-can be flown by-pilot"; | ||
| RENAME COLUMN "pilot" TO "can be flown by-pilot"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is invalid and should be
ALTER TABLE "plane-can be flown by-pilot"
RENAME COLUMN "pilot" TO "can be flown by-pilot";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first instruction is there to rename the the table representing the relation. I used https://www.postgresql.org/docs/9.1/static/sql-altertable.html for reference but I think that it is pretty consistently supported by different dbs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that reference should be valid in general, my point is that the generated SQL is invalid according to that reference. Try running the create schema statements for the first model to set it up and then running the migration statements, I'm pretty sure they will fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, also the change was referring to just the line I commented on, for reference I would expect the full block to be:
[ '''
ALTER TABLE "pilot-can fly-plane"
RENAME TO "plane-can be flown by-pilot";
ALTER TABLE "plane-can be flown by-pilot"
RENAME COLUMN "pilot" TO "can be flown by-pilot";
ALTER TABLE "plane-can be flown by-pilot"
RENAME COLUMN "can fly-plane" TO "plane";
''' ]or
[ '''
ALTER TABLE "pilot-can fly-plane"
RENAME TO "plane-can be flown by-pilot";
''', '''
ALTER TABLE "plane-can be flown by-pilot"
RENAME COLUMN "pilot" TO "can be flown by-pilot";
''', '''
ALTER TABLE "plane-can be flown by-pilot"
RENAME COLUMN "can fly-plane" TO "plane";
''' ]
AbstractSQLCompiler.ts
Outdated
| export const postgres = generateExport(Engines.postgres, true) | ||
| export const mysql = generateExport(Engines.mysql, true) | ||
| export const websql = generateExport(Engines.websql, false) | ||
| export const websql = generateExport(Engines.websql, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put the newline back please
AbstractSQLCompiler.ts
Outdated
| } | ||
|
|
||
| const compileSchema = (abstractSqlModel: AbstractSqlModel, engine: Engines, ifNotExists: boolean): SqlModel => { | ||
| let { hasDependants, schemaDependencyMap } = mkSchemaDependencyMap(abstractSqlModel.tables, engine, ifNotExists) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer const here
AbstractSQLCompiler.ts
Outdated
| fields?: {}[] | ||
| } | ||
|
|
||
| type ReduceFn<T> = (accumulator: any, element: T) => any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer this to be better typed, my guess is you can do that for your purposes as
type ReduceFn<T> = (accumulator: T[], element: T) => T[]
AbstractSQLCompiler.ts
Outdated
| [dependant: string]: true | ||
| } | ||
|
|
||
| export interface schemaDependencyMap { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use CamelCase for these interfaces please, to distinguish them from vars
AbstractSQLCompiler.ts
Outdated
| } | ||
| } | ||
|
|
||
| const insFn: ReduceFn<AbstractSqlTable> = (acc, table ) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have a stray space in here
AbstractSQLCompiler.ts
Outdated
| // the argument or undefined if no such element is found. | ||
| const genDiff = <T>(insFn: ReduceFn<T>, delFn: ReduceFn<T>, modFn: ReduceFn<Pair<T>>, matchFn: MatchFn<T>, src: Array<T>, dst: Array<T>) => { | ||
| let split = genSplit(src, dst, matchFn) | ||
| return _.flatten(_.reduce(split.ins, insFn, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all this code could be made much nice with either
_.flatMap(split.mod, modFn)
.concat(_.flatMap(split.del, delFn))
.concat(_.flatMap(split.ins, insFn))and then return [] to include nothing, and return [a, b, c] to return multiple. It avoids all the need for mutation and overall should end up much nicer imo. The alternative if you only ever need to return 0 or 1 results (never > 1) is:
_.map(split.mod, modFn)
.concat(_.map(split.del, delFn))
.concat(_.map(split.ins, insFn))
.reject(_.isNil)and return for nothing, return a for something
|
@nazrhom, status checks have failed for this PR. Please make appropriate changes and recommit. |
|
@nazrhom, status checks have failed for this PR. Please make appropriate changes and recommit. |
AbstractSQLCompiler.ts
Outdated
| fields?: {}[] | ||
| } | ||
|
|
||
| type ReduceFn<T> = (element: T) => string | undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect a reducing function to take two arguments, one for the current item and another for the accumulated result. Either this isn't a reducer, therefore needs a new name, or there's a bug :)
AbstractSQLCompiler.ts
Outdated
| const compileSchema = (abstractSqlModel: AbstractSqlModel, engine: Engines, ifNotExists: boolean): SqlModel => { | ||
| let ifNotExistsStr: string | ||
| const mkSchemaDependencyMap = (tables: ResourceMap<AbstractSqlTable>, engine: Engines, ifNotExists: boolean) => { | ||
| let ifNotExistsStr: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a space and then a tab here.
AbstractSQLCompiler.ts
Outdated
| } | ||
| }) | ||
|
|
||
| return { hasDependants: hasDependants, schemaDependencyMap: schemaDependencyMap } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The keys can be omitted.
AbstractSQLCompiler.ts
Outdated
| return acc | ||
| } | ||
| } | ||
| , { ins: dst, del: src, mod: mod } ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a reason for using shortened names for identifiers -- what's wrong with inserted, deleted, modified, and generateSplit?
AbstractSQLCompiler.ts
Outdated
| // insertion, deletion or modification. | ||
| // All these functions are treated as reducers, their first argument | ||
| // will be an accumulator of all the changes up to that point and | ||
| // they are expected to add their changes to it and return it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not a reducer then. This is a sort of a mapping function that's also allowed to have side-effects? Seeing how you define these functions in diffFields and diffSchemas later on, how you use map in this function's body and this comment, this is all needlessly confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed them in the last commit, given the comments Page did, they used to be reducers but aren't anymore and some comments/names where lagging behind. I removed every reference to reducers to make it clear. They are just mapping functions which are not expected to have side-effects, but just map to the corresponding sql statement.
AbstractSQLCompiler.ts
Outdated
| return | ||
| } | ||
| if (_.isEqual(_.omit(src, ['fieldName', 'references']), _.omit(dst, ['fieldName', 'references']))) { | ||
| return 'RENAME COLUMN "' + src.fieldName + '" TO "' + dst.fieldName + '";\n\t' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these SQL statements be backend-specific (i.e. different for WebSQL, PostgreSQL, etc)? If Pine supported SQLite for example this wouldn't work, and makes me suspicious WebSQL might also have different syntax or no support even.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I assumed postgre for now, I think that most of these are pretty standard across SQL dialects but Ill check each statement.
AbstractSQLCompiler.ts
Outdated
| let verb = dstTable.name.split('-').slice(1, -1).join(' ') | ||
| return !_.isUndefined(relations[verb]) | ||
| }) | ||
| } else return relations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need for else, you can simply return relations from outside the if. Same below.
test/sbvr/migrations.coffee
Outdated
| RENAME TO "plane-can be flown by-pilot"; | ||
| ALTER TABLE "plane-can be flown by-pilot" | ||
| RENAME COLUMN "pilot" TO "can be flown by-pilot"; | ||
| RENAME COLUMN "can fly-plane" TO "plane";\n\t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still has the issue from #13 (comment)
Oh, also the change was referring to just the line I commented on, for reference I would expect the full block to be:
[ ''' ALTER TABLE "pilot-can fly-plane" RENAME TO "plane-can be flown by-pilot"; ALTER TABLE "plane-can be flown by-pilot" RENAME COLUMN "pilot" TO "can be flown by-pilot"; ALTER TABLE "plane-can be flown by-pilot" RENAME COLUMN "can fly-plane" TO "plane"; ''' ]or
[ ''' ALTER TABLE "pilot-can fly-plane" RENAME TO "plane-can be flown by-pilot"; ''', ''' ALTER TABLE "plane-can be flown by-pilot" RENAME COLUMN "pilot" TO "can be flown by-pilot"; ''', ''' ALTER TABLE "plane-can be flown by-pilot" RENAME COLUMN "can fly-plane" TO "plane"; ''' ]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh I kept misreading, I thought that renames on columns where in the list of actions. I will change that everywhere so that each rename has its own alter table
|
@nazrhom, status checks have failed for this PR. Please make appropriate changes and recommit. |
test/sbvr/test.coffee
Outdated
| expectation(result) | ||
|
|
||
| runMigration = (describe, src, dst, expectation) -> | ||
| it 'Migration: \n\nVocabulary: src\n' + src + '\n\nVocabulary: dst\n' + dst, -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you use
describe 'Migration', ->
it 'Vocabulary: src\n' + src + '\n\nVocabulary: dst\n' + dst, ->
it might come out in the results a little nicer
| NestedIndent: function(indent) { | ||
| var $elf = this, _fromIdx = this.input.idx; | ||
| return indent + " "; | ||
| return indent + "\t"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is changed here but I don't see any changes to AbstractSQLRules2SQL.ometa?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this, my AbstractSQLRules2SQL.ometa is the same as the one appearing on master, I think this is caused by some other commit that was merged without including the changes to AbstractSQLRules.js?
AbstractSQLCompiler.ts
Outdated
| const extractMappings = (resource:string):[string, string] => { | ||
| const resourceParts = resource.split('-') | ||
| const subject = resourceParts[0] | ||
| const rest = resourceParts.slice(1).join('-') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can make this nicer with
const [ subject, ...rest ] = resource.split('-')
return [ subject, rest.join('-') ]
AbstractSQLCompiler.ts
Outdated
| const dstSDM = mkSchemaDependencyMap(dst.tables, engine, ifNotExists).schemaDependencyMap | ||
|
|
||
| const matchFn:MatchFn<AbstractSqlTable> = (tables, srcTable) => { | ||
| let match = _.find(tables, { name: srcTable.name }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be const
AbstractSQLCompiler.ts
Outdated
|
|
||
| const matchFn:MatchFn<AbstractSqlTable> = (tables, srcTable) => { | ||
| let match = _.find(tables, { name: srcTable.name }) | ||
| if (_.isUndefined(match)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be nicer as
if (match != null) {
return match
}
AbstractSQLCompiler.ts
Outdated
|
|
||
| const matchFn: MatchFn<AbstractSqlField> = (fieldArray, field) => { | ||
| let match = _.find(fieldArray, { fieldName: field.fieldName }) | ||
| if (_.isUndefined(match)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you shortcircuit with
if (match != null) {
return match
}
AbstractSQLCompiler.ts
Outdated
| } | ||
|
|
||
| const matchFn: MatchFn<AbstractSqlField> = (fieldArray, field) => { | ||
| let match = _.find(fieldArray, { fieldName: field.fieldName }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be const
AbstractSQLCompiler.ts
Outdated
| .concat(_.map(split.deleted, delFn)) | ||
| .concat(_.map(split.inserted, insFn)) | ||
|
|
||
| return _.reject(diff, _.isUndefined) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer _.isNil to catch both null and undefined
AbstractSQLCompiler.ts
Outdated
| } | ||
|
|
||
| const generateSplit = <T>(src:Array<T>, dst: Array<T>, matchFn: MatchFn<T>): Split<T> => { | ||
| let modified: Array<Pair<T>> = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be const
AbstractSQLCompiler.ts
Outdated
| let modified: Array<Pair<T>> = [] | ||
| return _.reduce(src, (acc, value) => { | ||
| const match = matchFn(acc.inserted, value) | ||
| if (_.isUndefined(match)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be match == null
…ation from a pair of AbstractSQLModels. Change-type: minor
Added support for automatic migrations between two schemas.
Supported auto-migrations are: adding or removing terms, both fields and tables, renaming will only be performed when removing a synonym or synonymous form.