Skip to content

Commit d2f3361

Browse files
committed
Build-To-Use: 4.1.0-57
CBL-8083: Added TLSContext::registerPrivateKey() [CBL-8083] (#2453) CBL-7988: Upgrade SQLite to 3.51.3 (#2452) CBL-7866: Exception when decode a cbllog binary file (#2447) CBL-7774: Increase WS Ping/Pong timeout to 20s from 10s (#2424) CBL-7991: Error when creating a partial value index with compound expressions (#2444) CBL-7782: Query for document count() is too slow for big database (#2439) EE: CBL-7962: Handle CBManagerStateResetting (#121) CBL-7985: Fix crash in MeshManager::maybeConnect (#120) c874deb Move TLSCodec.hh to P2P/include folder
2 parents ea6e1a1 + e4cfd54 commit d2f3361

60 files changed

Lines changed: 637 additions & 270 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@ jobs:
2929
strategy:
3030
fail-fast: false
3131
matrix:
32-
os: [ubuntu-latest, macOS-latest, windows-latest]
32+
include:
33+
- os: ubuntu-latest
34+
cc: clang
35+
cxx: clang++
36+
- os: macOS-latest
37+
- os: windows-latest
3338
runs-on: ${{ matrix.os }}
39+
env:
40+
CC: ${{ matrix.cc }}
41+
CXX: ${{ matrix.cxx }}
3442

3543
steps:
3644
- uses: actions/checkout@v2

C/Cpp_include/c4Database.hh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ struct C4Database
210210

211211
virtual C4Timestamp nextDocExpiration() const = 0;
212212

213+
// Deleted Table:
214+
virtual bool isDeletedTableComplete() const = 0;
215+
213216
// Blobs:
214217

215218
virtual C4BlobStore& getBlobStore() const = 0;

C/c4Log.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "LogObserver.hh"
1919
#include "Logging.hh"
2020
#include "PlatformIO.hh" // For vasprintf on Windows
21+
#include "fleece/PlatformCompat.hh"
2122
#include "WebSocketInterface.hh"
2223
#include "c4ExceptionUtils.hh"
2324

@@ -302,7 +303,8 @@ C4LogDomain c4log_getDomain(const char* name, bool create) noexcept {
302303
if ( !name ) return kC4DefaultLog;
303304
auto domain = LogDomain::named(name);
304305
// LogDomain instances are never deleted, so it's fine to use `new` and `strdup`.
305-
if ( !domain && create ) domain = new LogDomain(strdup(name));
306+
if ( !domain && create ) domain = new LogDomain(cbl_strdup(name));
307+
306308
return asExternal(domain);
307309
}
308310

C/include/c4Compat.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@
138138

139139

140140
// Type-checking for printf-style vararg functions:
141-
#ifdef _MSC_VER
142-
# define __printflike(A, B)
143-
#else
144-
# ifndef __printflike
141+
#ifndef __printflike
142+
# if __has_attribute(__format__)
145143
# define __printflike(fmtarg, firstvararg) __attribute__((__format__(__printf__, fmtarg, firstvararg)))
144+
# else
145+
# define __printflike(A, B)
146146
# endif
147147
#endif

C/include/c4Database.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ NODISCARD CBL_CORE_API C4Database* c4db_openAgain(C4Database* db, C4Error* C4NUL
9494
@param destinationName The name (without filename extension) of the database to create.
9595
@param config Database configuration (including destination directory.)
9696
@param error On failure, error info will be written here.
97+
\note if there is an open database on the sourcePath, it will fail with error code kC4ErrorBusy
9798
@return True on success, false on failure. */
9899
NODISCARD CBL_CORE_API bool c4db_copyNamed(C4String sourcePath, C4String destinationName,
99100
const C4DatabaseConfig2* config, C4Error* C4NULLABLE error) C4API;

C/tests/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,18 @@ target_include_directories(
139139
file(COPY data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/C/tests)
140140
file(COPY ../../LiteCore/tests/data/replacedb/ios120/iosdb.cblite2
141141
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/LiteCore/tests/data/replacedb/ios120)
142+
143+
if(WIN32)
144+
add_custom_command(TARGET C4Tests POST_BUILD
145+
COMMAND ${CMAKE_COMMAND} -E copy_if_different
146+
$<TARGET_FILE:LiteCore>
147+
$<TARGET_FILE_DIR:C4Tests>
148+
)
149+
150+
add_custom_command(TARGET C4Tests POST_BUILD
151+
COMMAND ${CMAKE_COMMAND} -E copy_if_different
152+
$<TARGET_PDB_FILE:LiteCore>
153+
$<TARGET_FILE_DIR:C4Tests>
154+
VERBATIM
155+
)
156+
endif()

C/tests/c4CertificateTest.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# include "CertHelper.hh"
1717
# ifdef WIN32
1818
# include "Winsock2.h"
19+
# include "Ws2tcpip.h"
1920
# define in_addr_t unsigned long
2021
# else
2122
# include <arpa/inet.h>
@@ -41,7 +42,8 @@ TEST_CASE("C4Certificate smoke test", "[Certs][C]") {
4142
}
4243

4344
TEST_CASE("C4Certificate Subject Name", "[Certs][C]") {
44-
auto ipAddr = htonl(inet_addr("127.0.0.1"));
45+
struct in_addr ipAddr;
46+
REQUIRE(inet_pton(AF_INET, "127.0.0.1", &ipAddr) == 1);
4547
fleece::slice ipAddrSlice((void*)&ipAddr, sizeof(in_addr_t));
4648
C4CertNameComponent nameComponents[] = {{kC4Cert_CommonName, "CommonName"_sl},
4749
{kC4Cert_Pseudonym, "Pseudonym"_sl},

C/tests/c4DatabaseTest.cc

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,8 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Test delete while database open", "[Data
233233
#ifdef FAIL_FAST
234234
CHECK(slice(message) == "LiteCore Busy, \"Can't delete db file while the caller has open connections\"");
235235
#else
236-
CHECK(slice(message)
237-
== "LiteCore Busy, \"Can't delete db file while other connections are open. The open connections are tagged "
238-
"appOpened.\"");
236+
CHECK(slice(message).hasPrefix("LiteCore Busy, \"Can't delete db file while other connections are open. The open "
237+
"connections are tagged "));
239238
#endif
240239
FLSliceResult_Release(message);
241240
}
@@ -369,6 +368,39 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database Enumerator", "[Database][Docume
369368
CHECK(i == 100);
370369
}
371370

371+
N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database Enumerator with V3.0 Database",
372+
"[Database][Document][Enumerator][C]") {
373+
closeDB();
374+
auto name = copyFixtureDB("db_versions/db_3_0_0_names_100_with_deleted.cblite2");
375+
C4DatabaseConfig2 config = {slice(TempDir())};
376+
config.flags &= !kC4DB_Create;
377+
db = REQUIRED(c4db_openNamed(name, &config, ERROR_INFO()));
378+
379+
auto defaultColl = getCollection(db, kC4DefaultCollectionSpec);
380+
C4EnumeratorOptions options = kC4DefaultEnumeratorOptions;
381+
options.flags &= ~kC4IncludeBodies;
382+
383+
C4Error error;
384+
c4::ref<C4DocEnumerator> e = REQUIRED(c4coll_enumerateAllDocs(defaultColl, &options, WITH_ERROR()));
385+
bool isFirstIteration = true;
386+
while ( c4enum_next(e, &error) ) {
387+
if ( isFirstIteration ) {
388+
// to allow Housekeeper/migration to catch up, in order to hit SQLITE_BUSY_SNAPSHOT
389+
std::this_thread::sleep_for(1ms);
390+
isFirstIteration = false;
391+
}
392+
auto doc = c4enum_getDocument(e, ERROR_INFO());
393+
REQUIRE(doc);
394+
395+
CHECK(c4doc_getProperties(doc) == nullptr);
396+
// Doc was loaded without its body, but it should load on demand:
397+
CHECK(c4doc_loadRevisionBody(doc, WITH_ERROR())); // have to explicitly load the body
398+
399+
c4doc_release(doc);
400+
}
401+
CHECK(error == C4Error{});
402+
}
403+
372404
N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database Enumerator With Info", "[Database][Enumerator][C]") {
373405
setupAllDocs();
374406
C4Error error;
@@ -877,7 +909,16 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database copy", "[Database][C]") {
877909

878910
if ( !c4db_deleteNamed(kNuName, slice(nuPath), &error) ) { REQUIRE(error.code == 0); }
879911

912+
{
913+
ExpectingExceptions x;
914+
// The source db cannot be open to copyNamed from.
915+
REQUIRE(!c4db_copyNamed(c4str(srcPathStr.c_str()), kNuName, &config, &error));
916+
CHECK(error.domain == LiteCoreDomain);
917+
CHECK(error.code == kC4ErrorBusy);
918+
}
919+
REQUIRED(c4db_close(db, nullptr));
880920
REQUIRE(c4db_copyNamed(c4str(srcPathStr.c_str()), kNuName, &config, WITH_ERROR()));
921+
881922
auto nudb = c4db_openNamed(kNuName, &config, ERROR_INFO());
882923
REQUIRE(nudb);
883924
auto defaultColl = getCollection(nudb, kC4DefaultCollectionSpec);

C/tests/c4QueryTest.cc

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,48 @@ N_WAY_TEST_CASE_METHOD(C4QueryTest, "C4Query partial value index", "[Query][C]")
317317
}
318318
}
319319

320+
N_WAY_TEST_CASE_METHOD(C4QueryTest, "C4Query partial value index with compound expressions", "[Query][C]") {
321+
C4Error err;
322+
auto defaultColl = getCollection(db, kC4DefaultCollectionSpec);
323+
324+
// First add some documents with specific fields to test
325+
{
326+
TransactionHelper t(db);
327+
createFleeceRev(db, "compound1"_sl, kRevID,
328+
R"({"locationID": "store1", "status": "active", "isPurged": false})"_sl);
329+
createFleeceRev(db, "compound2"_sl, kRevID,
330+
R"({"locationID": "store2", "status": "inactive", "isPurged": true})"_sl);
331+
createFleeceRev(db, "compound3"_sl, kRevID,
332+
R"({"locationID": "store1", "status": "active", "isPurged": true})"_sl);
333+
}
334+
335+
// Create a partial value index with compound expressions
336+
C4IndexOptions options{};
337+
options.where = R"(["=", [".isPurged"], false])";
338+
339+
// This is what was causing the bug - a value index with multiple expressions and a WHERE clause
340+
REQUIRE(c4coll_createIndex(defaultColl, C4STR("compound_idx"), c4str(R"([".locationID", ".status", ".isPurged"])"),
341+
kC4JSONQuery, kC4ValueIndex, &options, WITH_ERROR(&err)));
342+
343+
// Verify that the index works by running a query that would use it
344+
compileSelect("SELECT META().id FROM _ WHERE locationID = 'store1' AND status = 'active' AND isPurged = false",
345+
kC4N1QLQuery);
346+
REQUIRE(query);
347+
CHECK(run() == (vector<string>{"compound1"}));
348+
349+
// Create another index with N1QL syntax to verify that works too
350+
C4IndexOptions options2{};
351+
options2.where = "isPurged = true";
352+
REQUIRE(c4coll_createIndex(defaultColl, C4STR("compound_idx2"), c4str("locationID, status, isPurged"), kC4N1QLQuery,
353+
kC4ValueIndex, &options2, WITH_ERROR(&err)));
354+
355+
// Verify the second index works
356+
compileSelect("SELECT META().id FROM _ WHERE locationID = 'store1' AND status = 'active' AND isPurged = true",
357+
kC4N1QLQuery);
358+
REQUIRE(query);
359+
CHECK(run() == (vector<string>{"compound3"}));
360+
}
361+
320362
static bool lookForIndex(C4Database* db, slice name) {
321363
bool found = false;
322364
auto defaultColl = C4QueryTest::getCollection(db, kC4DefaultCollectionSpec);
@@ -1690,8 +1732,18 @@ N_WAY_TEST_CASE_METHOD(C4QueryTest, "Multiple C4Query observers", "[Query][C][!t
16901732
}
16911733

16921734
N_WAY_TEST_CASE_METHOD(C4QueryTest, "C4Query observers lifetime", "[Query][C][!throws]") {
1735+
int sw = 0;
1736+
SECTION("With 3.0 DB") {
1737+
closeDB();
1738+
auto name = copyFixtureDB("db_versions/db_3_0_0_names_100_with_deleted.cblite2");
1739+
C4DatabaseConfig2 config = {slice(TempDir())};
1740+
config.flags &= !kC4DB_Create;
1741+
db = REQUIRED(c4db_openNamed(name, &config, ERROR_INFO()));
1742+
sw = GENERATE(0, 1, 2, 3);
1743+
}
1744+
SECTION("With current DB") { sw = GENERATE(0, 1, 2, 3); }
1745+
16931746
compile(json5("['=', ['.', 'contact', 'address', 'state'], 'CA']"));
1694-
const int sw = GENERATE(0, 1, 2, 3);
16951747

16961748
struct State {
16971749
C4Query* query;

C/tests/c4Test.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ void C4ExpectException(C4ErrorDomain domain, int code, const std::function<void(
122122

123123
#pragma mark - C4TEST CLASS
124124

125-
#if defined(CMAKE) && defined(_MSC_VER)
125+
#if defined(CMAKE) && defined(_MSC_VER) && !defined(__clang__)
126126
string C4Test::sFixturesDir = "../C/tests/data/";
127127
string C4Test::sReplicatorFixturesDir = "../Replicator/tests/data/";
128128
#else

0 commit comments

Comments
 (0)