准备可扩展类型系统工作,更合理的JOIN查询优化算法,并行查询

This commit is contained in:
LIm1tZz7 committed 2026-10-03 11:56:13 +08:00
1 parent 9758439b82
commit 274b7fcdcc
75 files changed
+3828 -1789

No files matched your search

+3 -3
View File
@@ -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
-16
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"
+102
View File
@@ -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'
+148
View File
@@ -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'