准备可扩展类型系统工作,更合理的JOIN查询优化算法,并行查询
This commit is contained in:
1 parent
9758439b82
commit
274b7fcdcc
75 files changed
+3828
-1789
No files matched your search
+3
-3
@@ -3,7 +3,7 @@
|
||||
# 整条链路共用这一份文件,按分区各管各的,互不影响:
|
||||
# 数据库服务:[server](TCP 监听地址、端口、单行上限、单次结果行数上限)
|
||||
# —— 只有它装入 chusql_core_storage 动态库并独占数据目录
|
||||
# Web / 命令行:[web](账号口令、会话、分页上限、静态目录),都经 [server] 走
|
||||
# Web / 命令行:[web](账号名、会话、分页上限、静态目录),都经 [server] 走
|
||||
# 数据目录在 [storage] data_dir;这一行是安装时会替换的占位:
|
||||
# 不写就按平台惯例(Windows %LOCALAPPDATA%\ChuSQL\data,
|
||||
# Unix ${XDG_DATA_HOME:-$HOME/.local/share}/chusql/data)。
|
||||
@@ -41,9 +41,9 @@ level = "info"
|
||||
host = "127.0.0.1"
|
||||
port = 7778
|
||||
static_dir = "static"
|
||||
# 管理员账号与口令。口令留空表示"只允许管理员免密登录",普通账号一律不能登录。
|
||||
# 管理员账号名。口令存在系统表 __system_users 里,不写进这份文件;
|
||||
# 首启为空口令,登录后用 ALTER USER 或在 Web 账号页设置。
|
||||
user = "root"
|
||||
password = ""
|
||||
# 会话与登录限流
|
||||
session_idle = 28800
|
||||
session_max = 86400
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
-- ChuSQL 系统库 schema。
|
||||
--
|
||||
-- 系统库(system)不存在就自动建,里面的表都是系统表;这份脚本是它们唯一的定义,
|
||||
-- 存储进程启动时按它声明表结构(见 chusql-core/storage/src/initsql.rs)。
|
||||
-- 这里只描述"应该长什么样",不包含任何历史数据搬迁。
|
||||
|
||||
CREATE TABLE __chusql_users (
|
||||
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
user varchar(64) NOT NULL UNIQUE,
|
||||
password_hash varchar(256) NOT NULL,
|
||||
registered_at timestamp NOT NULL,
|
||||
last_login_at timestamp,
|
||||
revision int NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX __chusql_users (user);
|
||||
+78
-22
@@ -4,18 +4,19 @@ param(
|
||||
[string]$InstallDir = '',
|
||||
[string]$DataDir = '',
|
||||
[string]$RootUser = 'root',
|
||||
[string]$RootPassword = '',
|
||||
[string]$Password = '',
|
||||
[string]$Version = 'latest',
|
||||
[string]$Repo = 'TheBadRoger/ChuSQL',
|
||||
[switch]$ListVersions,
|
||||
[switch]$Interactive,
|
||||
[switch]$KeepPackage
|
||||
[switch]$KeepPackage,
|
||||
[switch]$NoStart
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Windows 安装脚本:从包内或 GitHub 发行版装 cli/web,
|
||||
# 写全局配置、改用户 PATH,装完删除安装包。
|
||||
# Windows 安装脚本:装 cli/web、写全局配置、改用户 PATH、
|
||||
# 跑 csql-bootstrap 建系统目录、最后拉起服务。
|
||||
|
||||
$script:tempDir = ''
|
||||
|
||||
@@ -131,13 +132,18 @@ function Ask-YesNo([string]$prompt, [bool]$defaultYes) {
|
||||
}
|
||||
}
|
||||
|
||||
# 读取隐藏输入的密码。
|
||||
# 读口令:终端里不回显;-Interactive 时从 stdin 逐行读。
|
||||
function Ask-Secret([string]$prompt) {
|
||||
if ($askFromStdin) { return (Ask-Line $prompt) }
|
||||
if ($askFromStdin) {
|
||||
Write-Host $prompt -NoNewline
|
||||
$line = [Console]::In.ReadLine()
|
||||
if ($null -eq $line) { return '' }
|
||||
return $line
|
||||
}
|
||||
$secure = Read-Host $prompt -AsSecureString
|
||||
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
|
||||
try { return [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) }
|
||||
finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }
|
||||
$ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
|
||||
try { return [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr) }
|
||||
finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr) }
|
||||
}
|
||||
|
||||
$wantWeb = $false
|
||||
@@ -159,18 +165,30 @@ if (-not $Component) {
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $PSBoundParameters.ContainsKey('RootPassword') -and $canPrompt) {
|
||||
# 管理员口令是装机必答项:交互时当场设,非交互时给 -Password 或
|
||||
# CHUSQL_ADMIN_PASSWORD。口令只经 stdin 传给 csql-bootstrap,不落 chusql.toml。
|
||||
$adminPassword = "$env:CHUSQL_ADMIN_PASSWORD"
|
||||
if ($Password) { $adminPassword = $Password }
|
||||
if ($canPrompt) {
|
||||
Write-Host ''
|
||||
Write-Host ('The administrator account is "{0}".' -f $RootUser)
|
||||
$firstPassword = Ask-Secret ('Root password for {0} (empty means no password) []:' -f $RootUser)
|
||||
if ($firstPassword) {
|
||||
$againPassword = Ask-Secret 'Repeat the root password:'
|
||||
if ($firstPassword -ne $againPassword) { Fail 'the two passwords do not match' }
|
||||
Write-Host 'root password set'
|
||||
} else {
|
||||
Write-Host 'no root password: administrator-only sign-in'
|
||||
while ($true) {
|
||||
$first = Ask-Secret ' Password: '
|
||||
if ($first.Length -eq 0) {
|
||||
Write-Host ' the password cannot be empty'
|
||||
continue
|
||||
}
|
||||
$second = Ask-Secret ' Repeat it: '
|
||||
if ($first -ne $second) {
|
||||
Write-Host ' the two entries differ, try again'
|
||||
continue
|
||||
}
|
||||
$adminPassword = $first
|
||||
break
|
||||
}
|
||||
$RootPassword = $firstPassword
|
||||
}
|
||||
if ($adminPassword.Length -eq 0) {
|
||||
Fail 'no administrator password: pass -Password or set CHUSQL_ADMIN_PASSWORD'
|
||||
}
|
||||
|
||||
$wanted = @()
|
||||
@@ -263,6 +281,7 @@ if (-not (Test-Path (Join-Path $pack 'bin\chusql_core_storage.dll'))) {
|
||||
$storageLib = Join-Path $pack 'bin\chusql_core_storage.dll'
|
||||
if (-not (Test-Path $storageLib)) { Fail "package is incomplete: bin\chusql_core_storage.dll not found in $pack" }
|
||||
if (-not (Test-Path (Join-Path $pack 'bin\chusql-server.exe'))) { Fail 'package is incomplete: bin\chusql-server.exe not found' }
|
||||
if (-not (Test-Path (Join-Path $pack 'bin\csql-bootstrap.exe'))) { Fail 'package is incomplete: bin\csql-bootstrap.exe not found' }
|
||||
if ($wantWeb) {
|
||||
if (-not (Test-Path (Join-Path $pack 'bin\chusql-web.exe'))) { Fail 'package is incomplete: bin\chusql-web.exe not found' }
|
||||
if (-not (Test-Path (Join-Path $pack 'static'))) { Fail 'package is incomplete: static\ not found' }
|
||||
@@ -279,7 +298,7 @@ Say (" components {0}" -f $componentLabel)
|
||||
if ($downloaded) { Say (" release {0}" -f $Version) }
|
||||
Say (" install to {0}" -f $InstallDir)
|
||||
Say (" data dir {0}" -f $DataDir)
|
||||
Say (" root user {0}{1}" -f $RootUser, $(if ($RootPassword) { '' } else { ' (no password: administrator-only sign-in)' }))
|
||||
Say (" root user {0} (password asked above, kept out of chusql.toml)" -f $RootUser)
|
||||
|
||||
# ---- 释放文件 ----
|
||||
# 包是合在一起的(web 和 cli 都在),这里按这次的选择逐个释放:没选的组件不落地
|
||||
@@ -287,6 +306,7 @@ Step 'Installing files'
|
||||
New-Item -ItemType Directory -Force -Path (Join-Path $InstallDir 'bin'), (Join-Path $InstallDir 'logs'), $DataDir | Out-Null
|
||||
Copy-Item $storageLib (Join-Path $InstallDir 'bin') -Force
|
||||
Copy-Item (Join-Path $pack 'bin\chusql-server.exe') (Join-Path $InstallDir 'bin') -Force
|
||||
Copy-Item (Join-Path $pack 'bin\csql-bootstrap.exe') (Join-Path $InstallDir 'bin') -Force
|
||||
Get-ChildItem (Join-Path $pack 'bin') -Filter '*.dll' -File -ErrorAction SilentlyContinue |
|
||||
ForEach-Object { Copy-Item $_.FullName (Join-Path $InstallDir 'bin') -Force }
|
||||
if ($wantCli) {
|
||||
@@ -310,12 +330,22 @@ $configDir = if ($env:APPDATA) { Join-Path $env:APPDATA 'ChuSQL' } else { Join-P
|
||||
$configFile = Join-Path $configDir 'chusql.toml'
|
||||
$toml = Get-Content -Path $template -Raw -Encoding UTF8
|
||||
$toml = $toml -replace '(?m)^(\s*user\s*=\s*).*$', "`$1`"$RootUser`""
|
||||
$toml = $toml -replace '(?m)^(\s*password\s*=\s*).*$', "`$1`"$RootPassword`""
|
||||
$toml = $toml -replace '(?m)^(\s*data_dir\s*=\s*).*$', "`$1`"$($DataDir -replace '\\', '/')`""
|
||||
New-Item -ItemType Directory -Force -Path $configDir | Out-Null
|
||||
Set-Content -Path $configFile -Value $toml -Encoding UTF8
|
||||
Say (" config file {0}" -f $configFile)
|
||||
Copy-Item (Join-Path $pack 'scripts\init.sql') $InstallDir -Force -ErrorAction SilentlyContinue
|
||||
|
||||
# ---- 引导系统目录 ----
|
||||
# csql-bootstrap 建 system 数据库和 __system_users 表,口令只走 stdin,不进 chusql.toml
|
||||
Step 'Creating the system catalog'
|
||||
$bootstrap = Join-Path $InstallDir 'bin\csql-bootstrap.exe'
|
||||
$bootstrapArgs = @('--config', $configFile, '--user', $RootUser)
|
||||
$bootstrapArgs += '--password-stdin'
|
||||
$bootstrapOutput = $adminPassword | & $bootstrap @bootstrapArgs 2>&1
|
||||
$bootstrapOutput | ForEach-Object { Say (" {0}" -f $_) }
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Fail ("the bootstrap program failed (exit {0}); the system catalog is not ready" -f $LASTEXITCODE)
|
||||
}
|
||||
|
||||
# ---- 命令入口 ----
|
||||
Step 'Creating commands'
|
||||
@@ -365,6 +395,27 @@ if (-not $KeepPackage) {
|
||||
Say ("kept the downloaded package: {0}" -f $tempDir)
|
||||
}
|
||||
|
||||
Step 'Starting the server'
|
||||
$serverPath = Join-Path $InstallDir 'bin\chusql-server.exe'
|
||||
$serverPid = 0
|
||||
if ($NoStart) {
|
||||
Say ' -NoStart: the server was not started'
|
||||
} else {
|
||||
$serverLog = Join-Path $InstallDir 'logs\server.err.log'
|
||||
$serverProc = Start-Process -FilePath $serverPath `
|
||||
-ArgumentList @('--config', $configFile) `
|
||||
-WorkingDirectory $InstallDir -WindowStyle Hidden -PassThru `
|
||||
-RedirectStandardOutput (Join-Path $InstallDir 'logs\server.log') `
|
||||
-RedirectStandardError $serverLog
|
||||
Start-Sleep -Milliseconds 1200
|
||||
if ($serverProc.HasExited) {
|
||||
Fail ("the server stopped right away (exit {0}); look at {1}" -f $serverProc.ExitCode, $serverLog)
|
||||
}
|
||||
$serverPid = $serverProc.Id
|
||||
Say (" server running, pid {0}" -f $serverPid)
|
||||
Say (" log {0}" -f $serverLog)
|
||||
}
|
||||
|
||||
Step 'Done'
|
||||
Say (" chusql.toml {0}" -f $configFile)
|
||||
Say (" data {0}" -f $DataDir)
|
||||
@@ -372,4 +423,9 @@ $startWith = @()
|
||||
if ($wantWeb) { $startWith += 'csql-web' }
|
||||
if ($wantCli) { $startWith += 'csql' }
|
||||
Say (" start with {0}" -f ($startWith -join ', '))
|
||||
Say (" tcp server {0} (listens on [server] host/port, defaults 127.0.0.1:7777)" -f (Join-Path $InstallDir 'bin\chusql-server.exe'))
|
||||
Say (" tcp server {0} (listens on [server] host/port, defaults 127.0.0.1:7777)" -f $serverPath)
|
||||
if ($serverPid -gt 0) {
|
||||
Say (" running pid {0} (stop it with: Stop-Process -Id {0})" -f $serverPid)
|
||||
} else {
|
||||
Say (" start it {0} --config `"{1}`"" -f $serverPath, $configFile)
|
||||
}
|
||||
+106
-49
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
# ChuSQL 安装脚本(Linux/macOS):从包内、发行版或源码装 cli/web,
|
||||
# 写全局配置、改 PATH,装完删除安装包。
|
||||
# ChuSQL 安装脚本(Linux/macOS):装 cli/web、写全局配置、
|
||||
# 改 PATH、跑 csql-bootstrap 建系统目录、最后拉起服务。
|
||||
|
||||
default_repo='TheBadRoger/ChuSQL'
|
||||
|
||||
@@ -28,16 +28,20 @@ usage: ./install.sh [--component web|cli|both] [options]
|
||||
asks (English, y/n), command line client first:
|
||||
command line client (csql)? default yes [Y/n]
|
||||
web front end (browser UI)? default no [y/N]
|
||||
it then asks whether to give the administrator account a
|
||||
password (default no); that password goes straight into the
|
||||
system catalog and is never written to chusql.toml
|
||||
|
||||
--interactive ask even when there is no terminal; the answers are
|
||||
read from stdin (one per line)
|
||||
--install-dir DIR where to install (default: $HOME/.local/share/chusql)
|
||||
--data-dir DIR where to keep data (default: <install-dir>/data)
|
||||
--user NAME administrator name (default: root)
|
||||
--password PW administrator password, empty means no password
|
||||
(administrator-only sign-in). Leave it out and the
|
||||
installer asks for it, twice, with the echo off.
|
||||
--keep-package do not delete the downloaded/extracted package
|
||||
--password VALUE password of the administrator account (required when
|
||||
there is no terminal to ask on; shows up in the shell
|
||||
history, so CHUSQL_ADMIN_PASSWORD is safer)
|
||||
--no-start do not start the server after installing
|
||||
|
||||
where the files come from:
|
||||
(default) a package next to this script, else the newest GitHub release
|
||||
@@ -51,6 +55,8 @@ where the files come from:
|
||||
-h, --help this text
|
||||
|
||||
environment:
|
||||
CHUSQL_ADMIN_PASSWORD password of the administrator account, same as
|
||||
--password
|
||||
CHUSQL_GITHUB_BASE GitHub base URL: GitHub Enterprise, a mirror, or a
|
||||
local test fixture (default: https://github.com)
|
||||
CHUSQL_GITHUB_TOKEN token for the version API (or GITHUB_TOKEN); without
|
||||
@@ -62,8 +68,9 @@ component=''
|
||||
install_dir=''
|
||||
data_dir=''
|
||||
root_user='root'
|
||||
root_password=''
|
||||
keep_package='no'
|
||||
password_arg=''
|
||||
no_start='no'
|
||||
repo="$default_repo"
|
||||
version='latest'
|
||||
url_arg=''
|
||||
@@ -71,7 +78,6 @@ from_source='no'
|
||||
source_dir=''
|
||||
list_versions='no'
|
||||
interactive_flag='no'
|
||||
password_given='no'
|
||||
want_web='no'
|
||||
want_cli='no'
|
||||
|
||||
@@ -81,8 +87,9 @@ while [ $# -gt 0 ]; do
|
||||
--install-dir) install_dir="${2:-}"; shift 2 ;;
|
||||
--data-dir) data_dir="${2:-}"; shift 2 ;;
|
||||
--user) root_user="${2:-}"; shift 2 ;;
|
||||
--password) root_password="${2:-}"; password_given='yes'; shift 2 ;;
|
||||
--keep-package) keep_package='yes'; shift ;;
|
||||
--password) password_arg="${2:-}"; shift 2 ;;
|
||||
--no-start) no_start='yes'; shift ;;
|
||||
--url) url_arg="${2:-}"; shift 2 ;;
|
||||
--repo) repo="${2:-}"; shift 2 ;;
|
||||
--version) version="${2:-}"; shift 2 ;;
|
||||
@@ -148,27 +155,6 @@ read_line() { # -> $line(读到空行也算成功)
|
||||
fi
|
||||
}
|
||||
|
||||
read_secret() { # read_secret PROMPT -> 值写到 stdout(提示和换行都走 stderr,回显关掉)
|
||||
printf '%s' "$1" >&2
|
||||
if [ "$answers" = 'stdin' ]; then
|
||||
IFS= read -r secret_line || secret_line=''
|
||||
else
|
||||
saved=''
|
||||
if command -v stty >/dev/null 2>&1; then
|
||||
saved=$(stty -g < "$answers" 2>/dev/null) || saved=''
|
||||
stty -echo < "$answers" 2>/dev/null || true
|
||||
fi
|
||||
IFS= read -r secret_line < "$answers" || secret_line=''
|
||||
if [ -n "$saved" ]; then
|
||||
stty "$saved" < "$answers" 2>/dev/null || true
|
||||
elif command -v stty >/dev/null 2>&1; then
|
||||
stty echo < "$answers" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
printf '\n' >&2
|
||||
printf '%s' "$secret_line"
|
||||
}
|
||||
|
||||
ask_yes_no() { # ask_yes_no PROMPT DEFAULT(y|n) -> $reply = yes|no
|
||||
while :; do
|
||||
printf '%s ' "$1" >&2
|
||||
@@ -183,6 +169,24 @@ ask_yes_no() { # ask_yes_no PROMPT DEFAULT(y|n) -> $reply = yes|no
|
||||
done
|
||||
}
|
||||
|
||||
# 读一行口令 -> $line;从终端读时关掉回显(-interactive 从 stdin 读,没法关)。
|
||||
read_secret() {
|
||||
if [ "$answers" = 'stdin' ]; then
|
||||
read_line
|
||||
return 0
|
||||
fi
|
||||
_saved=$(stty -g < "$answers" 2>/dev/null) || _saved=''
|
||||
stty -echo < "$answers" 2>/dev/null || true
|
||||
read_line
|
||||
if [ -n "$_saved" ]; then
|
||||
stty "$_saved" < "$answers" 2>/dev/null || true
|
||||
else
|
||||
stty echo < "$answers" 2>/dev/null || true
|
||||
fi
|
||||
printf '\n' >&2
|
||||
return 0
|
||||
}
|
||||
|
||||
if [ "$component" = '' ] && [ "$list_versions" = 'no' ]; then
|
||||
if [ "$can_prompt" = 'no' ]; then
|
||||
fail '--component is required (web, cli or both) when there is no terminal to ask on'
|
||||
@@ -198,18 +202,36 @@ if [ "$component" = '' ] && [ "$list_versions" = 'no' ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$list_versions" = 'no' ] && [ "$password_given" = 'no' ] && [ "$can_prompt" = 'yes' ]; then
|
||||
echo
|
||||
echo "The administrator account is $root_user."
|
||||
first=$(read_secret "Root password for $root_user (empty means no password) []: ")
|
||||
if [ -n "$first" ]; then
|
||||
again=$(read_secret 'Repeat the root password: ')
|
||||
[ "$first" = "$again" ] || fail 'the two passwords do not match'
|
||||
echo 'root password set' >&2
|
||||
else
|
||||
echo 'no root password: administrator-only sign-in' >&2
|
||||
# 管理员口令是装机必答项:交互时当场设,非交互时给 --password 或
|
||||
# CHUSQL_ADMIN_PASSWORD。口令只经 stdin 传给 csql-bootstrap,不落 chusql.toml。
|
||||
admin_password="${CHUSQL_ADMIN_PASSWORD:-}"
|
||||
if [ "$list_versions" = 'no' ]; then
|
||||
if [ -n "$password_arg" ]; then
|
||||
admin_password="$password_arg"
|
||||
fi
|
||||
if [ "$can_prompt" = 'yes' ]; then
|
||||
echo
|
||||
echo "The administrator account is $root_user."
|
||||
while :; do
|
||||
printf ' Password: ' >&2
|
||||
read_secret
|
||||
admin_password="$line"
|
||||
if [ -z "$admin_password" ]; then
|
||||
echo ' the password cannot be empty' >&2
|
||||
continue
|
||||
fi
|
||||
printf ' Repeat it: ' >&2
|
||||
read_secret
|
||||
if [ "$line" != "$admin_password" ]; then
|
||||
echo ' the two entries differ, try again' >&2
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
fi
|
||||
if [ -z "$admin_password" ]; then
|
||||
fail 'no administrator password: pass --password PASSWORD or set CHUSQL_ADMIN_PASSWORD'
|
||||
fi
|
||||
root_password="$first"
|
||||
fi
|
||||
|
||||
# 选中的组件:空格分隔的清单,外加一个显示名(cli 或 web,两个就是 cli+web)
|
||||
@@ -484,7 +506,7 @@ if [ "$from_source" = 'yes' ]; then
|
||||
pack="$tmp/stage"
|
||||
mkdir -p "$pack/bin" "$pack/scripts"
|
||||
cp "$storage_lib" "$pack/bin/"
|
||||
cp "$src/scripts/chusql.toml" "$src/scripts/init.sql" "$pack/scripts/"
|
||||
cp "$src/scripts/chusql.toml" "$pack/scripts/"
|
||||
cp "$src/scripts/install.sh" "$pack/"
|
||||
for comp in $wanted; do
|
||||
if [ "$comp" = 'web' ]; then
|
||||
@@ -518,6 +540,14 @@ if [ "$from_source" = 'yes' ]; then
|
||||
[ -n "$server_root" ] || fail 'cannot ask stack for --local-install-root'
|
||||
[ -f "$server_root/bin/chusql-server" ] || fail "chusql-server not found after the build: $server_root/bin/chusql-server"
|
||||
cp "$server_root/bin/chusql-server" "$pack/bin/"
|
||||
|
||||
# 引导程序独立成工程:建 system 目录不依赖服务本身,坏了还能单独重跑
|
||||
step 'Building the bootstrap program (stack build --fast chusql-bootstrap:exe:csql-bootstrap)'
|
||||
( cd "$src/chusql-bootstrap" && stack build --fast chusql-bootstrap:exe:csql-bootstrap ) || fail 'stack build failed in chusql-bootstrap'
|
||||
bootstrap_root=$( cd "$src/chusql-bootstrap" && stack path --local-install-root 2>/dev/null | tr -d '\r' )
|
||||
[ -n "$bootstrap_root" ] || fail 'cannot ask stack for --local-install-root'
|
||||
[ -f "$bootstrap_root/bin/csql-bootstrap" ] || fail "csql-bootstrap not found after the build: $bootstrap_root/bin/csql-bootstrap"
|
||||
cp "$bootstrap_root/bin/csql-bootstrap" "$pack/bin/"
|
||||
elif [ -n "$url_arg" ]; then
|
||||
# ---------- 指定 URL / 本地归档 ----------
|
||||
new_tmp
|
||||
@@ -567,6 +597,7 @@ fi
|
||||
# ---- 校验包内容 ----
|
||||
ls "$pack/bin/"libchusql_core_storage.* >/dev/null 2>&1 || fail "package is incomplete: bin/libchusql_core_storage.* not found in $pack"
|
||||
[ -f "$pack/bin/chusql-server" ] || fail "package is incomplete: bin/chusql-server not found in $pack"
|
||||
[ -f "$pack/bin/csql-bootstrap" ] || fail "package is incomplete: bin/csql-bootstrap not found in $pack"
|
||||
if [ "$want_web" = 'yes' ]; then
|
||||
[ -f "$pack/bin/chusql-web" ] || fail 'package is incomplete: bin/chusql-web not found'
|
||||
[ -d "$pack/static" ] || fail 'package is incomplete: static/ not found'
|
||||
@@ -581,17 +612,14 @@ echo " version $version"
|
||||
echo " components $component_label"
|
||||
echo " install to $install_dir"
|
||||
echo " data dir $data_dir"
|
||||
if [ -n "$root_password" ]; then
|
||||
echo " root user $root_user"
|
||||
else
|
||||
echo " root user $root_user (no password: administrator-only sign-in)"
|
||||
fi
|
||||
echo " root user $root_user (password asked above, kept out of chusql.toml)"
|
||||
|
||||
# ---- 释放文件 ----
|
||||
# 包是合在一起的(web 和 cli 都在),这里按这次的选择逐个释放:没选的组件不落地
|
||||
step 'Installing files'
|
||||
mkdir -p "$install_dir/bin" "$install_dir/logs" "$data_dir"
|
||||
cp "$pack/bin/chusql-server" "$install_dir/bin/"
|
||||
cp "$pack/bin/csql-bootstrap" "$install_dir/bin/"
|
||||
for lib in "$pack/bin/"libchusql_core_storage.*; do
|
||||
if [ -f "$lib" ]; then cp "$lib" "$install_dir/bin/"; fi
|
||||
done
|
||||
@@ -618,11 +646,17 @@ config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ChuSQL"
|
||||
config_file="$config_dir/chusql.toml"
|
||||
mkdir -p "$config_dir"
|
||||
sed -e "s|^\(user[[:space:]]*=[[:space:]]*\).*|\1\"$root_user\"|" \
|
||||
-e "s|^\(password[[:space:]]*=[[:space:]]*\).*|\1\"$root_password\"|" \
|
||||
-e "s|^\(data_dir[[:space:]]*=[[:space:]]*\).*|\1\"$data_dir\"|" \
|
||||
"$pack/scripts/chusql.toml" > "$config_file"
|
||||
echo " config file $config_file"
|
||||
if [ -f "$pack/scripts/init.sql" ]; then cp "$pack/scripts/init.sql" "$install_dir/"; fi
|
||||
|
||||
# ---- 引导系统目录 ----
|
||||
# csql-bootstrap 建 system 数据库和 __system_users 表;口令只走 stdin,不进 chusql.toml
|
||||
step 'Creating the system catalog'
|
||||
bootstrap="$install_dir/bin/csql-bootstrap"
|
||||
[ -n "$admin_password" ] || fail 'the administrator password is required'
|
||||
printf '%s\n' "$admin_password" | "$bootstrap" --config "$config_file" --user "$root_user" --password-stdin ||
|
||||
fail 'the bootstrap program failed; the system catalog is not ready'
|
||||
|
||||
# ---- 命令入口 ----
|
||||
step 'Creating commands'
|
||||
@@ -676,6 +710,24 @@ else
|
||||
echo " package kept at $pack"
|
||||
fi
|
||||
|
||||
# ---- 拉起服务 ----
|
||||
server_bin="$install_dir/bin/chusql-server"
|
||||
server_pid=''
|
||||
if [ "$no_start" = 'yes' ]; then
|
||||
note '--no-start: the server was not started'
|
||||
else
|
||||
step 'Starting the server'
|
||||
mkdir -p "$install_dir/logs"
|
||||
nohup "$server_bin" --config "$config_file" > "$install_dir/logs/server.log" 2> "$install_dir/logs/server.err.log" &
|
||||
server_pid=$!
|
||||
sleep 1
|
||||
if ! kill -0 "$server_pid" 2>/dev/null; then
|
||||
fail "the server stopped right away; look at $install_dir/logs/server.err.log"
|
||||
fi
|
||||
note "server running, pid $server_pid"
|
||||
note "log $install_dir/logs/server.err.log"
|
||||
fi
|
||||
|
||||
step 'Done'
|
||||
echo " chusql.toml $config_file"
|
||||
echo " data $data_dir"
|
||||
@@ -685,4 +737,9 @@ if [ "$want_cli" = 'yes' ]; then
|
||||
if [ -n "$start_with" ]; then start_with="$start_with, csql"; else start_with='csql'; fi
|
||||
fi
|
||||
echo " start with $start_with"
|
||||
echo " tcp server $install_dir/bin/chusql-server (listens on [server] host/port, defaults 127.0.0.1:7777)"
|
||||
echo " tcp server $server_bin (listens on [server] host/port, defaults 127.0.0.1:7777)"
|
||||
if [ -n "$server_pid" ]; then
|
||||
echo " running pid $server_pid (stop it with: kill $server_pid)"
|
||||
else
|
||||
echo " start it $server_bin --config \"$config_file\""
|
||||
fi
|
||||
+29
-5
@@ -49,11 +49,14 @@ function Assert-Stage([string]$stage) {
|
||||
'bin\csql.exe',
|
||||
'bin\chusql-web.exe',
|
||||
'bin\chusql-server.exe',
|
||||
'bin\csql-bootstrap.exe',
|
||||
'static\index.html',
|
||||
'csql-web.ps1',
|
||||
'scripts\chusql.toml',
|
||||
'install.ps1',
|
||||
'install.sh'
|
||||
'install.sh',
|
||||
'uninstall.ps1',
|
||||
'uninstall.sh'
|
||||
)
|
||||
foreach ($rel in $must) {
|
||||
if (-not (Test-Path (Join-Path $stage $rel))) {
|
||||
@@ -61,7 +64,7 @@ function Assert-Stage([string]$stage) {
|
||||
}
|
||||
}
|
||||
# bin 里带 chusql 名字的文件只能是已知产物;改名前的残留或别的垃圾都算装配错误
|
||||
$known = @('chusql_core_storage.dll', 'chusql-server.exe', 'chusql-web.exe')
|
||||
$known = @('chusql_core_storage.dll', 'chusql-server.exe', 'chusql-web.exe', 'csql-bootstrap.exe')
|
||||
$unknown = @(Get-ChildItem (Join-Path $stage 'bin') -Filter 'chusql*' -File -ErrorAction SilentlyContinue |
|
||||
Where-Object { $known -notcontains $_.Name })
|
||||
if ($unknown.Count -gt 0) {
|
||||
@@ -91,7 +94,7 @@ function Test-Installed([string]$name, [string]$comp) {
|
||||
$pwsh = Join-Path $PSHOME 'pwsh.exe'
|
||||
if (-not (Test-Path $pwsh)) { $pwsh = 'powershell' }
|
||||
& $pwsh -NoProfile -File (Join-Path $pkg 'install.ps1') -Component $comp -InstallDir $opt `
|
||||
-DataDir $data -RootUser root -RootPassword package-check *>&1 | Out-File -FilePath $log -Encoding UTF8
|
||||
-DataDir $data -RootUser root -Password 'verify-package' -NoStart *>&1 | Out-File -FilePath $log -Encoding UTF8
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Get-Content $log | Select-Object -First 120 | Write-Host
|
||||
Fail "$name`: installing the $comp part from the package failed (log above)"
|
||||
@@ -100,7 +103,8 @@ function Test-Installed([string]$name, [string]$comp) {
|
||||
$checks = @(
|
||||
(Join-Path $opt 'bin\chusql_core_storage.dll'),
|
||||
(Join-Path $opt 'bin\chusql-server.exe'),
|
||||
(Join-Path $opt 'init.sql'),
|
||||
(Join-Path $opt 'bin\csql-bootstrap.exe'),
|
||||
(Join-Path $data 'system\catalog.json'),
|
||||
(Join-Path $appdata 'ChuSQL\chusql.toml')
|
||||
)
|
||||
$forbidden = @()
|
||||
@@ -162,6 +166,13 @@ if (-not $NoBuild) {
|
||||
if ($LASTEXITCODE -ne 0) { Fail "stack build --fast $target failed" }
|
||||
} finally { Pop-Location }
|
||||
}
|
||||
|
||||
Step 'Building chusql-bootstrap:exe:csql-bootstrap (Haskell)'
|
||||
Push-Location (Join-Path $repoRoot 'chusql-bootstrap')
|
||||
try {
|
||||
& stack build --fast chusql-bootstrap:exe:csql-bootstrap
|
||||
if ($LASTEXITCODE -ne 0) { Fail 'stack build --fast csql-bootstrap failed' }
|
||||
} finally { Pop-Location }
|
||||
}
|
||||
|
||||
# ---------- 装配 + 打归档 ----------
|
||||
@@ -194,13 +205,26 @@ try {
|
||||
Copy-Item $front (Join-Path $stage 'bin')
|
||||
}
|
||||
|
||||
# 引导程序住在自己的工程里,安装根也单独问它要
|
||||
Push-Location (Join-Path $repoRoot 'chusql-bootstrap')
|
||||
try { $bootstrapRoot = (& stack path --local-install-root | Select-Object -Last 1).Trim() } finally { Pop-Location }
|
||||
$bootstrapExe = Join-Path $bootstrapRoot 'bin/csql-bootstrap.exe'
|
||||
if (-not (Test-Path $bootstrapExe)) {
|
||||
$bootstrapExe = Get-ChildItem -Path (Join-Path $repoRoot 'chusql-bootstrap\.stack-work') `
|
||||
-Recurse -Filter 'csql-bootstrap.exe' -File -ErrorAction SilentlyContinue |
|
||||
Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName
|
||||
}
|
||||
if (-not $bootstrapExe -or -not (Test-Path $bootstrapExe)) { Fail 'csql-bootstrap.exe is not built (run without -NoBuild)' }
|
||||
Copy-Item $bootstrapExe (Join-Path $stage 'bin')
|
||||
|
||||
# 第三方 dll 才要连带上:名字带 chusql 的是本 crate 产物或改名前的残留,上面已按名拷过
|
||||
Get-ChildItem (Join-Path $repoRoot 'chusql-core\storage\target\release') -Filter '*.dll' -File -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -notlike 'chusql*' } |
|
||||
ForEach-Object { Copy-Item $_.FullName (Join-Path $stage 'bin') }
|
||||
|
||||
Copy-Item (Join-Path $repoRoot 'scripts\chusql.toml'), (Join-Path $repoRoot 'scripts\init.sql') (Join-Path $stage 'scripts')
|
||||
Copy-Item (Join-Path $repoRoot 'scripts\chusql.toml') (Join-Path $stage 'scripts')
|
||||
Copy-Item (Join-Path $repoRoot 'scripts\install.ps1'), (Join-Path $repoRoot 'scripts\install.sh') $stage
|
||||
Copy-Item (Join-Path $repoRoot 'scripts\uninstall.ps1'), (Join-Path $repoRoot 'scripts\uninstall.sh') $stage
|
||||
New-Item -ItemType Directory -Force -Path (Join-Path $stage 'static') | Out-Null
|
||||
Copy-Item (Join-Path $repoRoot 'chusql-web\static\*') (Join-Path $stage 'static') -Recurse
|
||||
Copy-Item (Join-Path $repoRoot 'scripts\csql-web.sh') $stage
|
||||
|
||||
+24
-7
@@ -90,16 +90,19 @@ check_stage() {
|
||||
[ -f "$stage/bin/csql" ] || Fail 'bin/csql is missing from the package'
|
||||
[ -f "$stage/bin/chusql-web" ] || Fail 'bin/chusql-web is missing from the package'
|
||||
[ -f "$stage/bin/chusql-server" ] || Fail 'bin/chusql-server is missing from the package'
|
||||
[ -f "$stage/bin/csql-bootstrap" ] || Fail 'bin/csql-bootstrap is missing from the package'
|
||||
[ -f "$stage/static/index.html" ] || Fail 'static/index.html is missing from the package'
|
||||
[ -f "$stage/csql-web.sh" ] || Fail 'csql-web.sh is missing from the package'
|
||||
[ -f "$stage/scripts/chusql.toml" ] || Fail 'scripts/chusql.toml is missing from the package'
|
||||
[ -f "$stage/install.sh" ] || Fail 'install.sh is missing from the package'
|
||||
[ -f "$stage/install.ps1" ] || Fail 'install.ps1 is missing from the package'
|
||||
[ -f "$stage/uninstall.sh" ] || Fail 'uninstall.sh is missing from the package'
|
||||
[ -f "$stage/uninstall.ps1" ] || Fail 'uninstall.ps1 is missing from the package'
|
||||
# bin 里带 chusql 名字的文件只能是已知产物;改名前的残留或别的垃圾都算装配错误
|
||||
for f in "$stage"/bin/chusql* "$stage"/bin/libchusql*; do
|
||||
[ -e "$f" ] || continue
|
||||
case "$(basename "$f")" in
|
||||
libchusql_core_storage.so|libchusql_core_storage.dylib|libchusql_core_storage.dll|chusql-server|chusql-web) ;;
|
||||
libchusql_core_storage.so|libchusql_core_storage.dylib|libchusql_core_storage.dll|chusql-server|chusql-web|csql-bootstrap) ;;
|
||||
*) Fail "unexpected artifact in bin/: $(basename "$f")" ;;
|
||||
esac
|
||||
done
|
||||
@@ -116,8 +119,10 @@ verify_package() {
|
||||
rm -rf "$vtmp"
|
||||
Fail "$name: the archive does not unpack"
|
||||
fi
|
||||
if ! ( cd "$vtmp/pkg" && HOME="$vtmp/home" sh install.sh --component "$comp" \
|
||||
--install-dir "$vtmp/opt" --data-dir "$vtmp/data" --user root --password package-check ) \
|
||||
if ! ( cd "$vtmp/pkg" && HOME="$vtmp/home" CHUSQL_ADMIN_PASSWORD='verify-package' \
|
||||
sh install.sh --component "$comp" \
|
||||
--install-dir "$vtmp/opt" --data-dir "$vtmp/data" --user root \
|
||||
--no-start ) \
|
||||
>"$vtmp/log" 2>&1; then
|
||||
sed -n '1,120p' "$vtmp/log" >&2
|
||||
rm -rf "$vtmp"
|
||||
@@ -126,7 +131,8 @@ verify_package() {
|
||||
ok=yes
|
||||
ls "$vtmp/opt/bin/"libchusql_core_storage.* >/dev/null 2>&1 || ok=no
|
||||
[ -f "$vtmp/opt/bin/chusql-server" ] || ok=no
|
||||
[ -f "$vtmp/opt/init.sql" ] || ok=no
|
||||
[ -f "$vtmp/opt/bin/csql-bootstrap" ] || ok=no
|
||||
[ -f "$vtmp/data/system/catalog.json" ] || ok=no
|
||||
[ -f "$vtmp/home/.config/ChuSQL/chusql.toml" ] || ok=no
|
||||
grep -q "data_dir = \"$vtmp/data\"" "$vtmp/home/.config/ChuSQL/chusql.toml" || ok=no
|
||||
if [ "$comp" = web ]; then
|
||||
@@ -164,6 +170,8 @@ if [ "$build" = yes ]; then
|
||||
( cd "$repo_root/chusql-cli" && stack build --fast chusql-cli:exe:csql )
|
||||
Step 'Building the TCP database server (Haskell)'
|
||||
( cd "$repo_root/chusql-cli" && stack build --fast chusql-server:exe:chusql-server )
|
||||
Step 'Building the bootstrap program (Haskell)'
|
||||
( cd "$repo_root/chusql-bootstrap" && stack build --fast chusql-bootstrap:exe:csql-bootstrap )
|
||||
fi
|
||||
|
||||
# ---------- 装配 + 打归档 ----------
|
||||
@@ -191,6 +199,15 @@ for front_name in chusql-web chusql-server csql; do
|
||||
cp "$front" "$stage/bin/"
|
||||
done
|
||||
|
||||
# 引导程序住在自己的工程里,安装根也单独问它要
|
||||
bootstrap_root=$(cd "$repo_root/chusql-bootstrap" && stack path --local-install-root | tail -n 1 | tr -d '\r')
|
||||
bootstrap_bin="$bootstrap_root/bin/csql-bootstrap"
|
||||
if [ ! -f "$bootstrap_bin" ]; then
|
||||
bootstrap_bin=$(find "$repo_root/chusql-bootstrap/.stack-work" -name csql-bootstrap -type f 2>/dev/null | head -n 1 || true)
|
||||
fi
|
||||
[ -n "$bootstrap_bin" ] && [ -f "$bootstrap_bin" ] || Fail 'csql-bootstrap is not built (run without --no-build)'
|
||||
cp "$bootstrap_bin" "$stage/bin/"
|
||||
|
||||
# Haskell 一般静态链接,靶目录里真有共享库就一起带上;改名前的旧库(libchusql_storage.*)不能进包
|
||||
for lib in "$repo_root"/chusql-core/storage/target/release/*.so "$repo_root"/chusql-core/storage/target/release/*.dylib; do
|
||||
[ -f "$lib" ] || continue
|
||||
@@ -200,13 +217,13 @@ for lib in "$repo_root"/chusql-core/storage/target/release/*.so "$repo_root"/chu
|
||||
cp "$lib" "$stage/bin/"
|
||||
done
|
||||
|
||||
cp "$repo_root/scripts/chusql.toml" "$repo_root/scripts/init.sql" "$stage/scripts/"
|
||||
cp "$repo_root/scripts/install.sh" "$repo_root/scripts/install.ps1" "$stage/"
|
||||
cp "$repo_root/scripts/chusql.toml" "$stage/scripts/"
|
||||
cp "$repo_root/scripts/install.sh" "$repo_root/scripts/install.ps1" "$repo_root/scripts/uninstall.sh" "$repo_root/scripts/uninstall.ps1" "$stage/"
|
||||
mkdir -p "$stage/static"
|
||||
cp -R "$repo_root"/chusql-web/static/. "$stage/static/"
|
||||
cp "$repo_root/scripts/csql-web.sh" "$stage/"
|
||||
if [ -f "$repo_root/scripts/csql-web.ps1" ]; then cp "$repo_root/scripts/csql-web.ps1" "$stage/"; fi
|
||||
chmod +x "$stage/install.sh" "$stage/csql-web.sh"
|
||||
chmod +x "$stage/install.sh" "$stage/uninstall.sh" "$stage/csql-web.sh"
|
||||
|
||||
check_stage "$stage"
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$InstallDir = '',
|
||||
[string]$DataDir = '',
|
||||
[switch]$KeepData,
|
||||
[switch]$KeepConfig,
|
||||
[switch]$Yes
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# Windows 卸载脚本:停服务、删程序与配置、从用户 PATH 摘掉安装目录。
|
||||
# 数据目录默认一起删,-KeepData 保留。
|
||||
|
||||
# 打印步骤标题。
|
||||
function Step([string]$msg) { Write-Host ''; Write-Host "==> $msg" -ForegroundColor Cyan }
|
||||
# 打印一行输出。
|
||||
function Say([string]$msg) { Write-Host $msg }
|
||||
# 报错退出。
|
||||
function Fail([string]$msg) { Write-Host "!! $msg" -ForegroundColor Red; exit 1 }
|
||||
|
||||
if (-not $InstallDir) { $InstallDir = Join-Path $env:LOCALAPPDATA 'ChuSQL' }
|
||||
if (-not $DataDir) { $DataDir = Join-Path $InstallDir 'data' }
|
||||
$InstallDir = [System.IO.Path]::GetFullPath($InstallDir)
|
||||
$DataDir = [System.IO.Path]::GetFullPath($DataDir)
|
||||
$configDir = if ($env:APPDATA) { Join-Path $env:APPDATA 'ChuSQL' } else { Join-Path $InstallDir 'config' }
|
||||
$configFile = Join-Path $configDir 'chusql.toml'
|
||||
|
||||
if (-not $Yes) {
|
||||
if ([Console]::IsInputRedirected) {
|
||||
Fail 'no terminal: rerun with -Yes to confirm'
|
||||
}
|
||||
Say 'ChuSQL uninstaller'
|
||||
Say (" install dir {0}" -f $InstallDir)
|
||||
Say (" data dir {0}" -f $DataDir)
|
||||
if ($KeepData) { Say ' data kept' }
|
||||
$answer = (Read-Host 'Remove it? [y/N]').Trim()
|
||||
if ($answer -notmatch '^(y|yes)$') {
|
||||
Say 'nothing was removed'
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
|
||||
Step 'Stopping the service'
|
||||
$running = @(Get-Process -Name 'chusql-server', 'chusql-web' -ErrorAction SilentlyContinue |
|
||||
Where-Object {
|
||||
try { $_.Path -and $_.Path.StartsWith($InstallDir, [StringComparison]::OrdinalIgnoreCase) }
|
||||
catch { $false }
|
||||
})
|
||||
foreach ($proc in $running) {
|
||||
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
||||
Say (" stopped {0} (pid {1})" -f $proc.ProcessName, $proc.Id)
|
||||
}
|
||||
if ($running.Count -eq 0) { Say ' nothing was running' }
|
||||
|
||||
Step 'Removing files'
|
||||
if (-not (Test-Path $InstallDir)) {
|
||||
Say (" not found: {0}" -f $InstallDir)
|
||||
} elseif ($KeepData) {
|
||||
Get-ChildItem -LiteralPath $InstallDir -Force | ForEach-Object {
|
||||
if ($_.FullName -eq $DataDir) {
|
||||
Say (" kept data: {0}" -f $_.FullName)
|
||||
} else {
|
||||
Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
Say (" kept {0} (data only)" -f $InstallDir)
|
||||
} else {
|
||||
Remove-Item -LiteralPath $InstallDir -Recurse -Force
|
||||
if (-not $DataDir.StartsWith($InstallDir, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
Remove-Item -LiteralPath $DataDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Say (" removed {0}" -f $InstallDir)
|
||||
}
|
||||
|
||||
Step 'Removing chusql.toml'
|
||||
if ($KeepConfig) {
|
||||
Say (" kept config: {0}" -f $configFile)
|
||||
} else {
|
||||
if (Test-Path $configFile) {
|
||||
Remove-Item -LiteralPath $configFile -Force
|
||||
Say (" removed config: {0}" -f $configFile)
|
||||
} else {
|
||||
Say (" no config file: {0}" -f $configFile)
|
||||
}
|
||||
if (Test-Path $configDir) { Remove-Item -LiteralPath $configDir -Force -ErrorAction SilentlyContinue }
|
||||
}
|
||||
|
||||
Step 'Removing the PATH entry'
|
||||
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
||||
if (-not $userPath) { $userPath = '' }
|
||||
$entries = @($userPath -split ';' | Where-Object { $_ })
|
||||
$updated = @($entries | Where-Object { $_ -ne $InstallDir })
|
||||
if ($updated.Count -ne $entries.Count) {
|
||||
[Environment]::SetEnvironmentVariable('Path', ($updated -join ';'), 'User')
|
||||
Say (" removed {0} from the user PATH" -f $InstallDir)
|
||||
} else {
|
||||
Say ' the user PATH has no install directory'
|
||||
}
|
||||
|
||||
Step 'Done'
|
||||
Say ' open a new terminal: the old PATH stays in the current one'
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
# ChuSQL 卸载脚本(Linux/macOS):停服务、删程序与配置、
|
||||
# 从 .profile 摘掉 PATH 段。数据目录默认一起删。
|
||||
|
||||
install_dir=''
|
||||
data_dir=''
|
||||
keep_data='no'
|
||||
keep_config='no'
|
||||
assume_yes='no'
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Uninstall ChuSQL (Linux/macOS).
|
||||
|
||||
Usage: uninstall.sh [options]
|
||||
|
||||
--install-dir DIR program directory (default: $HOME/.local/share/chusql)
|
||||
--data-dir DIR data directory (default: <install-dir>/data)
|
||||
--keep-data keep the data directory, remove programs only
|
||||
--keep-config keep chusql.toml
|
||||
--yes do not ask (required when there is no terminal)
|
||||
-h, --help show this help
|
||||
EOF
|
||||
}
|
||||
|
||||
step() { echo "== $1"; }
|
||||
note() { echo " $1"; }
|
||||
fail() {
|
||||
echo "!! $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 停掉命令行里带这个路径的进程
|
||||
stop_by_path() {
|
||||
target=$1
|
||||
[ -n "$target" ] || return 0
|
||||
pids=$(ps -eo pid=,args= 2>/dev/null |
|
||||
awk -v t="$target" '{ pid = $1; $1 = ""; sub(/^ +/, ""); if (index($0, t) == 1) print pid }' || true)
|
||||
[ -n "$pids" ] || return 0
|
||||
for pid in $pids; do
|
||||
kill "$pid" 2>/dev/null || true
|
||||
done
|
||||
note "stopped: $pids"
|
||||
sleep 1
|
||||
for pid in $pids; do
|
||||
kill -9 "$pid" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
# 问一句 y/n,不是 y 就当没同意
|
||||
ask_yes_no() {
|
||||
printf '%s ' "$1" >&2
|
||||
answer=''
|
||||
IFS= read -r answer || answer=''
|
||||
case "$answer" in
|
||||
y | Y | yes | YES | Yes) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h | --help) usage; exit 0 ;;
|
||||
--install-dir) install_dir="${2:-}"; shift 2 ;;
|
||||
--data-dir) data_dir="${2:-}"; shift 2 ;;
|
||||
--keep-data) keep_data='yes'; shift ;;
|
||||
--keep-config) keep_config='yes'; shift ;;
|
||||
--yes) assume_yes='yes'; shift ;;
|
||||
*) fail "unknown option: $1 (try --help)" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
install_dir=${install_dir:-$HOME/.local/share/chusql}
|
||||
data_dir=${data_dir:-$install_dir/data}
|
||||
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ChuSQL"
|
||||
config_file="$config_dir/chusql.toml"
|
||||
profile="$HOME/.profile"
|
||||
|
||||
if [ "$assume_yes" = 'no' ]; then
|
||||
echo 'ChuSQL uninstaller'
|
||||
echo " install dir $install_dir"
|
||||
echo " data dir $data_dir"
|
||||
if [ "$keep_data" = 'yes' ]; then echo ' data kept'; fi
|
||||
if [ -t 0 ]; then
|
||||
ask_yes_no 'Remove it? [y/N]' || {
|
||||
echo 'nothing was removed'
|
||||
exit 0
|
||||
}
|
||||
else
|
||||
fail 'no terminal: rerun with --yes to confirm'
|
||||
fi
|
||||
fi
|
||||
|
||||
step 'Stopping the service'
|
||||
stop_by_path "$install_dir/bin/chusql-server"
|
||||
stop_by_path "$install_dir/bin/chusql-web"
|
||||
|
||||
step 'Removing files'
|
||||
if [ ! -d "$install_dir" ]; then
|
||||
note "not found: $install_dir"
|
||||
elif [ "$keep_data" = 'yes' ]; then
|
||||
for entry in "$install_dir"/*; do
|
||||
[ -e "$entry" ] || continue
|
||||
if [ "$entry" = "$data_dir" ]; then
|
||||
note "kept data: $entry"
|
||||
continue
|
||||
fi
|
||||
rm -rf "$entry"
|
||||
done
|
||||
note "kept $install_dir (data only)"
|
||||
else
|
||||
rm -rf "$install_dir"
|
||||
case "$data_dir" in
|
||||
"$install_dir" | "$install_dir"/*) ;;
|
||||
*) rm -rf "$data_dir" ;;
|
||||
esac
|
||||
note "removed $install_dir"
|
||||
fi
|
||||
|
||||
step 'Removing chusql.toml'
|
||||
if [ "$keep_config" = 'yes' ]; then
|
||||
note "kept config: $config_file"
|
||||
else
|
||||
rm -f "$config_file"
|
||||
rmdir "$config_dir" 2>/dev/null || true
|
||||
note "removed config: $config_file"
|
||||
fi
|
||||
|
||||
step 'Removing the PATH entry'
|
||||
marker='# ChuSQL'
|
||||
if [ ! -f "$profile" ]; then
|
||||
note "no $profile"
|
||||
elif grep -q "^$marker" "$profile"; then
|
||||
tmp="$profile.chusql-uninstall"
|
||||
awk -v m="$marker" '
|
||||
skip > 0 { skip--; next }
|
||||
index($0, m) == 1 { skip = 2; next }
|
||||
{ print }
|
||||
' "$profile" >"$tmp" && mv "$tmp" "$profile"
|
||||
note "removed the $marker block from $profile"
|
||||
else
|
||||
note "no $marker block in $profile"
|
||||
fi
|
||||
|
||||
step 'Done'
|
||||
note 'open a new terminal: the old PATH stays in the current one'
|
||||
Reference in new issue
Block a user