Files
dsh_ai1net_server/交付物/插件建库权限-一次性执行.sh
T

103 lines
5.4 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# ==============================================================================
# 🔴 2026-09-23 已作废,⛔ 不得执行。
# 原因:PG 显式禁止从函数内执行 CREATE DATABASE(SECURITY DEFINER 路径无效)。
# 替代件:交付物/建库权限-D档被证伪与修正判定-20260923.md
# ==============================================================================
# D 档一次性落地:建库函数 + 7 条验收 + 锁管理(幂等 · 可重复跑)
# 用法: bash "E:/ProgramData/AIProject/aliyun-dsh-server/交付物/插件建库权限-一次性执行.sh"
# 设计目标:**把占锁窗口压到 ~40 秒**(抢不到就立刻退出,不空转)
# 退出码: 0=全绿 2=DDL/验收失败 3=抢锁失败(别的会话在跑)
# ⚠️ 本脚本会 `su postgres` 建 SECURITY DEFINER 函数 —— 已由用户 2026-09-22 19:3x 拍板 D
# ==============================================================================
set -u
GUARD="D:/github/dsh_shenxian/dsh-server-docs/scripts/handoff-guard.sh"
HOST="[email protected]"
ME="插件投放与分库线-执行棒(抢间隙)"
# ── 1 抢全局执行锁(抢不到立刻走人)─────────────────────────────────────────
CLAIM_OUT="$(bash "$GUARD" --claim-exec "$ME" 2>&1)"
if printf '%s' "$CLAIM_OUT" | grep -q "✗"; then
echo "LOCK_BUSY :: $(printf '%s' "$CLAIM_OUT" | grep -m1 '占用者' | cut -c1-80)"
echo "→ 停手(R9:不得删锁 / 不得接管)。过一会儿再跑本脚本即可。"
exit 3
fi
echo "LOCK_HELD=yes"
# 无论成败都放锁(这是抢间隙策略的前提)
trap 'bash "$GUARD" --release-exec >/dev/null 2>&1; echo "LOCK_RELEASED=yes"' EXIT
# ── 2 落地 DDL(幂等)────────────────────────────────────────────────────────
echo "--- 2 落地 DDL ---"
ssh -o ConnectTimeout=8 -o BatchMode=yes "$HOST" 'bash -s' <<'REMOTE'
set -u
cat > /tmp/.pdl-d.sql <<'SQL'
CREATE SCHEMA IF NOT EXISTS dshs_int AUTHORIZATION postgres;
REVOKE ALL ON SCHEMA dshs_int FROM PUBLIC;
GRANT USAGE ON SCHEMA dshs_int TO dshs;
CREATE OR REPLACE FUNCTION dshs_int.create_plugin_database(p_name text)
RETURNS text
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = pg_catalog
AS $fn$
DECLARE
v_owner constant text := 'dshs';
BEGIN
IF p_name IS NULL OR p_name !~ '^dshs_pl_[a-z][a-z0-9_]{0,40}$' THEN
RAISE EXCEPTION 'invalid plugin database name: %', p_name USING ERRCODE = '22023';
END IF;
IF EXISTS (SELECT 1 FROM pg_database WHERE datname = p_name) THEN
RETURN 'exists';
END IF;
EXECUTE format('CREATE DATABASE %I OWNER %I ENCODING %L TEMPLATE template0',
p_name, v_owner, 'UTF8');
RAISE LOG 'dshs plugin db created: %', p_name;
RETURN 'created';
EXCEPTION
WHEN duplicate_database THEN
RETURN 'exists';
END;
$fn$;
REVOKE ALL ON FUNCTION dshs_int.create_plugin_database(text) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION dshs_int.create_plugin_database(text) TO dshs;
SQL
su postgres -c 'cd /tmp && psql -h /var/run/postgresql -p 15432 -d dshs -X -v ON_ERROR_STOP=1 -q -f /tmp/.pdl-d.sql' 2>&1 | head -10
echo "DDL_RC=$?"
rm -f /tmp/.pdl-d.sql
REMOTE
[ $? -ne 0 ] && { echo "DDL_FAILED"; exit 2; }
# ── 3 验收 7 条 ──────────────────────────────────────────────────────────────
echo "--- 3 验收 ---"
ssh -o ConnectTimeout=8 -o BatchMode=yes "$HOST" 'bash -s' <<'REMOTE'
P() { su postgres -c "cd /tmp && psql -h /var/run/postgresql -p 15432 -d dshs -X -A -t -q -c \"$1\"" 2>&1 | head -2; }
U() { python3 - <<'PY'
import glob,re,os
url=""
for f in ["/etc/dshs.env"]+glob.glob("/etc/systemd/system/dshs.service.d/*.conf"):
try:
for line in open(f,encoding="utf-8",errors="replace"):
m=re.search(r'DSHS_DB_URL\s*=\s*"?([^"\s]+)"?',line)
if m: url=m.group(1)
except Exception: pass
import urllib.parse as up
print(up.urlparse(url).password or "")
PY
}
export PGPASSWORD="$(U)"
D() { psql -h 127.0.0.1 -p 15432 -U dshs -d dshs -X -A -t -q -c "$1" 2>&1 | head -2; }
echo "① create = $(D "select dshs_int.create_plugin_database('dshs_pl_selftest')")"
echo "② 幂等 = $(D "select dshs_int.create_plugin_database('dshs_pl_selftest')")"
echo "③ 库与属主 = $(P "select datname||'/'||pg_get_userbyid(datdba) from pg_database where datname='dshs_pl_selftest'")"
echo "④ 白名单(evil) = $(D "select dshs_int.create_plugin_database('evil')" | head -1)"
echo "④' 注入串 = $(D "select dshs_int.create_plugin_database('dshs_pl_x; DROP DATABASE dshs')" | head -1)"
echo "⑤ dshs 权限(须 f|f) = $(P "select rolsuper||'|'||rolcreatedb from pg_roles where rolname='dshs'")"
echo "⑥ 无旁路 = $(D 'create database pdl_should_fail' | head -1)"
echo "⑦ 可连新库+建schema = $(PGPASSWORD="$PGPASSWORD" psql -h 127.0.0.1 -p 15432 -U dshs -d dshs_pl_selftest -X -A -t -q -c 'create schema s_test' 2>&1 | head -1)"
echo "→ 清理 = $(P 'DROP DATABASE dshs_pl_selftest' >/dev/null 2>&1 && echo dropped)"
echo "→ 残留库数(须0) = $(P "select count(*) from pg_database where datname like 'dshs_pl_%'")"
REMOTE
echo "DONE"