PostgreSQL(1)

最近产品提了一个需求,要做菜单优化。

菜单优化:

  1. 重写菜单数据初始化sql脚本,数据结构两层变为三层
  2. 权限数据调整
  3. 兼容原有菜单权限数据

这里面第三点是最麻烦的,兼容原有的菜单数据,我给需求说了两个方案。

PlanA:从时间方面(因为是个小迭代,整个迭代的功能只有一周时间开发)考虑管理员菜单权限保留,其它普通角色菜单权限一律置空,需求也接受(因为客户现场大多数情况都是用管理员账号)。

PlanB:保留原有数据,但是需要1-2天预研一下升级方案是否可行,我提出的方案是直接用sql脚本做,需求也认可,PlanA为兜底方案。

我就提个解决方案,结果这事最后让我支援一下给做了…,所以在这简单记录一下过程产物。

PLV8

简单地说就算PostgreSQL里加个扩展,这个扩展就是V8引擎,是的,你理解的没错,就是Google开源的JavaScript引擎,有了这个扩展那就能在sql里写js代码了,这对于在脚本里写逻辑那可是爽歪歪了。

安装过程我就不多说了,网上有很多,大体流程就是

  1. 从GitHub wget 下来
  2. make install
1
2
3
4
5
#添加扩展
CREATE EXTENSION plv8;

#验证plv8的版本,出来版本号就证明装上了
SELECT plv8_version();

然后你就可以写JS代码了,ES6、coffeeScript等都可以,只要最终是v8能解析的就成。

好现在开始写了,一开始不太了解,打算用存储过程做,但是后来老前辈提醒我,我这个是升级脚本,只需要执行一遍就成,所以没必要做存储过程,最后还得删掉,因为留着没意义。

然后建议我用DO $$的写法,此方式执行完不会留下其它痕迹就跟执行一条长sql一样,贴个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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等。

参考文档: