Releases: FiveTechSoft/OpenADS
v1.5.2
Full Changelog: v1.5.1...v1.5.2
v1.5.1
Full Changelog: v1.5.0...v1.5.1
v1.5.0
v1.5.0 — x86 fix, CDX/AOF correctness, complete API docs
Bug Fixes (credits: Jonsson / RusSoft Ltda.)
- CDX tag order: list_tags() sorts by creation order, not alphabetical
- CDX expression-index key size: fixed truncation causing rows out of order on large tables
- AdsSetAOF: returns AE_INVALID_EXPRESSION for non-optimisable filters (fixes silent SET FILTER disable)
- x86 calling convention: __stdcall (WINAPI) on Win32 fixes stack corruption
- Conditional FOR on logical fields: treat as numeric 0/1 (PR #121)
- INDEX ON corruption: prevent source table index corruption (PR #118)
- ODBC driver x86: C4100/C2733 fixes
New Features
- Server-side aggregation (COUNT/SUM/AVG/MIN/MAX)
- FetchWhere V2 with cached forward scan
- ODBC driver with scrollable cursors, typed access, app-lock emulation
- Native write path for PostgreSQL/MariaDB/Firebird
- SQL push-down for SET FILTER/AOF
- Complete API docs: all 364 ACE functions in Portuguese
Testing
- x64: 872/872 passed
- x86: 874/874 passed
- New tests: CDX tag order, expression-index scale, multi-tag navigation
Full changelog: v1.4.0...v1.5.0
What's Changed
- feat(serverd): read settings from an openads.ini via --config by @Admnwk in #88
- feat(serverd): interactive --setup wizard (writes openads.ini, optional service) by @Admnwk in #89
- docs: client onboarding kit — Studio launcher, quick start, migration guide by @Admnwk in #90
- build: install() rules + CPack for reproducible packages by @Admnwk in #91
- packaging: Windows GUI installer (Inno Setup) by @Admnwk in #94
- fix(cdx): rewrite stored key expression when re-creating a tag (re-filter on another column) by @Admnwk in #93
- cookbook(orm): fast-queries DBF example (index-ordered nav + AOF) + vendored ORM refresh by @Admnwk in #95
- packaging(installer): make openads-setup.iss compile (brace comment + deprecated arch) by @Admnwk in #96
- fix(abi): give server_type_mask C++ linkage (fixes clang -Werror CI break) by @Admnwk in #99
- fix(abi): serialize native AdsCreateIndex61 (concurrent-create heap corruption) + daily-ops soak harness by @Admnwk in #97
- fix(abi): AdsGetRecordCount on a conditional ORDER handle counts FOR matches (#87) by @Admnwk in #100
- feat: route non-AOF SET FILTER bulk scans through server-side FetchWhere (Tier-2) by @Admnwk in #101
- feat: FetchWhere V2 — serve forward filter scan from cache (no goto per match) by @Admnwk in #102
- feat: push Clipper SET FILTER/AOF down to SQL backends (SQLite) by @Admnwk in #107
- feat(abi,odbc): system.primarykeys catalog + ODBC SQLPrimaryKeys by @Admnwk in #106
- feat: expand xBase->SQL push-down coverage ($ contains, LEFT) by @Admnwk in #108
- SQL backends: native write (dbAppend/REPLACE/dbDelete) + rLock()/fLock() for FB/PG/MariaDB/ODBC (#103 slices 1-4, supersedes #104) by @Admnwk in #109
- abi(firebird): wire backend write (dbAppend/REPLACE/dbDelete) into the navigational ABI (#103 slice 1) by @Admnwk in #104
- feat: extend SET FILTER/AOF push-down to PostgreSQL by @Admnwk in #110
- feat(odbc): typed SQLGetData, scrollable cursor, positional parameters by @Admnwk in #105
- fix(adi): create non-structural .adi bag on the correct ADT table by @Admnwk in #112
- Tier-3: server-side aggregation push-down (AdsAggregate COUNT/SUM/AVG/MIN/MAX) by @Admnwk in #113
- fix(adi): reopen a non-structural .adi bag on the correct ADT table by @Admnwk in #114
- ODBC driver: Driver-Manager / ADO conformance (descriptor handles, SQLBindCol, BIT, ODBC_VER) by @Admnwk in #117
- fix(index): prevent INDEX ON from corrupting source table indexes by @Admnwk in #118
- fix(aggregate): validate Tier-3 spec field names (reject unknown/empty, close SQL-injection surface) by @Admnwk in #116
- ODBC: emulate rLock()/fLock() via SQL Server sp_getapplock (completes #109) by @Admnwk in #111
- fix(abi): AdsGetField crash on SQL backends when reading by field ordinal (ADSFIELD) by @Admnwk in #115
- fix: unbreak build (AE_PARSE_ERROR) + ODBC driver x86 (C4100/C2733) by @Admnwk in #119
- ci: add msvc-x86 (32-bit) build leg by @Admnwk in #120
- fix(index): conditional FOR ignored a logical-field condition by @Admnwk in #121
Full Changelog: v1.4.0...v1.5.0
v1.4.0 — ADS dialect, CDX engine, wire FetchWhere, enterprise pool, SQL backends
1.4.0
ADS Dialect Compatibility (Harbour/FiveWin ERP)
- N-way comma join (3+ tables) with left-deep hash-join on composite keys + filter pushdown.
<alias>.*wildcard projection,UPPER(col)in WHERE (case-insensitive map),FROM t AS abase-table alias,[file.dat]free-table brackets,WHERE 1 = 1constant folding, ODBC temporal literals ({d ...}/{ts ...}/{t ...}).
CDX Index Engine
- Bulk-load builder (
build_bulk) — bottom-up B+tree, ~10× fasterCREATE INDEX. - O(1) browse-position cache for
AdsGetRelKeyPos/AdsGetKeyNum. - Conditional (FOR) index predicates — persist in the sub-tag header + apply at insert; full reopen round-trip.
- Read-only flush-skip, FOR-clause hardening, NTX empty-rooted-leaf PACK/reindex (5004), composite key width no longer pinned to the 254-byte probe (follow-up to #68).
- CDX index direction fix (v1.3.0): Harbour rddads / X# put compound vs descending option bits on swapped positions; descending now decoded only when both
0x02and0x08are set, so Harbour/FiveWin indexes no longer build reversed.
Wire Protocol
- Server-side filtered scan (
FetchWhere, opcode0xA4) — client sends a Clipper-style FOR predicate, server returns only matching rows.
Enterprise Server
- Sharded-reactor connection pool (
WorkerPool) behindOPENADS_SERVER_POOL(default OFF),FrameReadernon-blocking partial-frame buffering,Sessionextracted fromserver.cpp. EnterpriseConfigenv tunables; session reaping + max-sessions cap; deadlock-freestop().
SQL Backends
- PostgreSQL column metadata (
IS_NULLABLE/COLUMN_DEFAULT) via information schema. - stmt_map serialisation (concurrency), SQLite busy-timeout + WAL, CREATE TABLE honours statement table type.
ABI
- Connection/handle introspection: real
AdsGetConnectionType(remote/local),AdsGetHandleType(by handle kind),AdsGetIndexCondition/AdsGetIndexFilename.
ADT / Build / Tooling
- ADT companion-stream count fix;
data_dict.cppstrict-warning cleanups; xBase++ smoke test; FiveWin ORM cookbook; concurrency tests.
Full CI green (msvc-x64, ninja-clang, ninja-clang/TLS, macos, PHP). Verified locally + over the wire against a live openads_serverd.
What's Changed
- fix(abi): serialise stmt_map() so concurrent SQL statements don't corrupt it by @Admnwk in #82
- cookbook(orm): build the track against hb_orm2 + a FiveWin grid fed by the ORM by @Admnwk in #70
- test(cdx): empty-table bare CHAR key width + doc sync v1.2.2 by @Admnwk in #71
- feat(serverd): bound session threads (reaping + optional max-sessions cap) for 24x7 servers by @Admnwk in #73
- fix(cdx): don't pin composite index keys to the 254-byte probe width (follow-up to #68) by @Admnwk in #69
- fix(adt): write the ADT header companion-type count instead of a flat 1 by @Admnwk in #75
- feat(wire): server-side filtered scan (FetchWhere) for non-AOF predicates by @Admnwk in #80
- fix(ntx): handle empty-but-rooted leaf on insert (PACK/reindex 5004) by @Admnwk in #83
- fix(cdx): persist and apply conditional (FOR) index predicates by @Admnwk in #76
- feat(pg): expose column nullable/default via AdsDDGetFieldProperty by @Admnwk in #84
- feat(serverd): sharded-reactor connection pool behind OPENADS_SERVER_POOL (default off) by @Admnwk in #74
Full Changelog: v1.3.0...v1.4.0
v1.3.0 — CDX index direction fix for Harbour rddads / FiveWin
1.3.0
- CDX index direction fix for Harbour rddads (FiveWin).
AdsCreateIndex61decodeddescending = ulOptions & ADS_DESCENDING (0x08). Instrumenting the two RDD clients showed they place the compound/descending option bits on swapped positions: X#'s ADSRDD sends0x02for an ascending tag and0x0Afor descending, while Harbour'srddadssends0x08for ascending and0x0Afor descending. So a plain HarbourINDEX ON f TAG t(0x08) was read as descending and every Harbour/FiveWin index was built reversed —AdsGotoToplanded on the last key andSkipwalked backward, so aTBrowse/tDatabasegrid showed its rows upside-down (Seekstill worked, which masked it). Direction is now decoded as descending only when both0x02and0x08are set (0x0A); a lone0x02or0x08is that client's compound marker and is ascending. The SQLCREATE INDEXpath emits0x0Afor a descending tag so it round-trips through the same decode. Pinned byabi_cdx_index_direction_testandexamples/fivewin/tdata_index_test.prg. - Build fix: drop a dead
trim()indata_dict.cpp. An unreferenced static function tripped-WXC4505 on a clean MSVC build.
Includes everything from 1.2.x (CDX char key width, empty-leaf walk, leaf recno bits + prefix seek, MSSQL/NTX/ABI fixes).
Full Changelog: v1.2.3...v1.3.0
v1.2.3 — CDX char key width fix + clang build fix
1.2.3
- CDX character index key width fix (PR #68).
AdsCreateIndex61derived a character tag's fixed key width from the trimmed value of the first record. When the first row was short (e.g."ANA") and later rows shared a longer prefix ("ANABELA CARDOSO","ANABELA FERREIRA"), every later key was truncated to the first row's width and collapsed onto the same stored key, so distinct values became indistinguishable and a seek landed on the wrong record — both inside the index and for native FoxPro/Clipper readers of the bag. The key width now comes from the declared field length for a bare character field, falling back to the untrimmed first-record width for a composite expression, keeping the 32-char default only for an empty table. Numeric CDX/NTX key widths are unchanged. Pinned byabi_cdx_char_keylen_test. - Build fix:
<cstdint>insqlite_uri_test.std::uint8_twas used without including<cstdint>; clang/libc++ does not pull it in transitively, so theninja-clang-WerrorCI job failed while MSVC and AppleClang stayed green. Added the explicit include. - Full unit suite 739/739, 0 regression.
What's Changed
- fix(cdx): size a character index key from the field width, not the trimmed first record by @Admnwk in #68
Full Changelog: v1.2.2...v1.2.3
v1.2.2 — CDX/MSSQL/NTX/ABI fixes
Fixes
- CDX empty-leaf walk (PR #63) — forward/backward index walks skip empty leaves left by \erase(). Fixes REINDEX/bulk-delete ADSCDX/5000.
- CDX leaf recno bits + prefix seek (PR #62) — recno field sized from record count (not key length); partial seeks match on search-key length.
- MSSQL backward SKIP (PR #65) — off-by-one: skip onto row 0 reads it, not BOF.
- ABI typed getters for SQL backends (PR #66) — AdsGetDouble/Long/LongLong/String dispatch through backend ops vtable for PostgreSQL.
- NTX numeric key format (PR #67) — native DBFNTX form (zero-padded + complemented negatives).
Tests
738/738 unit tests passing (was 726). New edge-case tests:
- \�bi_ntx_numeric_edge_test.cpp\ — -0.0 normalisation, width/dec clamping, byte-complement, custom key
- \cdx_empty_tree_test.cpp\ — empty tree, all-erased tree, exact prefix, descending prefix
Docs
- Project history page (EN/ES/PT)
- Changelog + whatsnew updated for v1.2.2
What's Changed
- fix(ntx): store numeric index keys in the native DBFNTX form by @Admnwk in #67
- fix(mssql): backward SKIP onto the first row reads row 0, not BOF by @Admnwk in #65
- fix(abi): route typed getters + AdsGetIndexHandle through the per-backend ops vtable (PostgreSQL) by @Admnwk in #66
- fix(cdx): skip empty leaves on forward index walks (fixes REINDEX/bulk-delete ADSCDX/5000) by @Admnwk in #63
- fix(cdx): size leaf recno bits from record count; partial-key (prefix) seek by @Admnwk in #62
Full Changelog: v1.2.1...v1.2.2
v1.2.1
v1.2.1 — 2026-06-24
- Added unit tests: adm_memo, codepage, maria_uri, postgres_uri, proc, sqlite_uri (706/706 tests pass)
- Remote benchmark docs: iMac WiFi (784K rec/s) and charleskwon.com SSH tunnel (676K rec/s)
- Removed sensitive credentials from tracking
- ORM examples synced to v1.1.0-alpha
What's Changed
- docs(cookbook): portable boolean type + honest seek-vs-scan narrative in all-backends bench by @Admnwk in #49
- examples: self-contained ORM example over the ACE ABI by @Admnwk in #52
- perf(cdx): 64 KB read-ahead block cache for the record read path by @Admnwk in #51
- fix(engine): PACK rebuilds indexes; SOFT seek lands on next-greater key by @Admnwk in #40
- feat(adt): M6 ADI index keys for Date/Time/Timestamp/Money/Logical by @Admnwk in #45
- fix(abi): correct named-parameter substitution in AdsExecuteSQL (:p1/:p10 collision + 4096-byte truncation) by @Admnwk in #42
- feat(sql): accept SAP ADS dialect ({static} hint, [bracket] tables, table alias, UPPER()/LOWER() in WHERE, WHERE 1=1, comma-join) by @Admnwk in #48
- build: gate SQLite backend sources on OPENADS_WITH_SQLITE (fix OFF build) by @Admnwk in #43
- feat: SQLCipher-compatible encrypted databases via the SQLite backend by @Admnwk in #19
- fix(cdx,tx): multi-tag CDX allocator, branch erase, and safe transaction flush by @Admnwk in #25
- feat(contrib): harbour-orm — ActiveRecord ORM for Harbour over the ACE ABI by @Admnwk in #29
- fix(abi): reject INDEX expression naming an unknown column by @Admnwk in #56
- fix(adi): correct multi-level char-key index build (runaway + truncated child page) by @Admnwk in #59
- feat(sql-backend): native Firebird backend behind the backend-ops registry (stacked on #31) by @Admnwk in #54
- fix(cdx): multi-tag OrdNumber, duplicate binding, and unbounded .cdx growth by @Admnwk in #55
- fix: conform DBF/CDX/ADT writer to on-disk format so native engines open it by @Admnwk in #60
- examples: standalone ACE self-test (Harbour host, no RDD) by @Admnwk in #61
- feat: ODBC-backed table driver (read + write) behind the ACE ABI (builds on #18) by @Admnwk in #24
- feat(ado): FiveWin ADO bridge class for OpenADS (TDataBase:SqlQuery via AdsExecuteSQLDirect) by @Admnwk in #26
- test(bench): OpenADS Plus benchmark harness (NAV + exhaustive stress + ADS/OpenADS A/B) by @Admnwk in #27
- examples(orm): sync to v1.1.0-alpha — events, soft-deletes, N:N + nested eager, withCount, ES + roadmap by @Admnwk in #64
Full Changelog: v1.1.0...v1.2.1
v1.2.0 — Deferred-Flush Bulk Insert + MSSQL Backend
v1.2.0 (2026-06-24)
Deferred-Flush Bulk-Insert Mode (528× speedup)
New \AdsSetDeferredFlush(hTable, 1)\ API puts the table into deferred-flush mode: \AdsWriteRecord\ writes the record to OS cache but skips the per-record \FlushFileBuffers\ call. Data is flushed only when \AdsFlushFileBuffers\ is called explicitly.
Benchmark: 500K records + CDX index
- Before: ~2.7 hours (50 rec/s)
- After: 26 seconds (26,381 rec/s)
MSSQL Native TDS 7.4 Backend
Native SQL Server connectivity via the TDS 7.4 wire protocol with optional mbedTLS encryption. URI: \mssql://user:pass@host:port/database. Enable: \OPENADS_WITH_MSSQL=ON.
Test Results
649/649 unit tests pass. 0 failures.
What's Changed
- docs(cookbook): portable boolean type + honest seek-vs-scan narrative in all-backends bench by @Admnwk in #49
- examples: self-contained ORM example over the ACE ABI by @Admnwk in #52
- perf(cdx): 64 KB read-ahead block cache for the record read path by @Admnwk in #51
- fix(engine): PACK rebuilds indexes; SOFT seek lands on next-greater key by @Admnwk in #40
- feat(adt): M6 ADI index keys for Date/Time/Timestamp/Money/Logical by @Admnwk in #45
- fix(abi): correct named-parameter substitution in AdsExecuteSQL (:p1/:p10 collision + 4096-byte truncation) by @Admnwk in #42
- feat(sql): accept SAP ADS dialect ({static} hint, [bracket] tables, table alias, UPPER()/LOWER() in WHERE, WHERE 1=1, comma-join) by @Admnwk in #48
- build: gate SQLite backend sources on OPENADS_WITH_SQLITE (fix OFF build) by @Admnwk in #43
- feat: SQLCipher-compatible encrypted databases via the SQLite backend by @Admnwk in #19
- fix(cdx,tx): multi-tag CDX allocator, branch erase, and safe transaction flush by @Admnwk in #25
- feat(contrib): harbour-orm — ActiveRecord ORM for Harbour over the ACE ABI by @Admnwk in #29
- fix(abi): reject INDEX expression naming an unknown column by @Admnwk in #56
- fix(adi): correct multi-level char-key index build (runaway + truncated child page) by @Admnwk in #59
- feat(sql-backend): native Firebird backend behind the backend-ops registry (stacked on #31) by @Admnwk in #54
- fix(cdx): multi-tag OrdNumber, duplicate binding, and unbounded .cdx growth by @Admnwk in #55
- fix: conform DBF/CDX/ADT writer to on-disk format so native engines open it by @Admnwk in #60
- examples: standalone ACE self-test (Harbour host, no RDD) by @Admnwk in #61
- feat: ODBC-backed table driver (read + write) behind the ACE ABI (builds on #18) by @Admnwk in #24
Full Changelog: v1.1.0...v1.2.0
v1.1.0
What's Changed
- feat: SQLite + PostgreSQL + MariaDB + ODBC behind a pluggable backend-ops registry by @Admnwk in #31
- feat: SQL passthrough — run SQL directly against the SQLite backend (builds on #18) by @Admnwk in #21
- feat: PostgreSQL table driver behind the ACE ABI (builds on #18) by @Admnwk in #22
- fix(abi): AdsSetDouble/AdsSetLogical honor remote tables (tcp:// navigational writes) by @Admnwk in #28
Full Changelog: v1.0.4...v1.1.0