{"action":"created","issue":{"url":"https://api.github.com/repos/Nozbe/WatermelonDB/issues/62","repository_url":"https://api.github.com/repos/Nozbe/WatermelonDB","labels_url":"https://api.github.com/repos/Nozbe/WatermelonDB/issues/62/labels{/name}","comments_url":"https://api.github.com/repos/Nozbe/WatermelonDB/issues/62/comments","events_url":"https://api.github.com/repos/Nozbe/WatermelonDB/issues/62/events","html_url":"https://github.com/Nozbe/WatermelonDB/pull/62","id":364007355,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE4MzAwMjE5","number":62,"title":"[RFC] Migrations — specification","user":{"login":"radex","id":183747,"node_id":"MDQ6VXNlcjE4Mzc0Nw==","avatar_url":"https://avatars2.githubusercontent.com/u/183747?v=4","gravatar_id":"","url":"https://api.github.com/users/radex","html_url":"https://github.com/radex","followers_url":"https://api.github.com/users/radex/followers","following_url":"https://api.github.com/users/radex/following{/other_user}","gists_url":"https://api.github.com/users/radex/gists{/gist_id}","starred_url":"https://api.github.com/users/radex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/radex/subscriptions","organizations_url":"https://api.github.com/users/radex/orgs","repos_url":"https://api.github.com/users/radex/repos","events_url":"https://api.github.com/users/radex/events{/privacy}","received_events_url":"https://api.github.com/users/radex/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":{"login":"radex","id":183747,"node_id":"MDQ6VXNlcjE4Mzc0Nw==","avatar_url":"https://avatars2.githubusercontent.com/u/183747?v=4","gravatar_id":"","url":"https://api.github.com/users/radex","html_url":"https://github.com/radex","followers_url":"https://api.github.com/users/radex/followers","following_url":"https://api.github.com/users/radex/following{/other_user}","gists_url":"https://api.github.com/users/radex/gists{/gist_id}","starred_url":"https://api.github.com/users/radex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/radex/subscriptions","organizations_url":"https://api.github.com/users/radex/orgs","repos_url":"https://api.github.com/users/radex/repos","events_url":"https://api.github.com/users/radex/events{/privacy}","received_events_url":"https://api.github.com/users/radex/received_events","type":"User","site_admin":false},"assignees":[{"login":"radex","id":183747,"node_id":"MDQ6VXNlcjE4Mzc0Nw==","avatar_url":"https://avatars2.githubusercontent.com/u/183747?v=4","gravatar_id":"","url":"https://api.github.com/users/radex","html_url":"https://github.com/radex","followers_url":"https://api.github.com/users/radex/followers","following_url":"https://api.github.com/users/radex/following{/other_user}","gists_url":"https://api.github.com/users/radex/gists{/gist_id}","starred_url":"https://api.github.com/users/radex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/radex/subscriptions","organizations_url":"https://api.github.com/users/radex/orgs","repos_url":"https://api.github.com/users/radex/repos","events_url":"https://api.github.com/users/radex/events{/privacy}","received_events_url":"https://api.github.com/users/radex/received_events","type":"User","site_admin":false},{"login":"tkapelak","id":1301070,"node_id":"MDQ6VXNlcjEzMDEwNzA=","avatar_url":"https://avatars2.githubusercontent.com/u/1301070?v=4","gravatar_id":"","url":"https://api.github.com/users/tkapelak","html_url":"https://github.com/tkapelak","followers_url":"https://api.github.com/users/tkapelak/followers","following_url":"https://api.github.com/users/tkapelak/following{/other_user}","gists_url":"https://api.github.com/users/tkapelak/gists{/gist_id}","starred_url":"https://api.github.com/users/tkapelak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tkapelak/subscriptions","organizations_url":"https://api.github.com/users/tkapelak/orgs","repos_url":"https://api.github.com/users/tkapelak/repos","events_url":"https://api.github.com/users/tkapelak/events{/privacy}","received_events_url":"https://api.github.com/users/tkapelak/received_events","type":"User","site_admin":false},{"login":"mobily","id":1467712,"node_id":"MDQ6VXNlcjE0Njc3MTI=","avatar_url":"https://avatars1.githubusercontent.com/u/1467712?v=4","gravatar_id":"","url":"https://api.github.com/users/mobily","html_url":"https://github.com/mobily","followers_url":"https://api.github.com/users/mobily/followers","following_url":"https://api.github.com/users/mobily/following{/other_user}","gists_url":"https://api.github.com/users/mobily/gists{/gist_id}","starred_url":"https://api.github.com/users/mobily/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mobily/subscriptions","organizations_url":"https://api.github.com/users/mobily/orgs","repos_url":"https://api.github.com/users/mobily/repos","events_url":"https://api.github.com/users/mobily/events{/privacy}","received_events_url":"https://api.github.com/users/mobily/received_events","type":"User","site_admin":false},{"login":"rozPierog","id":23499434,"node_id":"MDQ6VXNlcjIzNDk5NDM0","avatar_url":"https://avatars1.githubusercontent.com/u/23499434?v=4","gravatar_id":"","url":"https://api.github.com/users/rozPierog","html_url":"https://github.com/rozPierog","followers_url":"https://api.github.com/users/rozPierog/followers","following_url":"https://api.github.com/users/rozPierog/following{/other_user}","gists_url":"https://api.github.com/users/rozPierog/gists{/gist_id}","starred_url":"https://api.github.com/users/rozPierog/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rozPierog/subscriptions","organizations_url":"https://api.github.com/users/rozPierog/orgs","repos_url":"https://api.github.com/users/rozPierog/repos","events_url":"https://api.github.com/users/rozPierog/events{/privacy}","received_events_url":"https://api.github.com/users/rozPierog/received_events","type":"User","site_admin":false},{"login":"kokusGr","id":28709559,"node_id":"MDQ6VXNlcjI4NzA5NTU5","avatar_url":"https://avatars1.githubusercontent.com/u/28709559?v=4","gravatar_id":"","url":"https://api.github.com/users/kokusGr","html_url":"https://github.com/kokusGr","followers_url":"https://api.github.com/users/kokusGr/followers","following_url":"https://api.github.com/users/kokusGr/following{/other_user}","gists_url":"https://api.github.com/users/kokusGr/gists{/gist_id}","starred_url":"https://api.github.com/users/kokusGr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kokusGr/subscriptions","organizations_url":"https://api.github.com/users/kokusGr/orgs","repos_url":"https://api.github.com/users/kokusGr/repos","events_url":"https://api.github.com/users/kokusGr/events{/privacy}","received_events_url":"https://api.github.com/users/kokusGr/received_events","type":"User","site_admin":false}],"milestone":null,"comments":6,"created_at":"2018-09-26T12:38:28Z","updated_at":"2018-10-01T14:52:47Z","closed_at":null,"author_association":"MEMBER","pull_request":{"url":"https://api.github.com/repos/Nozbe/WatermelonDB/pulls/62","html_url":"https://github.com/Nozbe/WatermelonDB/pull/62","diff_url":"https://github.com/Nozbe/WatermelonDB/pull/62.diff","patch_url":"https://github.com/Nozbe/WatermelonDB/pull/62.patch"},"body":"Step 1 in implementing migrations. Here's the API I came up with:\r\n\r\n```js\r\n// model/migrations.js\r\nexport default schemaMigrations({\r\n  minimumVersion: 1,\r\n  currentVersion: 3,\r\n  migrations: [\r\n    {\r\n      from: 2,\r\n      to: 3,\r\n      steps: [\r\n        addColumn({\r\n          table: 'posts',\r\n          column: { name: 'subtitle', type: 'string', isOptional: true },\r\n        }),\r\n        createTable({\r\n          name: 'comments',\r\n          columns: [\r\n            { name: 'post_id', type: 'string', isIndexed: true },\r\n            { name: 'body', type: 'string' },\r\n          ],\r\n        }),\r\n      ],\r\n    },\r\n    {\r\n      from: 1,\r\n      to: 2,\r\n      steps: [\r\n        // ...\r\n      ],\r\n    },\r\n  ],\r\n})\r\n\r\n// index.js\r\nimport schema from 'model/schema'\r\nimport migrations from 'model/migrations'\r\n\r\nconst adapter = xxxAdapter({\r\n  schema,\r\n  migrations,\r\n})\r\n```\r\n\r\nThe version numbers (to/from, minimumVersion, currentVersion) are a little repetitive, but the idea is that this should be as explicit as possible so it can be validated if the migrations cover the entire schema version range correctly.\r\n\r\nI'll first implement two types of migration steps: createTable and addColumn. This covers 90% of the cases. You don't _need_ to destroy tables or columns (you can just ignore them), or rename them. And it's relatively rare to change indexing/optionality of columns. In the future, we'll add more migration step types if we need them (or, the community might contribute).\r\n\r\nI sketched out future API for them though:\r\n\r\n```js\r\n// table operations\r\ndestroyTable('table_name')\r\nrenameTable({ from: 'old_table_name', to: 'new_table_name' })\r\n\r\n// column operations\r\nrenameColumn({ table: 'table_name', from: 'old_column_name', to: 'new_column_name' })\r\ndestroyColumn({ table: 'table_name', column: 'column_name' })\r\n\r\n// indexing\r\naddColumnIndex({ table: 'table_name', column: 'column_name' })\r\nremoveColumnIndex({ table: 'table_name', column: 'column_name' })\r\n\r\n// optionality\r\nmakeColumnOptional({ table: 'table_name', column: 'column_name' }) // allows nulls now\r\nmakeColumnRequired({ table: 'table_name', column: 'column_name' }) // nulls are changed to null value ('', 0, false)\r\n```\r\n\r\n**alternatives considered**\r\n\r\nWe could scope migration steps per-table, for example:\r\n\r\n```js\r\n{\r\n  from: .., to: ..,\r\n  changes: [\r\n    modifyTable('table_name', [\r\n      rename({ to: 'new_table_name' }),\r\n      addColumn({ name: 'x', type: 'string' }) \r\n    ])\r\n  ]\r\n}\r\n```\r\n\r\n… but this complicates the implementation, without an obvious benefit. You don't write migrations every day, so the API doesn't have to be beautiful, it has to be as foolproof as possible.\r\n\r\nWDYT?"},"comment":{"url":"https://api.github.com/repos/Nozbe/WatermelonDB/issues/comments/425937234","html_url":"https://github.com/Nozbe/WatermelonDB/pull/62#issuecomment-425937234","issue_url":"https://api.github.com/repos/Nozbe/WatermelonDB/issues/62","id":425937234,"node_id":"MDEyOklzc3VlQ29tbWVudDQyNTkzNzIzNA==","user":{"login":"radex","id":183747,"node_id":"MDQ6VXNlcjE4Mzc0Nw==","avatar_url":"https://avatars2.githubusercontent.com/u/183747?v=4","gravatar_id":"","url":"https://api.github.com/users/radex","html_url":"https://github.com/radex","followers_url":"https://api.github.com/users/radex/followers","following_url":"https://api.github.com/users/radex/following{/other_user}","gists_url":"https://api.github.com/users/radex/gists{/gist_id}","starred_url":"https://api.github.com/users/radex/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/radex/subscriptions","organizations_url":"https://api.github.com/users/radex/orgs","repos_url":"https://api.github.com/users/radex/repos","events_url":"https://api.github.com/users/radex/events{/privacy}","received_events_url":"https://api.github.com/users/radex/received_events","type":"User","site_admin":false},"created_at":"2018-10-01T14:52:47Z","updated_at":"2018-10-01T14:52:47Z","author_association":"MEMBER","body":"> but I think it would be great to expand it to at least destroyColumns just because I think that it wouldn't be that hard to do and would allow for much more consistent migration code.\r\n\r\nit's not trivial. SQLite doesn't support deleting columns, so you have to do a transaction where you repackage data between old and new tables. Likewise, LokiJS is schema-less, so you have to rewrite all records to remove a field. One rarely removes columns, and when you do, you can just ignore their contents, so I think it's safe to ignore it _for now_"}}
{"actor":{"display_login":"radex"}}