title: PostgreSQL(1) author: Gamehu tags: - PostgreSQL categories: - 工作 date: 2019-11-29 10:45:00 --- {% asset_img 1.jpg Photo by fabio on Unsplash %} 最近产品提了一个需求,要做菜单优化。 菜单优化: 1. 重写菜单数据初始化sql脚本,数据结构两层变为三层 2. 权限数据调整 3. 兼容原有菜单权限数据 这里面第三点是最麻烦的,兼容原有的菜单数据,我给需求说了两个方案。 PlanA:从时间方面(因为是个小迭代,整个迭代的功能只有一周时间开发)考虑管理员菜单权限保留,其它普通角色菜单权限一律置空,需求也接受(因为客户现场大多数情况都是用管理员账号)。 PlanB:保留原有数据,但是需要1-2天预研一下升级方案是否可行,我提出的方案是直接用sql脚本做,需求也认可,PlanA为兜底方案。 我就提个解决方案,结果这事最后让我支援一下给做了...,所以在这简单记录一下过程产物。 ### PLV8 简单地说就算PostgreSQL里加个扩展,这个扩展就是V8引擎,是的,你理解的没错,就是Google开源的JavaScript引擎,有了这个扩展那就能在sql里写js代码了,这对于在脚本里写逻辑那可是爽歪歪了。 安装过程我就不多说了,网上有很多,大体流程就是 1. 从GitHub wget 下来 2. make install ``` #添加扩展 CREATE EXTENSION plv8; #验证plv8的版本,出来版本号就证明装上了 SELECT plv8_version(); ``` 然后你就可以写JS代码了,ES6、coffeeScript等都可以,只要最终是v8能解析的就成。 ### 写 好现在开始写了,一开始不太了解,打算用存储过程做,但是后来老前辈提醒我,我这个是升级脚本,只需要执行一遍就成,所以没必要做存储过程,最后还得删掉,因为留着没意义。 然后建议我用DO $$的写法,此方式执行完不会留下其它痕迹就跟执行一条长sql一样,贴个代码: ``` DO $$ plv8.elog(INFO, '------------------------------------update user menus start------------------------------------'); buildTree = function(list) { try { let temp = {}; let tree = {}; for (let menu of list) { temp[menu.id] = menu; } for (let i in temp) { if (temp[i].parent) { if (!temp[temp[i].parent].children) { temp[temp[i].parent].children = {}; } temp[temp[i].parent].children[temp[i].id] = temp[i]; } else { tree[temp[i].id] = temp[i]; } } return tree; } catch (error) { plv8.elog(ERROR, 'buildTree ' + error); } }; getTreeOfMenus = function() { let cmdb = require('xxx'); let result = cmdb.service.query('default', '{xxx{id name}}', {}); return buildTree(result); }; let allMenus = getTreeOfMenus(); delete allMenus['xxx']; let ids = []; idsOfflatten = function(data) { try { for (let id in data) { ids.push(id); if (data[id].children) { idsOfflatten(data[id].children); } } return ids; } catch (error) { plv8.elog(ERROR, 'idsOfflatten ' + error); } }; let flatIds = idsOfflatten(allMenus); plv8.elog(INFO, 'new menu ids:' + flatIds); getNormalMenus = function(menus) { try { plv8.elog(INFO, 'old menus : ' + JSON.stringify(menus)); let old_keys = Object.keys(menus); old_keys.forEach(id => { if (!flatIds.includes(id)) { plv8.elog(INFO, 'delete menu id: ' + id); delete menus[id]; } }); return menus; } catch (error) { plv8.elog(ERROR, 'getNormalMenus ' + error); } }; updateMenus = function() { try { let ROLE_ADMIN = 'admin', ROLE_DOMAIN = 'domain'; let query = 'SELECT name, menus, role_type FROM xxx'; let updateAdmin = 'UPDATE xxx SET menus=NULL, last_modified=CURRENT_TIMESTAMP where name = $1'; let updateNormal = 'UPDATE xxx SET menus=$1, last_modified=CURRENT_TIMESTAMP where name = $2'; let execCount=0; plv8.execute(query).forEach(row => { let roleType = row.role_type; if (roleType === ROLE_DOMAIN || roleType === ROLE_ADMIN) { let adminCount = plv8.execute(updateAdmin, [row.name]); plv8.elog(INFO, 'update admin user menus is null ,count: '+adminCount ); adminCount>0 ? execCount+=1:null; return; } let newMenus = getNormalMenus(row.menus); let normalCount = plv8.execute(updateNormal, [newMenus, row.name]); plv8.elog(INFO, 'update normal user menus , ' + JSON.stringify(newMenus)+',count:'+normalCount); normalCount>0 ? execCount+=1:null; }); return execCount; } catch (error) { plv8.elog(ERROR, 'updateMenus ' + error); } }; let updateCount=updateMenus(); plv8.elog(INFO,'total of successes :'+updateCount); plv8.elog(INFO,'------------------------------------update user menus end------------------------------------'); $$ LANGUAGE plv8; ``` ### 小结: PostgreSQL很强大,这是我初试水,后续有机会会再写写工作中的一些PostgreSQL的实践例子。 其实PostgreSQL几乎可扩展主流的所有编程语言比如C++、Java、nodejs等。 参考文档: {% blockquote plv8 documention https://plv8.github.io/ %} {% endblockquote %}