Repository navigation
Releases
Releases · livebug/open-dbclient
v0.7.2
修 0.7.1 上补全在慢库 / 大库上退化成“只有关键字”的问题。
修复
- 预取失败被当成“这个库没有表”缓存下来:当每一批表读取都失败(超时、规则报错)时,0.7.1
仍写入了一份空表列表并当作成功;补全随后只给关键字,直到缓存过期。现在全部失败时不写缓存、
记录失败,并在输出通道写一行Completion has no table list for ...,把原因(超时/报错)留在日志里 - 补全不再为一次按键等整次预取:预取按 schema 串行读取,库里 schema 多、驱动又慢时,一次
FROM会等到所有 schema 读完。现在补全最多等 1 秒,先给已经发布的 schema 名和表名,其余继续
在后台读;缓存过期时也先用旧列表,同时后台刷新 - 分批结果边读边发布:每读完一个 schema 就更新缓存,而不是全部读完才写入 —— 慢库上补全能
逐步变得完整,而不是长时间空白 - schema 列表读不到时不再回退到整库读取:那正是会耗尽桥堆的那条
getTables;现在直接报告
“没有安全的分批方式”,不读 - 预取有时间上限:最多 60 秒开始新的一批,避免一次预取长时间占住连接
v0.7.1
修复 0.7.0 上两处让基本使用失效的问题,并给元数据缓存加上有效期。
修复
- 配置了驱动目录却报 “JDBC driver ... is not loaded”:合并时
refreshAll命令被注册了两次,
registerCommand对重复 id 抛异常,整个activate在注册命令这一步中断 —— 后面「把驱动 jar
交给桥」根本没执行。桥能起来、连接菜单也能点,但驱动一个都没加载。删掉重复注册,并让
check-docs.mjs校验「同一条命令不得注册两次」,以后这类问题在npm test就会失败 - 桥自动重启后驱动全部丢失:驱动活在桥 JVM 里,而崩溃重启不经过「重启桥」命令。现在桥每次
进入 ready 都会重新加载驱动;桥一进入 starting/stopped,DriverManager就丢弃旧列表,
连接前发现驱动类不在会先重载,桥在连接途中被替换时也会重载并重试一次 - 驱动注册不再能让激活卡死:
driver.register加 120 秒超时,并在 info 级别留下
「正在加载 N 个驱动 jar」 - 大库上的元数据预取会把桥撑爆:整库一次
getTables的结果会被驱动整个读进堆内存,
超大 catalog 上足以耗尽堆。补全预取改为按 schema(或按 catalog)分批,并设 20000 张表
的总预算;每次读取和缓存占用因此都有界 metadata.queries的表规则在预取路径上恢复了作用:分批后每次读取都带 schema,
规则不再因「${schema}填不出来」被拒绝而回落到驱动- OOM 后不再半死不活:JVM 加
-XX:+ExitOnOutOfMemoryError,堆耗尽时直接退出,
由已有的自动重启换一个新进程(驱动会随之重新加载)
新增
metadata.cacheMinutes(默认5):树与补全的元数据缓存多少分钟后重新读取;0表示直到
断开连接或手动刷新。手动刷新总是立即清空
新增(仅开发)
check-docs.mjs增加重复命令注册校验;新增prefetchPlan.test.ts、cacheAge.test.ts,
把「按什么维度分批」与「缓存何时过期」两条规则固定下来
v0.7.0
本版同时包含 0.4.1–0.4.4 线上未发布的修复:中文日志、把连接自身的 catalog 带进元数据规则、
表 / 视图列表只读一次、健康报告取完整快照、补齐未注册的命令、限定名补全。逐条说明见仓库
CHANGELOG.md 的 0.4.4 一节。
新增
- 共享服务鉴权:
--listen <port> --token <secret>开启共享密钥鉴权,每个请求帧必须携带
token,否则返回UNAUTHORIZED(常量时间比较,防时序侧信道)。保护共享桥不被任意可达端口的
客户端驱动、也防system.shutdown被滥用。 - 连接归属隔离:连接记住打开它的租户(
connection.open的tenantId),后续
query.execute/metadata.*/connection.close/ 事务等操作必须携带同一tenantId,否则
返回FORBIDDEN。一个租户不能使用或关闭另一个租户的连接;connection.list会带出tenantId。 - 桥版本
0.3.0 -> 0.4.0。
v0.4.0
这一版的主题是把元数据读取交给用户写的 SQL,并且让你看得见它在做什么。前几轮反馈里最重要的一条
是“有些库展开一张表要等几分钟”—— 而慢的不是 SQL,是围着它的 DatabaseMetaData 读取。所以这一版做两件事:
凡是能换成用户自己写的语句的读取都开出来(现在覆盖模式/表/列/索引/目录/表类型六种,外加 DDL),以及
把“跑的是哪条语句、花在哪个调用、哪张表上”落到日志与健康报告里。
新增
- 连接树上显示表/视图的中文名。数据库给了表注释时,注释跟在表名后面(
orders 订单),悬浮提示里
是完整注释和附加字段。标签仍是物理名 —— 那才是要写进 SQL 的东西。树的筛选也匹配注释,
于是输入“订单”能找到orders(之前只比名称,中文名等于白写) - 元数据规则可以带回表注释和任意附加字段。别名成
REMARKS/TABLE_COMMENT/COMMENT的列被
认作表注释(多列都有值时按此顺序取),它随后出现在树上、参与筛选、并可作为${remark}用于自定义
动作与 DDL 规则;select 出来的其它列不再被丢弃,按你 select 的顺序(不是字母序)显示在节点的
悬浮提示里,形如EST_ROWS: 12000。这才是这个设置真正可扩展的地方:目录表里有用的字段,
不用等插件认识那个库 - 示例规则新增 MySQL 一档,PostgreSQL/openGauss 的例子改用
pg_catalog—— 比information_schema
快得多,而且只有它能取到表注释,也就是中文 schema 上人真正认得的那个名字 metadata.queries现在也能替掉列与索引的读取(kind: "columns"/"indexes")。之前只有
schemas/tables能自定义,而真正让树变慢的恰恰是每张表一次的getColumns:展开一张表、
补全缓存填充、Show Columns 各算一次。列规则能表达树会显示的每一件事(COLUMN_NAME、TYPE_NAME、
COLUMN_SIZE/DECIMAL_DIGITS、IS_NULLABLE、COLUMN_DEF、REMARKS、ORDINAL_POSITION、
IS_PRIMARY_KEY、IS_AUTOINCREMENT、IS_GENERATEDCOLUMN),索引规则是INDEX_NAME、
NON_UNIQUE、COLUMN_NAME、ORDINAL_POSITION、ASC_OR_DESC、TYPE_NAME、CARDINALITY;
没给的字段取诚实默认(类型 →UNKNOWN、可空性 → 未知(显示?)、JDBC 类型 →OTHER、
序号 → 结果行序),而不是编一个- 字面量占位符
${tableLiteral}/${schemaLiteral}/${catalogLiteral}/
${namePatternLiteral}:自带单引号并转义值里的引号。这些值是数据,自己往引号里塞的名字
遇到一张叫it's的表就变成了另一条语句。示例里全部改成= ${tableLiteral} catalogs与tableTypes也开了(原来只剩这两个读取不能自定义)。它们各是一次性的小结果、
平时不慢,所以理由是"万一你那个驱动在这里返回了没用的东西":目录列表把连不上的库也列出来、
表类型一个都不给。两者都不需要占位符(跑到它们时还不知道能按什么过滤),tableTypes的
kind 大小写都认(存下来的是规范拼写),名字列表会去重、空白条目按"没给"处理- 新增
columns示例(PostgreSQL/openGauss 走pg_attribute+format_type+col_description,
MySQL 走information_schema.columns)与 MySQL 的indexes示例。PG 的索引例子故意不给:
列出索引的列需要unnest ... WITH ORDINALITY或LATERAL,而这正是那些较老的
PostgreSQL 兼容库(本功能最想服务的对象)没有的语法,当示例发出去就会在它们上面报错
变更
- 自定义动作与 DDL 规则的占位符新增
${remark}(表注释;在列上是列注释),以及元数据规则带回来的
任何附加字段 —— 别名EST_ROWS就是${EST_ROWS}。${remark}和${column}一样填不出来就跳过
模板,而不是当成空串:按中文名查对象的语句少了这个名字,查出来的就是别的东西。附加字段里与
占位符重名的(table、schema这种)会被丢掉,否则一条奇怪的规则就能改掉所有模板指向的表 - 这些值都从节点上直接取,不再为了拿一个界面上已经显示出来的注释而多跑一次查询
- 每条元数据规则跑的到底是哪条 SQL,现在看得出来:命中规则时输出通道写一行(规则名 + 占位符已
填好的语句),没命中而回落驱动时写一行debug。规则是用户写的 SQL、却是插件选中的,而设置里那条
语句并不是发给数据库的那条(占位符被填过) —— 所以它和别的语句一样要能被查出来 - 导出时因
export.csv.quoting=never而留下的「有字段没加引号」提示,除了弹窗还写进
DB Client 输出通道 —— 弹窗几秒就消失,而这件事关系到已经落盘的文件,日志里还带着完整路径 - 列注释的读取不再每次都付费。注释是结果里每张表一次
getColumns换来的,而这正是几个慢库
上「查询慢」的真正原因,所以:① 只有会显示注释的调用才去读(结果网格、表预览、DDL 结果、导出表头),
树与补全的元数据读取一律不读;② 读到的结果按「连接 + URL + 表」在会话内缓存(LRU,上限 512 张表,
空结果也缓存 —— 不然"这张表没有注释"会变成每次查询都重来一次慢调用);③ 新增
result.columnRemarks(默认true),关掉后结果与导出只显示物理列名,一次元数据调用都不发 - 健康报告新增「元数据调用」:按
tables/columns/schemas/indexes/ddl统计次数、总耗时、
平均耗时与失败数,并列出最慢的几次读取及其对象(columns public.orders 12.3 s)。原来的
查询指标只回答「这条语句跑了多久」—— 而当时间根本没花在语句上时(慢驱动的getColumns是几
分钟的目录查询),看上去就像数据库慢。现在能把那笔开销指到具体的调用和具体的表上,也才有得选:
换掉它(metadata.queries)或关掉它(result.columnRemarks)。统计在每个元数据读取的唯一
入口采集,所以新增的读取不可能漏掉
新增(仅开发,不影响扩展行为)
scripts/check-i18n.mjs:界面文案以英文原文为键,漏译不会报错,只会让中文用户在一句中文里
看到一句英文 —— 这是只有那个语言的用户才会先看见的错。脚本扫src/**与media/**里的每个
t('...')字面量(连同'a' + 'b'拼接与转义),要求目录里都有条目;读不出来的调用必须列进白名单,
白名单里还要写清它可能产生哪几个键,否则白名单会退化成一句谁都看不懂的eslint-disable。
目录里多余的键只警告不失败。已接入npm test(npm run checks= 文档 + 文案)CommentCacheTests:3 项针对列注释缓存的测试 —— 空结果必须被记住(否则每次查询都要重新付出
那笔慢调用)、超出上限时丢的是最久没用过的那条、没读过的表不能假装知道MetadataMetricsTests:5 项针对元数据计时的测试 —— 按调用名汇总、按总耗时而不是次数排序
(一百次 1 毫秒的调用不该盖过一次 5 秒的)、失败与成功分开计、只记住最慢的 20 次且汇总不受上限影响- 示例规则真的在真库上跑过了:PostgreSQL 16 与 MariaDB 11 各起一个容器,建一张带注释、主键、
serial/auto_increment 与索引的表,把每个示例按占位符展开后丢进去。两个库里columns示例都带回了
主键与注释、IS_AUTOINCREMENT也都对(nextval/extra LIKE '%auto_increment%'),indexes示例
把NON_UNIQUE的 0/1 与index_type都带了回来 —— 这些是单元测试没法证明的部分 - 新增一项测试把示例与读取端钉在一起:列/索引示例里每个
AS 别名都必须是读取端真正会读的名字
(直接拿COLUMN_ROW_COLUMNS/INDEX_ROW_COLUMNS对照)。服务器能告诉你语句跑得通,但告诉不了你
插件读不读这个别名 —— 一个写成IS_PK的规则会安静地少一个主键图标。表规则不参与这项检查:
那里的额外列是特性(EST_ROWS会被保留并显示)
v0.2.0
修复
-- @connection指令不生效,且每次绑定都会多插一行。扩展写入的是
-- @connection: 名称(带冒号),而解析只认不带冒号的写法,于是指令永远解析不到 ——
每次执行都会再问一次连接,每次回答都再插一行。两种写法现在都认,写入与解析放在同一处,
并用往返测试钉住- 「Run again」重跑的是该面板第一次执行的 SQL。复用的面板没有更新回调,重跑处理器停留在
面板创建时捕获的那条语句 —— 新建查询文件时就是模板里的SELECT 1 - 从查询历史插入/删除完全没反应。两个命令都用
asNode()收窄参数,而历史条目没有
kind字段,于是拿到undefined后静默返回 - 导出结果时报「结果已不可用」。桥在连接关闭或磁盘预算用尽后会丢弃缓存结果,而面板能活得
比它久。现在遇到这种情况会重跑语句再导出,而不是丢一个死胡同给用户 - 保存脚本后执行按钮消失。新建查询是 untitled 文档,存成非
.sql文件名会被重新判定语言,
editorLangId == sql条件随之失效,连带快捷键和 SQL 补全一起消失。菜单改用扩展自己的上下文
键(同时认带指令的文档),保存后若语言退化为纯文本则恢复为 SQL - 结果网格滚动时不断重绘导致难以滚动。每帧重建 tbody 会改变表格高度,可能再触发滚动事件,
渲染自己喂自己。现在记住已渲染的窗口,窗口未变就不重建;表头高度改为重建时测量一次,
行位置不再偏移 - 连接模板的用户覆盖真正生效了。文件里一直写着"可以把副本放到
<globalStorage>/templates/connection-templates.json,按id合并",但代码从未读过那个路径 ——
是"打算这么做"被当成"已经这么做"写进了文档。现在实现了,并支持用disabled移除内置条目
新增
- 取 DDL 的 SQL 可以自己写(
ddl.queries)。很多数据库本来就能直接给出建表语句,而且比"从 JDBC
元数据重建"更准 —— 重建看不到存储子句、表空间、引擎选项,也看不到驱动报成OTHER的类型。规则按连接
URL 的 glob 匹配,第一条命中生效;没有命中才回到内置重建,所以默认行为不变。示例:
DESC ${qualified}(Hive)、SELECT pg_get_tabledef('${qualified}')(PostgreSQL)、
SHOW CREATE TABLE ${quotedQualified}(MySQL)。结果按数据库返回的样子展示:单单元格开成文本,
否则开结果网格 - 占位符显式区分原始名与加引号名。这是必须的而不是洁癖:
pg_get_tabledef('${quotedQualified}')
会去找一张名字里带引号的表 —— 不报语法错,只是找不到。现在${qualified}是原始名(用于字符串
字面量、Hive 的DESC),${quotedQualified}才加引号。这个改动同时作用于自定义动作 - 自定义 SQL 动作(
actions设置):在表、视图、列上按自己的模板跑 SQL。填不上的占位符会阻止
执行而不是变成空串(LIKE '%%'那种静默错配比直接拒绝危险得多);语句在已绑定连接的编辑器里打开,
而不是静默执行 - 连接改用单页表单,带「测试联通」按钮。必须测试通过才能保存:存一个连不上的连接,问题要到
第一次查询时才暴露。测试走桥的独立探测通道(开完即关,不进连接池),所以测一个已经连着的连接
不会干扰它。字段里的 JDBC URL 会随驱动选择自动预填(仅在为空时,不覆盖已填内容) - 内置 DDL 生成器的风格选项:
ddl.ifNotExists、ddl.indent、ddl.includeIndexes、
ddl.quoteIdentifiers。有规则匹配时这些不生效 query.maxRows:可配置的取数行数上限(0表示不限),命中上限会标记为截断- 表树筛选:按名称过滤表、视图、列、索引,生效时视图描述显示
filter: 关键字 result.openIn:结果面板位置,默认below(上下分屏)- 每条 SQL 上方的 Run 按钮(code lens),可用
query.codeLens关闭 - 脚本参数变量:
${V_DATE}形式的占位符,打开脚本时下方自动弹出参数面板,
执行前替换;变量样式可用variables.pattern自定义正则。有变量未填值则拒绝执行并指出是哪个
v0.1.1
扩展的行为与 0.1.0 完全相同。 这一版只修正了随包发布的文档,并让构建产物可复现。
如果你已经在用 0.1.0,从功能角度看没有升级的必要。
发这一版的原因是:0.1.0 的 VSIX 是从 tag 所在提交构建的,而下面这些文档修正发生在那之后,
所以 v0.1.0 里那份 README.md 是旧的。
修复
- README 列出的连接模板与实际不符:删去并不存在的 ClickHouse,补上遗漏的 MariaDB 与 GaussDB
- README 把命令面板的调用路径写成
Open DB Client: ...,而命令实际贡献在DB Client分类下,
用户照着输是找不到的 - README 里本地打包的产物名漏了版本号
新增(仅开发,不影响扩展行为)
scripts/check-docs.mjs: 校验 README 中的设置名、命令面板标签、快捷键、视图、链接锚点与
CHANGELOG 小节是否与package.json一致,并接入npm test。这类错误不会让构建失败,
但用户一用就会撞上- 打包
bridge.jar时不再用构建时刻作为 ZIP 条目时间戳,改用提交时间(CI 经
SOURCE_DATE_EPOCH传入)。这样时间戳不再是变量,同一个 JDK 下重建即可得到相同的 jar
一致性说明
下面是直接下载两个 Release 的 VSIX 逐项比对得到的结论,不是估计:
| 文件 | 与 0.1.0 的关系 |
|---|---|
out/extension.js |
逐字节相同 |
media/result/main.js |
逐字节相同 |
resources/bridge.jar |
83 个条目内容完全相同;仅 ZIP 条目时间戳不同(0.1.0 未固定时间戳) |
README.md |
有差异 —— 这正是本版存在的理由 |
关于 jar 的可复现性:它以同一个 JDK 为前提。用不同版本的 javac 编译,生成的字节码并不保证
一致 —— 实测用 JDK 21 重建 0.1.1,时间戳可以完全对上,但类文件字节与 Created-By 行都不同。
v0.1.0
首个版本。
新增
连接
- 通过用户自备的 JDBC 驱动连接任意数据库,不绑定数据库类型
- 驱动获取:从 Maven Central 按坐标下载 / 添加本地 jar / 指向已有目录
- 驱动发现读取 jar 内的
META-INF/services/java.sql.Driver,也支持手动指定驱动类名 - 连接管理:新建、编辑、复制、删除、测试、连接、断开;连接池可在重新连接时复用
- 密码存入 VS Code
SecretStorage - 连接模板(MySQL / PostgreSQL / openGauss / Oracle / SQL Server / ClickHouse / Hive /
SQLite / Transwarp / Custom),只预填 URL 前缀与驱动类名,不含方言逻辑 - 通过状态栏或
-- @connection <名称>注释指令把 SQL 文件绑定到连接 - 数据库树:目录 → schema → 表/视图 → 列/索引,单层结构自动折叠
查询
- 执行选中内容、光标处语句或整个文件;支持一次执行多条语句
- 破坏性语句执行前二次确认
- 长查询可取消(调用
Statement.cancel()真正下发到数据库) - 结果集不经过进程间通信:桥直接落盘,界面按 200 行一页读取,支持虚拟滚动
- 值转换:超大整数与
BigDecimal转字符串避免精度丢失,超长文本截断,二进制显示为占位符 - 磁盘结果缓存按 LRU 淘汰,默认上限 512 MiB
- 查询历史,保留最近 500 条,可插回编辑器
SQL 智能补全
- 表名补全与列名补全,支持别名/表名限定
INSERT INTO t (处补列而非补表- 关键字与代码片段补全
- 补全在扩展进程内计算,不消耗 IPC 往返;元数据按需加载并 LRU 缓存
导出
- 四种格式:CSV(CRLF + 可选 UTF-8 BOM)、JSON、Excel
.xlsx、INSERT语句 - 可从查询结果导出,也可不查询直接导出整张表
- xlsx 手写 OOXML,支持大表自动拆多 sheet,不引入 Apache POI
- 导出不设行数上限;写失败时删除半成品文件
表结构
- 查看列(类型、长度、可空、默认值、注释)与索引(是否唯一)
- 根据元数据生成
CREATE TABLE语句
健康监控
- 状态栏实时显示连接数与堆占用
- Markdown 健康报告:JVM 堆/线程/运行时长、每个连接的池借还情况、运行中查询数、
结果缓存占用与淘汰数、已加载驱动 - 指标仅来自 JDK JMX,不依赖任何第三方库,也不采集数据库服务端指标
说明
- Java 桥零外部依赖,由
javac+jar直接构建(无需 Maven/Gradle),产物约 144 KiB - 不实现任何数据库方言;跨库行为差异通过 JDBC 能力探测(
DatabaseMetaData/
supportsXxx())处理 - 代码中不含任何驱动 jar
已知限制
- SQL 补全不做子查询与 CTE 作用域
- 结果网格只读,不能在网格内编辑数据
- 不支持 SSH 隧道
- 健康监控不含数据库服务端指标