mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-01 01:10:02 +08:00
- 将 JDBC allowMultiQueries 参数映射为 MySQL driver 支持的 multiStatements - 修复自定义 MySQL DSN 透传导致旧版本 MySQL 连接失败的问题 - 更新 MySQL 兼容 driver-agent revision Refs #441
397 lines
9.8 KiB
Bash
Executable File
397 lines
9.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
set -euo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||
cd "$SCRIPT_DIR"
|
||
|
||
DEFAULT_DRIVERS=(mariadb oceanbase diros starrocks sphinx sqlserver sqlite duckdb dameng kingbase highgo vastbase opengauss iris mongodb tdengine clickhouse)
|
||
OUTPUT_FILE="internal/db/driver_agent_revisions_gen.go"
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
用法:
|
||
./tools/generate-driver-agent-revisions.sh [选项]
|
||
|
||
选项:
|
||
--platform <GOOS/GOARCH> 按目标平台解析 Go build tags,默认使用当前 Go 环境
|
||
--drivers <列表> 只更新指定驱动(逗号分隔),并保留其他已生成 revision
|
||
-h, --help 显示帮助
|
||
EOF
|
||
}
|
||
|
||
normalize_driver() {
|
||
local value
|
||
value="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
|
||
case "$value" in
|
||
doris|diros) echo "diros" ;;
|
||
oceanbase) echo "oceanbase" ;;
|
||
opengauss|open_gauss|open-gauss) echo "opengauss" ;;
|
||
mariadb|diros|starrocks|sphinx|sqlserver|sqlite|duckdb|dameng|kingbase|highgo|vastbase|iris|mongodb|tdengine|clickhouse)
|
||
echo "$value"
|
||
;;
|
||
*)
|
||
return 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
build_driver_name() {
|
||
echo "$1"
|
||
}
|
||
|
||
driver_build_tags() {
|
||
local driver="$1"
|
||
local build_driver tag
|
||
build_driver="$(build_driver_name "$driver")"
|
||
tag="gonavi_${build_driver}_driver"
|
||
if [[ "$driver" == "duckdb" && "$goos" == "windows" && "$goarch" == "amd64" ]]; then
|
||
tag="$tag duckdb_use_lib"
|
||
fi
|
||
echo "$tag"
|
||
}
|
||
|
||
hash_file() {
|
||
local target="$1"
|
||
if command -v sha256sum >/dev/null 2>&1; then
|
||
sha256sum "$target" | awk '{print $1}'
|
||
return
|
||
fi
|
||
if command -v shasum >/dev/null 2>&1; then
|
||
shasum -a 256 "$target" | awk '{print $1}'
|
||
return
|
||
fi
|
||
echo "未找到 sha256sum 或 shasum" >&2
|
||
exit 1
|
||
}
|
||
|
||
files_equal() {
|
||
local left="$1"
|
||
local right="$2"
|
||
if command -v cmp >/dev/null 2>&1; then
|
||
cmp -s "$left" "$right"
|
||
return
|
||
fi
|
||
[[ "$(hash_file "$left")" == "$(hash_file "$right")" ]]
|
||
}
|
||
|
||
should_include_internal_db_file() {
|
||
local driver="$1"
|
||
local identity="$2"
|
||
|
||
case "$identity" in
|
||
internal/db/agent_process_stub.go|\
|
||
internal/db/agent_process_windows.go|\
|
||
internal/db/database.go|\
|
||
internal/db/database_optional_factories_full.go|\
|
||
internal/db/database_optional_factories_lite.go|\
|
||
internal/db/driver_agent_binary_check.go|\
|
||
internal/db/driver_support.go|\
|
||
internal/db/json_decode.go|\
|
||
internal/db/mysql_agent_path.go|\
|
||
internal/db/optional_driver_agent_impl.go|\
|
||
internal/db/optional_driver_build_full.go|\
|
||
internal/db/optional_driver_build_lite.go|\
|
||
internal/db/query_value.go|\
|
||
internal/db/scan_rows.go|\
|
||
internal/db/ssl_mode.go|\
|
||
internal/db/timeout.go)
|
||
return 0
|
||
;;
|
||
esac
|
||
|
||
case "$driver:$identity" in
|
||
mariadb:internal/db/mariadb_impl.go|\
|
||
mariadb:internal/db/mysql_impl.go|\
|
||
oceanbase:internal/db/oceanbase_impl.go|\
|
||
oceanbase:internal/db/oracle_impl.go|\
|
||
oceanbase:internal/db/mysql_impl.go|\
|
||
diros:internal/db/diros_impl.go|\
|
||
diros:internal/db/mysql_impl.go|\
|
||
starrocks:internal/db/starrocks_impl.go|\
|
||
starrocks:internal/db/mysql_impl.go|\
|
||
sphinx:internal/db/sphinx_impl.go|\
|
||
sphinx:internal/db/mysql_impl.go|\
|
||
sqlserver:internal/db/sqlserver_impl.go|\
|
||
sqlite:internal/db/sqlite_impl.go|\
|
||
duckdb:internal/db/duckdb_impl.go|\
|
||
duckdb:internal/db/duckdb_driver_import.go|\
|
||
duckdb:internal/db/duckdb_platform_supported.go|\
|
||
duckdb:internal/db/duckdb_platform_unsupported.go|\
|
||
dameng:internal/db/dameng_impl.go|\
|
||
dameng:internal/db/dameng_metadata.go|\
|
||
kingbase:internal/db/kingbase_impl.go|\
|
||
kingbase:internal/db/kingbase_identifier_utils.go|\
|
||
highgo:internal/db/highgo_impl.go|\
|
||
vastbase:internal/db/vastbase_impl.go|\
|
||
opengauss:internal/db/opengauss_impl.go|\
|
||
opengauss:internal/db/postgres_impl.go|\
|
||
iris:internal/db/iris_impl.go|\
|
||
mongodb:internal/db/mongodb_impl.go|\
|
||
mongodb:internal/db/mongodb_impl_v1.go|\
|
||
tdengine:internal/db/tdengine_impl.go|\
|
||
clickhouse:internal/db/clickhouse_impl.go)
|
||
return 0
|
||
;;
|
||
esac
|
||
|
||
return 1
|
||
}
|
||
|
||
should_include_source_file() {
|
||
local driver="$1"
|
||
local identity="$2"
|
||
if [[ "$identity" == internal/db/* ]]; then
|
||
should_include_internal_db_file "$driver" "$identity"
|
||
return
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
target_platform=""
|
||
driver_csv=""
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--platform)
|
||
target_platform="${2:-}"
|
||
shift 2
|
||
;;
|
||
--drivers)
|
||
driver_csv="${2:-}"
|
||
shift 2
|
||
;;
|
||
-h|--help)
|
||
usage
|
||
exit 0
|
||
;;
|
||
*)
|
||
echo "未知参数:$1" >&2
|
||
usage
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
if ! command -v go >/dev/null 2>&1; then
|
||
echo "未找到 Go,请先安装 Go 并确保 go 在 PATH 中。" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if [[ -z "$target_platform" ]]; then
|
||
target_platform="$(go env GOOS)/$(go env GOARCH)"
|
||
fi
|
||
if [[ "$target_platform" != */* ]]; then
|
||
echo "--platform 参数格式错误,应为 GOOS/GOARCH,例如 darwin/arm64" >&2
|
||
exit 1
|
||
fi
|
||
|
||
goos="${target_platform%%/*}"
|
||
goarch="${target_platform##*/}"
|
||
gomodcache="$(go env GOMODCACHE)"
|
||
|
||
declare -a drivers=()
|
||
if [[ -n "$driver_csv" ]]; then
|
||
IFS=',' read -r -a raw_drivers <<<"$driver_csv"
|
||
for item in "${raw_drivers[@]}"; do
|
||
drivers+=("$(normalize_driver "$item")")
|
||
done
|
||
else
|
||
drivers=("${DEFAULT_DRIVERS[@]}")
|
||
fi
|
||
|
||
selected_driver_set="|"
|
||
for driver in "${drivers[@]}"; do
|
||
selected_driver_set="${selected_driver_set}${driver}|"
|
||
done
|
||
|
||
existing_revision_for() {
|
||
local target="$1"
|
||
local line
|
||
[[ -n "$driver_csv" && -f "$OUTPUT_FILE" ]] || return 1
|
||
while IFS= read -r line; do
|
||
if [[ "$line" =~ \"([^\"]+)\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
|
||
if [[ "${BASH_REMATCH[1]}" == "$target" ]]; then
|
||
printf '%s\n' "${BASH_REMATCH[2]}"
|
||
return 0
|
||
fi
|
||
fi
|
||
done <"$OUTPUT_FILE"
|
||
return 1
|
||
}
|
||
|
||
detect_revision_jobs() {
|
||
local configured="${GONAVI_DRIVER_REVISION_JOBS:-}"
|
||
local detected
|
||
|
||
if [[ "$configured" =~ ^[0-9]+$ && "$configured" -gt 0 ]]; then
|
||
echo "$configured"
|
||
return
|
||
fi
|
||
|
||
if command -v nproc >/dev/null 2>&1; then
|
||
detected="$(nproc)"
|
||
elif command -v sysctl >/dev/null 2>&1; then
|
||
detected="$(sysctl -n hw.ncpu 2>/dev/null || true)"
|
||
else
|
||
detected=""
|
||
fi
|
||
|
||
if ! [[ "$detected" =~ ^[0-9]+$ && "$detected" -gt 0 ]]; then
|
||
detected=4
|
||
fi
|
||
|
||
if [[ "$detected" -gt 4 ]]; then
|
||
detected=4
|
||
fi
|
||
echo "$detected"
|
||
}
|
||
|
||
declare -a output_drivers=()
|
||
if [[ -n "$driver_csv" ]]; then
|
||
output_drivers=("${DEFAULT_DRIVERS[@]}")
|
||
else
|
||
output_drivers=("${drivers[@]}")
|
||
fi
|
||
|
||
fingerprint_driver() {
|
||
local driver="$1"
|
||
local build_driver tag cgo_enabled tmp file identity file_hash revision
|
||
build_driver="$(build_driver_name "$driver")"
|
||
tag="$(driver_build_tags "$driver")"
|
||
cgo_enabled=0
|
||
if [[ "$driver" == "duckdb" ]]; then
|
||
cgo_enabled=1
|
||
fi
|
||
|
||
tmp="$(mktemp "${TMPDIR:-/tmp}/gonavi-agent-revision.XXXXXX")"
|
||
{
|
||
printf 'driver=%s\n' "$driver"
|
||
printf 'build_tag=%s\n' "$tag"
|
||
printf 'goos=%s\n' "$goos"
|
||
printf 'goarch=%s\n' "$goarch"
|
||
} >"$tmp"
|
||
|
||
while IFS= read -r file; do
|
||
[[ -n "$file" && -f "$file" ]] || continue
|
||
case "$file" in
|
||
"$SCRIPT_DIR"/*)
|
||
identity="${file#$SCRIPT_DIR/}"
|
||
;;
|
||
"$gomodcache"/*)
|
||
identity="gomod/${file#$gomodcache/}"
|
||
;;
|
||
*)
|
||
identity="$file"
|
||
;;
|
||
esac
|
||
if [[ "$identity" == "$OUTPUT_FILE" ]]; then
|
||
continue
|
||
fi
|
||
if ! should_include_source_file "$driver" "$identity"; then
|
||
continue
|
||
fi
|
||
file_hash="$(hash_file "$file")"
|
||
printf '%s %s\n' "$file_hash" "$identity"
|
||
done < <(
|
||
CGO_ENABLED="$cgo_enabled" GOOS="$goos" GOARCH="$goarch" GOTOOLCHAIN=auto \
|
||
go list -deps \
|
||
-tags "$tag" \
|
||
-f '{{if not .Standard}}{{range .GoFiles}}{{$.Dir}}/{{.}}{{"\n"}}{{end}}{{range .CgoFiles}}{{$.Dir}}/{{.}}{{"\n"}}{{end}}{{end}}' \
|
||
./cmd/optional-driver-agent | sort -u
|
||
) >>"$tmp"
|
||
|
||
revision="$(hash_file "$tmp" | cut -c1-16)"
|
||
rm -f "$tmp"
|
||
printf 'src-%s' "$revision"
|
||
}
|
||
|
||
revision_jobs="$(detect_revision_jobs)"
|
||
revision_tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-agent-revisions.XXXXXX")"
|
||
cleanup_revision_tmp_dir() {
|
||
rm -rf "$revision_tmp_dir"
|
||
}
|
||
trap cleanup_revision_tmp_dir EXIT
|
||
|
||
declare -a revision_pids=()
|
||
declare -a revision_pid_drivers=()
|
||
revision_failed=0
|
||
|
||
wait_for_oldest_revision_job() {
|
||
local pid driver
|
||
pid="${revision_pids[0]}"
|
||
driver="${revision_pid_drivers[0]}"
|
||
|
||
if ! wait "$pid"; then
|
||
echo "❌ 生成 driver-agent revision 失败:$driver ($goos/$goarch)" >&2
|
||
revision_failed=1
|
||
fi
|
||
|
||
if [[ ${#revision_pids[@]} -le 1 ]]; then
|
||
revision_pids=()
|
||
revision_pid_drivers=()
|
||
else
|
||
revision_pids=("${revision_pids[@]:1}")
|
||
revision_pid_drivers=("${revision_pid_drivers[@]:1}")
|
||
fi
|
||
}
|
||
|
||
start_revision_job() {
|
||
local driver="$1"
|
||
{
|
||
fingerprint_driver "$driver" >"$revision_tmp_dir/$driver.revision"
|
||
} &
|
||
revision_pids+=("$!")
|
||
revision_pid_drivers+=("$driver")
|
||
}
|
||
|
||
for driver in "${output_drivers[@]}"; do
|
||
if [[ -n "$driver_csv" && "$selected_driver_set" != *"|$driver|"* ]] && revision="$(existing_revision_for "$driver")"; then
|
||
printf '%s\n' "$revision" >"$revision_tmp_dir/$driver.revision"
|
||
continue
|
||
fi
|
||
|
||
while [[ ${#revision_pids[@]} -ge "$revision_jobs" ]]; do
|
||
wait_for_oldest_revision_job
|
||
done
|
||
start_revision_job "$driver"
|
||
done
|
||
|
||
while [[ ${#revision_pids[@]} -gt 0 ]]; do
|
||
wait_for_oldest_revision_job
|
||
done
|
||
|
||
if [[ "$revision_failed" -ne 0 ]]; then
|
||
exit 1
|
||
fi
|
||
|
||
tmp_output="$(mktemp "${TMPDIR:-/tmp}/gonavi-agent-revisions-go.XXXXXX")"
|
||
{
|
||
cat <<'EOF'
|
||
// Code generated by tools/generate-driver-agent-revisions.sh; DO NOT EDIT.
|
||
|
||
package db
|
||
|
||
func init() {
|
||
optionalDriverAgentRevisions = map[string]string{
|
||
EOF
|
||
for driver in "${output_drivers[@]}"; do
|
||
revision="$(<"$revision_tmp_dir/$driver.revision")"
|
||
printf '\t\t"%s": "%s",\n' "$driver" "$revision"
|
||
done
|
||
cat <<'EOF'
|
||
}
|
||
}
|
||
EOF
|
||
} >"$tmp_output"
|
||
|
||
gofmt -w "$tmp_output"
|
||
|
||
if [[ -f "$OUTPUT_FILE" ]] && files_equal "$tmp_output" "$OUTPUT_FILE"; then
|
||
rm -f "$tmp_output"
|
||
else
|
||
mv "$tmp_output" "$OUTPUT_FILE"
|
||
fi
|
||
|
||
echo "已生成 driver-agent revisions: $OUTPUT_FILE ($goos/$goarch)"
|