Skip to content

Commit bf35328

Browse files
committed
fix(cross-platform): 防止 PostgreSQL 時間型別無聲取整
1 parent 10167b3 commit bf35328

8 files changed

Lines changed: 446 additions & 12 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ SQL Server `sysname` 與 `CREATE TYPE ... FROM ...` alias type 會同時顯示
8686

8787
PostgreSQL domain 也會同時顯示 schema domain 名稱與 base definition,例如 `"public"."precise_amount" (numeric(18,6))`;integer、文字、exact numeric、enum/自訂型別、網路位址等 domain 會沿用 base type 的安全編輯器與參數,再由 PostgreSQL 套用 domain constraint。constraint 拒絕時會回復整筆交易。
8888

89+
PostgreSQL `timestamp(p)`、`timestamptz(p)`、`time(p)` 與 `timetz(p)` 會依欄位宣告保留 0–6 位小數秒;無時區型別禁止混入 offset,timestamptz 必須明確指定 offset 或 `Z`,載入固定顯示為 UTC `Z`。超出 p 的小數秒會在送出前拒絕,`time` 則保留 PostgreSQL 支援的 `24:00:00` 上界。
90+
8991
### Windows 完整版
9092

9193
- Windows
@@ -209,7 +211,7 @@ SQLite / PostgreSQL / SQL Server database rename 實機矩陣(需先啟動 Doc
209211

210212
## 已知限制
211213

212-
- Linux / macOS 目前是跨平台預覽版,涵蓋 MySQL / MariaDB、PostgreSQL、SQL Server、SQLite 的連線、metadata、SQL 工作流程、CSV / TSV / JSON 結果匯出、Primary Key 穩定分頁,以及常用 scalar、無損高精度 DECIMAL/NUMERIC、MySQL/MariaDB BIT/ENUM/SET/完整範圍 TIME/YEAR/DATE/DATETIME/TIMESTAMP/UUID/INET6、PostgreSQL bit string/timetz/interval/pg_lsn/oid/xid/cid/xid8/tsvector/tsquery/range/multirange/array/geometric/jsonpath/snapshot/hstore/ltree/reg*/enum/composite/extension UDT/domain、1 MiB 內 binary、JSON、XML、PostgreSQL 網路位址,以及 SQL Server scalar temporal/legacy LOB/hierarchyid/alias type/sql_variant 欄位的 Table 資料編輯;Oracle、MongoDB、Redis、Snowflake、其餘進階型別、模型與 AI 等功能仍需從 Windows 完整版遷移。Linux 缺少 `secret-tool`/Secret Service、macOS Keychain 不可用,或使用者未勾選保存時,程式重開後仍需重新輸入密碼;Linux 已可安全下載、關閉、套用、健康檢查與 rollback,macOS 則在正式 Developer ID 簽署與 Apple notarization 憑證完成前維持驗證後手動安裝。
214+
- Linux / macOS 目前是跨平台預覽版,涵蓋 MySQL / MariaDB、PostgreSQL、SQL Server、SQLite 的連線、metadata、SQL 工作流程、CSV / TSV / JSON 結果匯出、Primary Key 穩定分頁,以及常用 scalar、無損高精度 DECIMAL/NUMERIC、MySQL/MariaDB BIT/ENUM/SET/完整範圍 TIME/YEAR/DATE/DATETIME/TIMESTAMP/UUID/INET6、PostgreSQL scalar temporal/bit string/timetz/interval/pg_lsn/oid/xid/cid/xid8/tsvector/tsquery/range/multirange/array/geometric/jsonpath/snapshot/hstore/ltree/reg*/enum/composite/extension UDT/domain、1 MiB 內 binary、JSON、XML、PostgreSQL 網路位址,以及 SQL Server scalar temporal/legacy LOB/hierarchyid/alias type/sql_variant 欄位的 Table 資料編輯;Oracle、MongoDB、Redis、Snowflake、其餘進階型別、模型與 AI 等功能仍需從 Windows 完整版遷移。Linux 缺少 `secret-tool`/Secret Service、macOS Keychain 不可用,或使用者未勾選保存時,程式重開後仍需重新輸入密碼;Linux 已可安全下載、關閉、套用、健康檢查與 rollback,macOS 則在正式 Developer ID 簽署與 Apple notarization 憑證完成前維持驗證後手動安裝。
213215
- Oracle 的部分 DDL 還是會被權限、語法或物件型態擋下來;預覽會附上權限診斷 SQL 跟修復建議,但終究要看帳號實際有什麼權限。
214216
- Windows 完整版對沒有 Primary Key 的資料表仍可用原始值組 WHERE 條件;欄位有浮點數或大文字時可能比不準,可在選項中改成唯讀。Linux / macOS 預覽版會直接停用無 Primary Key Table 的修改與刪除,只保留新增與瀏覽。
215217
- XLSX 匯出要把整份結果放進記憶體;還原 SQL 備份也是整個檔一次讀進來,特別大的備份要留意。

‎docs/FEATURE_NOTES.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- 安全資料編輯:新增、修改與刪除只接受 schema metadata 中存在且可編輯的欄位,所有值使用 provider 參數,不把輸入拼入 SQL。修改/刪除要求 Primary Key,WHERE 同時比對 PK 與載入時的可編輯原始值;影響 0 列視為外部變更衝突,超過 1 列也會回復交易,避免靜默覆蓋或批次誤改。MySQL/MariaDB BLOB、PostgreSQL bytea、SQL Server binary/varbinary/image 與 SQLite BLOB 接受 `0x` 加偶數個 hex 字元並以原生 binary 參數寫入,上限 1 MiB;MySQL/MariaDB BIT(1–64) 接受非負十進位整數,會依欄位寬度拒絕溢位並以 MySqlDbType.Bit 寫入;ENUM/SET 會保留 COLUMN_TYPE 宣告並使用 MySqlDbType.Enum/Set,SET 由 server 依宣告順序正規化,strict mode 拒絕未宣告值時回復交易;TIME 使用專用值型別,不受 `TimeSpan.Parse` 的 24 小時格式限制,可依 TIME(0–6) 精度解析正負 `HHH:mm:ss.ffffff`,上限為 ±838:59:59 且以 MySqlDbType.Time 寫入,載入則 cast text 保留 MySQL 原生顯示。PostgreSQL bit(n)/bit varying(n) 使用純 0/1 字串,metadata 保留欄位長度並驗證精確/最大 bit 數,再轉成 Npgsql BitArray 參數;載入時明確 cast text 以保留前導零。PostgreSQL time with time zone 使用 `HH:mm:ss.ffffff±HH:mm`,要求明確 offset 並以 Npgsql TimeTz 寫入;載入時 cast text,原值 predicate 再嚴格轉回 DateTimeOffset。PostgreSQL interval 使用 `months=<整數>;days=<整數>;microseconds=<整數>` 保留原生三分量並以 NpgsqlInterval 寫入;載入與原值 predicate 也按三分量正規化,不用 `TimeSpan` 近似月數,且不會把 `1 month` 與 `30 days` 視為同一原值。PostgreSQL pg_lsn 要求 `XXXXXXXX/XXXXXXXX` 格式,斜線兩側各限制為 32-bit 十六進位值並正規化為大寫,再以 NpgsqlLogSequenceNumber/PgLsn 原生參數寫入。PostgreSQL oid/xid/cid 使用 UInt32,xid8 使用 UInt64,並繞過 Npgsql 不支援的通用 DbType.UInt64、直接指定各自原生型別,避免高位值被有號整數截斷。PostgreSQL tsvector/tsquery 禁止 NUL 且限制 1 MiB;因 Npgsql 8 client parser 已標示為不可靠且 obsolete,寫入與 optimistic 原值會以 Unknown 參數讓 PostgreSQL 在型別上下文中權威解析,仍維持 SQL 與值分離,server 拒絕畸形內容時整筆交易回復。MySQL/MariaDB JSON、PostgreSQL json/jsonb 與 SQLite JSON 會先經嚴格 JSON parser 驗證,再以 provider 專用參數寫入,並針對 MySQL 儲存正規化與 PostgreSQL json 無原生等號的差異使用正確原值 predicate。PostgreSQL xml、SQL Server xml 與 SQLite XML 會禁止 DTD/外部實體並限制文件深度,原生 provider 另使用 XML 參數與可比較的序列化原值 predicate。PostgreSQL inet/cidr/macaddr/macaddr8 會驗證 address family、prefix、CIDR host bits、zone identifier 與 MAC byte 長度,再轉成 Npgsql 原生型別;載入時由 PostgreSQL 轉成文字以保留 inet prefix。SQL Server text/ntext/image 使用對應的 SqlDbType,optimistic predicate 則轉成 varchar(max)/nvarchar(max)/varbinary(max) 後逐 byte 比對,避開 legacy LOB 不可直接使用等號的限制。超過 1 MiB 的既有 binary/JSON/XML/bit string/全文檢索值只顯示摘要,且不放入非 PK optimistic predicate。無 Primary Key 的 Table 只允許新增與瀏覽,generated 及尚未支援的進階型別維持唯讀。
1414
- PostgreSQL 文字序列化型別:`jsonpath`、`pg_snapshot`/`txid_snapshot`、`hstore`、`ltree`/`lquery`/`ltxtquery`、11 種 `reg*` 物件參照,以及任意 enum/composite/extension UDT 改以最多 1 MiB 的純文字編輯;參數保持 Unknown,讓欄位型別上下文交由 PostgreSQL 權威 parser 驗證。載入一律轉 canonical text;沒有原生等號的型別以載入時文字做 optimistic predicate,仍可偵測其他連線改動。PostgreSQL 16 實機矩陣涵蓋 22 個代表型別的 metadata、新增、修改、畸形 jsonpath/composite rollback、衝突與刪除,其中自訂型別直接覆蓋 enum、named composite、`cube` extension 與 enum domain;Linux X11 另從 UI 修改 jsonpath/ltree,以及 enum/named composite/`cube` UDT 後直接查庫核對。
1515
- PostgreSQL domain:metadata 分開保存 schema-qualified domain 與 base storage type,例如 `"public"."short_label" (character varying(30))`;integer、varchar、numeric、enum/UDT、CIDR 等 domain 沿用 base type 的解析、參數與 optimistic predicate,並保留 domain constraint 權威驗證。PostgreSQL 16 實機矩陣涵蓋五種 base type 的 metadata、CRUD、constraint rollback、衝突與刪除;Linux X11 另從 UI 修改全部五種 domain 後直接查庫核對。
16+
- PostgreSQL scalar temporal:`timestamp(p) without time zone`、`timestamp(p) with time zone`、`time(p) without time zone` 與既有 `time(p) with time zone` 會依 format_type metadata 保留 0–6 位小數秒精度並使用對應的 Npgsql 原生參數。無時區 timestamp 禁止 offset;timestamptz 必須包含明確 offset 或 Z,載入固定正規化為 UTC `Z`,不受 session timezone 影響;time 保留 PostgreSQL 合法的 `24:00:00` 上界。超過欄位 p 的值會在送出前拒絕,避免 server 取整。PostgreSQL 16 實機矩陣涵蓋 metadata、0001/9999 年、毫秒/微秒、offset 轉 UTC、24:00、錯誤輸入不落資料、CRUD 與 optimistic conflict;Linux X11 另驗證 `timestamp(3)` 第 4 位小數被 UI 拒絕,再修改四欄並以 `psql` 逐欄查回。
1617
- Spatial 型別:MySQL/MariaDB 的 8 種 OGC spatial 與 SQL Server geometry/geography 使用共同的 `SRID=<非負整數>;<WKT>` 編輯格式;provider 載入 canonical WKT、顯式重建原生物件,optimistic predicate 同時比對 SRID 與 WKT。MariaDB 對畸形 WKT 只回 warning/NULL 的差異已 fail-closed 包裝為錯誤,nullable 欄位不會被靜默清空。MySQL 8、MariaDB 11.4、SQL Server 2022 實機矩陣涵蓋 NULL、metadata、全部型別 CRUD、SRID 保存、畸形 WKT rollback、衝突與刪除;Linux X11 另從 MariaDB UI 修改 geometry/point 後直接查庫核對。
1718
- MariaDB 原生 UUID/INET6:metadata 分別映射為 Guid 與 NetworkAddress 編輯器,載入會明確 cast text,UUID 正規化為小寫 D 格式,INET6 接受 IPv4/IPv6 並將 IPv4 保存為 mapped IPv6。寫入與原值 predicate 使用文字參數及 `CAST(... AS UUID/INET6)`,錯誤 UUID、CIDR prefix 與 IPv6 zone identifier 在送出前 fail closed。MariaDB 11.4 實機矩陣涵蓋 metadata、IPv4/IPv6 canonical 化、CRUD、錯誤輸入不落資料與 optimistic conflict;CI 會與 MySQL 8、PostgreSQL 16、SQL Server 2022 同時啟動驗證。Linux X11 另從實際 Table 編輯器修改 UUID 與 IPv4,畫面重新載入 canonical UUID/mapped IPv6 後再直接查庫與底層 HEX 核對。
1819
- MySQL/MariaDB DATE/DATETIME/TIMESTAMP:metadata 會保留 `DATETIME(p)`/`TIMESTAMP(p)` 的 0–6 位小數秒精度,載入以固定長度 canonical ISO 文字顯示,寫入則使用 Date/DateTime/Timestamp 原生參數。DATE/DATETIME 會先檢查 1000–9999 年範圍;三者都拒絕寬鬆日期格式、offset 與超過欄位 fsp 的小數秒,避免 server 預設在沒有 warning 的情況下四捨五入。MySQL 8 與 MariaDB 11.4 實機矩陣涵蓋四個代表欄位、metadata、1000/9999 年、毫秒/微秒、錯誤輸入不落資料、CRUD 與 optimistic conflict;Linux X11 另驗證 `DATETIME(3)` 第 4 位小數會在 UI 被拒絕,再從 Table 編輯器修改 DATE、兩種 DATETIME 與 TIMESTAMP,重新載入後用原生 client 逐欄查回核對。

‎docs/ROADMAP.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
| SSH tunnel、SSL/TLS | ✅ | 四種網路 provider 已有共用安全設定 UI、憑證驗證、SSH SHA256 主機金鑰固定與隧道生命週期;SQLite 為本機檔案,不適用網路層設定。 |
6666
| PAM/LDAP/Kerberos/MFA/SSO | 📋 | 依 provider 驗證能力分階段加入,不保存明文祕密。 |
6767
| 深色模式/平台原生設計 | ✅ | Windows 原生 WinForms、淺/深色主題與 DPI 向量圖示已完成。 |
68-
| 跨平台授權/Windows、macOS、Linux | 🟡 | 商業授權本身不適用開源專案;Avalonia 跨平台版已可使用 MySQL / MariaDB、PostgreSQL、SQL Server、SQLite 的連線、metadata、SQL 工作流程、CSV / TSV / JSON 結果匯出、Primary Key 穩定分頁,以及常用 scalar、MySQL/MariaDB BIT/ENUM/SET/完整範圍 TIME/DATE/DATETIME/TIMESTAMP/UUID/INET6、PostgreSQL bit string/timetz/interval/pg_lsn/oid/xid/cid/xid8/tsvector/tsquery、1 MiB 內 binary/JSON/XML、PostgreSQL 網路位址與 SQL Server scalar temporal/legacy LOB 欄位的安全 Table 資料編輯,並可選擇以 Linux Secret Service 或 macOS Keychain 保存密碼。Linux x64/ARM64 使用 self-contained tar 安裝包,macOS Intel/Apple Silicon 使用 `.app.zip`,並可依 RID 安全檢查、下載及驗證最新 Release;Linux 已完成交易式套用、啟動健康檢查與 rollback,待補 macOS Developer ID/notarization、自動套用、其餘 provider 與進階功能。 |
68+
| 跨平台授權/Windows、macOS、Linux | 🟡 | 商業授權本身不適用開源專案;Avalonia 跨平台版已可使用 MySQL / MariaDB、PostgreSQL、SQL Server、SQLite 的連線、metadata、SQL 工作流程、CSV / TSV / JSON 結果匯出、Primary Key 穩定分頁,以及常用 scalar、MySQL/MariaDB BIT/ENUM/SET/完整範圍 TIME/DATE/DATETIME/TIMESTAMP/UUID/INET6、PostgreSQL scalar temporal/bit string/timetz/interval/pg_lsn/oid/xid/cid/xid8/tsvector/tsquery、1 MiB 內 binary/JSON/XML、PostgreSQL 網路位址與 SQL Server scalar temporal/legacy LOB 欄位的安全 Table 資料編輯,並可選擇以 Linux Secret Service 或 macOS Keychain 保存密碼。Linux x64/ARM64 使用 self-contained tar 安裝包,macOS Intel/Apple Silicon 使用 `.app.zip`,並可依 RID 安全檢查、下載及驗證最新 Release;Linux 已完成交易式套用、啟動健康檢查與 rollback,待補 macOS Developer ID/notarization、自動套用、其餘 provider 與進階功能。 |
6969

7070
## Provider 與服務覆蓋
7171

@@ -87,4 +87,4 @@
8787
4. ✅ RDBMS、MongoDB 與 Redis 的連線 URI 匯入及設定頁確認流程已完成。
8888
5. ✅ 連線星號、持久化色彩與批次屬性操作已完成。
8989
6. 🟡 MongoDB 第一~三期、Redis/Garnet 第三期(string+集合型別安全編輯,含實機矩陣)、Snowflake 第二期(SQL REST API 查詢+查詢編輯器 DML/DDL)已完成。下一步候選:Snowflake 實機驗收、key-pair JWT、參數綁定與網格寫回,MongoDB Atlas/SRV 驗證矩陣與 Aggregation Pipeline,Redis Cluster/Sentinel/Pub/Sub,或回頭補模型/BI 路線。
90-
7. 🟡 Linux / macOS 跨平台第二階段進行中:獨立 Core、Avalonia UI、四種 RDBMS workflow、系統密碼庫、結果安全匯出、Table optimistic concurrency 編輯、MySQL/MariaDB BIT/ENUM/SET/完整範圍 TIME/YEAR/DATE/DATETIME/TIMESTAMP/8 種 OGC spatial、MariaDB UUID/INET6、三種 provider 的無損高精度 DECIMAL/NUMERIC、PostgreSQL bit string/timetz/interval/pg_lsn/oid/xid/cid/xid8/tsvector/tsquery/range/multirange/array/geometric/jsonpath/snapshot/hstore/ltree/reg*/enum/composite/extension UDT/domain、SQL Server geometry/geography/hierarchyid/alias type/sql_variant/scalar temporal、1 MiB 內 binary hex/JSON/XML、PostgreSQL 網路位址、SQL Server legacy LOB 編輯與 200 列穩定分頁、四架構 self-contained CI/Release 資產,以及依 RID 與 sidecar 完成串流 SHA-256 的安全更新下載已完成;Linux 安裝/啟動/解除安裝納入 Xvfb smoke,並已支援交易式套用、啟動健康檢查、失敗 rollback 與舊版重啟。下一步是其餘可由實機矩陣驗證的進階型別;macOS Developer ID/notarization、自動套用及 ARM64 實機啟動則等待對應憑證與硬體。
90+
7. 🟡 Linux / macOS 跨平台第二階段進行中:獨立 Core、Avalonia UI、四種 RDBMS workflow、系統密碼庫、結果安全匯出、Table optimistic concurrency 編輯、MySQL/MariaDB BIT/ENUM/SET/完整範圍 TIME/YEAR/DATE/DATETIME/TIMESTAMP/8 種 OGC spatial、MariaDB UUID/INET6、三種 provider 的無損高精度 DECIMAL/NUMERIC、PostgreSQL scalar temporal/bit string/timetz/interval/pg_lsn/oid/xid/cid/xid8/tsvector/tsquery/range/multirange/array/geometric/jsonpath/snapshot/hstore/ltree/reg*/enum/composite/extension UDT/domain、SQL Server geometry/geography/hierarchyid/alias type/sql_variant/scalar temporal、1 MiB 內 binary hex/JSON/XML、PostgreSQL 網路位址、SQL Server legacy LOB 編輯與 200 列穩定分頁、四架構 self-contained CI/Release 資產,以及依 RID 與 sidecar 完成串流 SHA-256 的安全更新下載已完成;Linux 安裝/啟動/解除安裝納入 Xvfb smoke,並已支援交易式套用、啟動健康檢查、失敗 rollback 與舊版重啟。下一步是其餘可由實機矩陣驗證的進階型別;macOS Developer ID/notarization、自動套用及 ARM64 實機啟動則等待對應憑證與硬體。

‎mySQLPunk.Core/Models/TableDataModels.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public enum TableColumnValueKind
1616
MySqlTemporal,
1717
MySqlTime,
1818
MySqlYear,
19+
PostgreSqlTemporal,
1920
TimeWithTimeZone,
2021
Interval,
2122
LogSequenceNumber,

0 commit comments

Comments
 (0)