UNPKG

@calljmp/cli

Version:
3 lines (2 loc) 5.89 kB
"use strict";var D=Object.create;var w=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var C=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var L=(g,t)=>{for(var e in t)w(g,e,{get:t[e],enumerable:!0})},S=(g,t,e,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of E(t))!A.call(g,s)&&s!==e&&w(g,s,{get:()=>t[s],enumerable:!(a=T(t,s))||a.enumerable});return g};var M=(g,t,e)=>(e=g!=null?D(C(g)):{},S(t||!g||!g.__esModule?w(e,"default",{value:g,enumerable:!0}):e,g)),R=g=>S(w({},"__esModule",{value:!0}),g);var j={};L(j,{SqliteMigration:()=>N});module.exports=R(j);var v=M(require("sqlite3")),$=require("sqlite"),u=require("./utils");class N{_steps=[];_target=null;get totalSteps(){return this._steps.length}get steps(){return[...this._steps]}_generate(t=!1){const e=[],a=this._steps.some(s=>s.deferForeignKeys);a&&(e.push("PRAGMA defer_foreign_keys = ON;"),t&&e.push(""));for(const s of this._steps){t&&e.push(`-- ${s.type.toUpperCase()}: ${s.name}`);for(const o of s.statements){const r=(0,u.normalizeSql)(o);e.push(r.endsWith(";")?r:`${r};`)}t&&e.push("")}return a&&e.push("PRAGMA defer_foreign_keys = OFF;"),e}statements(){return this._generate(!1)}sql(){return this._generate(!0).join(` `)}async exec(t){await(await this._acquireTarget()).exec(t)}async prepare(t){const e=await(0,$.open)({filename:":memory:",driver:v.default.Database});await e.exec(t);const a=await this._acquireTarget(),s=new Set;await this._migrateTables(e,a,s),await this._migrateObjects("index",e,a,s),await this._migrateObjects("trigger",e,a,s),await this._migrateObjects("view",e,a,s)}async _acquireTarget(){return this._target||(this._target=await(0,$.open)({filename:":memory:",driver:v.default.Database})),this._target}async _isAddOnlyColumns(t,e,a){const s=await this._columns(t,a),o=await this._columns(e,a),r=o.filter(f=>!s.some(h=>h.name===f.name)),c=s.filter(f=>!o.some(h=>h.name===f.name));return{addOnly:r.length===0&&c.every(f=>f.dflt_value!==null||f.notnull===0),added:c}}async _migrateTables(t,e,a){const s=await this._buildForeignKeyGraph(t),o=this._reverseGraph(s),r=await this._objects(e,"table"),c=await this._objects(t,"table"),p=[...r.keys()].filter(i=>!c.has(i)),f=[...c.keys()].filter(i=>!r.has(i)),h=[...c.keys()].filter(i=>r.has(i)&&(0,u.normalizeSql)(r.get(i))!==(0,u.normalizeSql)(c.get(i))),_=this._topologicalSort([...new Set([...h,...p,...f])],s);for(const i of _)p.includes(i)&&this._steps.push({type:"table",name:i,statements:[`DROP TABLE ${i}`]});for(const i of _)if(f.includes(i)){const l=c.get(i);if(!l)throw new Error(`Table ${i} not found in schema definitions.`);this._steps.push({type:"table",name:i,statements:[l]})}for(const i of _)if(h.includes(i)){const{addOnly:l,added:d}=await this._isAddOnlyColumns(t,e,i);if(l){const y=d.map(m=>`ALTER TABLE ${i} ADD COLUMN ${m.name} ${m.type||""}${m.notnull?" NOT NULL":""}${m.dflt_value!==null?` DEFAULT ${m.dflt_value}`:""}`.trim());this._steps.push({type:"table",name:i,statements:y})}else this._findDependentTables(i,o,a)}const n=this._topologicalSort([...a],s);if(n.length>0){for(const l of n)this._steps.push({type:"table",name:`migrating ${l} to new schema`,deferForeignKeys:!0,statements:[`ALTER TABLE ${l} RENAME TO ${l}_old`]});for(const l of n){const d=c.get(l);if(!d)throw new Error(`Table ${l} not found in schema definitions.`);this._steps.push({type:"table",name:`preparing new ${l}`,statements:[d]})}for(const l of n){const d=await this._columns(e,l),y=await this._columns(t,l),m=d.filter(b=>y.some(O=>O.name===b.name));m.length>0&&this._steps.push({type:"table",name:`copying data back to ${l}`,statements:[`INSERT INTO ${l} (${m.map(b=>b.name).join(", ")}) SELECT ${m.map(b=>b.name).join(", ")} FROM ${l}_old`]})}const i=[...n].reverse();for(const l of i)this._steps.push({type:"table",name:`dropping old ${l}`,statements:[`DROP TABLE ${l}_old`]})}}async _migrateObjects(t,e,a,s){const o=n=>{const i={index:/INDEX\s+\w+\s+ON\s+["`]?(\w+)["`]?/i,trigger:/ON\s+["`]?(\w+)["`]?/i,view:/CREATE\s+VIEW\s+\w+\s+AS\s+SELECT.*?\sFROM\s+["`]?(\w+)["`]?/is};return n.trim().toUpperCase().match(i[t])?.[1]?.toLowerCase()??""},r=await this._objects(a,t),c=await this._objects(e,t),p=n=>{const i=c.get(n);if(!i)throw new Error(`Object ${n} not found in schema definitions.`);return i},f=[...r.keys()].filter(n=>!c.has(n)),h=[...c.keys()].filter(n=>!r.has(n)),_=[...c.keys()].filter(n=>r.has(n)&&((0,u.normalizeSql)(r.get(n))!==(0,u.normalizeSql)(p(n))||s.has(o(p(n)))));for(const n of f)this._steps.push({type:t,name:n,statements:[`DROP ${t.toUpperCase()} ${n}`]});for(const n of h)this._steps.push({type:t,name:n,statements:[p(n)]});for(const n of _)(t==="view"||!s.has(o(p(n))))&&this._steps.push({type:t,name:n,statements:[`DROP ${t.toUpperCase()} ${n}`]}),this._steps.push({type:t,name:n,statements:[p(n)]})}async _buildForeignKeyGraph(t){const e=new Map,a=await this._objects(t,"table");for(const s of a.keys()){const o=await this._foreignKeys(t,s);for(const r of o)e.has(r.table.toLowerCase())||e.set(r.table.toLowerCase(),new Set),e.get(r.table.toLowerCase()).add(s.toLowerCase())}return e}_reverseGraph(t){const e=new Map;for(const[a,s]of t)for(const o of s)e.has(o)||e.set(o,new Set),e.get(o).add(a);return e}_findDependentTables(t,e,a){const s=o=>{if(!a.has(o)){a.add(o);for(const[r,c]of e)c.has(o)&&s(r)}};s(t.toLowerCase())}_topologicalSort(t,e){const a=new Set,s=[],o=r=>{if(!a.has(r)){a.add(r);for(const c of e.get(r)||[])o(c);s.push(r)}};for(const r of t)o(r);return s.reverse()}async _objects(t,e){const a=await t.all('SELECT name, sql FROM sqlite_master WHERE type = ? AND sql IS NOT NULL AND name NOT LIKE "sqlite_%"',e);return new Map(a.map(s=>[s.name.toLowerCase(),s.sql]))}async _foreignKeys(t,e){return t.all(`PRAGMA foreign_key_list(${e})`)}async _columns(t,e){return t.all(`PRAGMA table_info(${e})`)}}0&&(module.exports={SqliteMigration});