From 9f58252c1e827a1a44afbb2eb6f004f009638e65 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 28 May 2026 10:30:49 -0400 Subject: [PATCH 1/4] drop `prefixPreview` and `suffixPreview` --- CHANGELOG.md | 6 ++ src/mc-efc-private.h | 3 + src/mc-efc.c | 24 +++++- src/mongocrypt-ctx.c | 12 +-- test/data/cleanup/text-search/collinfo.json | 4 +- .../encrypted-field-config-map.json | 4 +- test/data/compact/text-search/collinfo.json | 4 +- .../encrypted-field-config-map.json | 4 +- .../text-search/encrypted-payload.json | 4 +- .../efc-textSearchFields-badVersionSet.json | 4 +- .../efc-textSearchFields-goodVersionSet.json | 4 +- test/data/efc/efc-textSearchFields.json | 4 +- .../fle2-insert-text-search-preview/cmd.json | 9 +++ .../encrypted-field-map-prefixPreview.json | 24 ++++++ .../encrypted-field-map-suffixPreview.json | 24 ++++++ test/test-mongocrypt-ctx-encrypt.c | 81 ++++++++++++++++++- test/test-mongocrypt-ctx-setopt.c | 23 +++++- 17 files changed, 205 insertions(+), 33 deletions(-) create mode 100644 test/data/fle2-insert-text-search-preview/cmd.json create mode 100644 test/data/fle2-insert-text-search-preview/encrypted-field-map-prefixPreview.json create mode 100644 test/data/fle2-insert-text-search-preview/encrypted-field-map-suffixPreview.json diff --git a/CHANGELOG.md b/CHANGELOG.md index b51e793aa..1cc6b04f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### Added +- Stable support for `prefix` and `suffix` query types. + +### Removed +- Support for the experimental `prefixPreview` and `suffixPreview` query types. + ## 1.18.1 diff --git a/src/mc-efc-private.h b/src/mc-efc-private.h index 86a4d3f8d..f78379602 100644 --- a/src/mc-efc-private.h +++ b/src/mc-efc-private.h @@ -32,6 +32,9 @@ typedef enum _supported_query_type_flags { SUPPORTS_SUBSTRING_PREVIEW_QUERIES = 1 << 3, SUPPORTS_SUFFIX_QUERIES = 1 << 4, SUPPORTS_PREFIX_QUERIES = 1 << 5, + // prefixPreview and suffixPreview are dropped. Setting this results in an error. + SUPPORTS_SUFFIX_PREVIEW_DEPRECATED_QUERIES = 1 << 6, + SUPPORTS_PREFIX_PREVIEW_DEPRECATED_QUERIES = 1 << 7, } supported_query_type_flags; typedef struct _mc_EncryptedField_t { diff --git a/src/mc-efc.c b/src/mc-efc.c index 432615b5d..ad43a531a 100644 --- a/src/mc-efc.c +++ b/src/mc-efc.c @@ -35,12 +35,14 @@ static bool _parse_query_type_string(const char *queryType, supported_query_type *out = SUPPORTS_RANGE_PREVIEW_DEPRECATED_QUERIES; } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR), qtv)) { *out = SUPPORTS_SUBSTRING_PREVIEW_QUERIES; - } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR), qtv) - || mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIX_STR), qtv)) { + } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIX_STR), qtv)) { *out = SUPPORTS_SUFFIX_QUERIES; - } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR), qtv) - || mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIX_STR), qtv)) { + } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIX_STR), qtv)) { *out = SUPPORTS_PREFIX_QUERIES; + } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR), qtv)) { + *out = SUPPORTS_SUFFIX_PREVIEW_DEPRECATED_QUERIES; + } else if (mstr_eq_ignore_case(mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR), qtv)) { + *out = SUPPORTS_PREFIX_PREVIEW_DEPRECATED_QUERIES; } else { return false; } @@ -176,6 +178,20 @@ static bool _parse_field(mc_EncryptedFieldConfig_t *efc, bson_t *field, mongocry return false; } + if (query_types & SUPPORTS_PREFIX_PREVIEW_DEPRECATED_QUERIES) { + CLIENT_ERR("Cannot use field '%s' with 'prefixPreview' queries. 'prefixPreview' is unsupported. Use 'prefix' " + "instead.", + field_path); + return false; + } + + if (query_types & SUPPORTS_SUFFIX_PREVIEW_DEPRECATED_QUERIES) { + CLIENT_ERR("Cannot use field '%s' with 'suffixPreview' queries. 'suffixPreview' is unsupported. Use 'suffix' " + "instead.", + field_path); + return false; + } + /* Prepend a new mc_EncryptedField_t */ mc_EncryptedField_t *ef = bson_malloc0(sizeof(mc_EncryptedField_t)); if (has_keyid) { diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index 1462edecd..00f742b80 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -1028,15 +1028,11 @@ bool mongocrypt_ctx_setopt_query_type(mongocrypt_ctx_t *ctx, const char *query_t ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_SUFFIX; ctx->opts.query_type.set = true; } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR))) { - // TODO: MONGOCRYPT-870 disallow prefixPreview - // _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'prefixPreview' is deprecated, please use 'prefix'"); - ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_PREFIX; - ctx->opts.query_type.set = true; + _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'prefixPreview' is deprecated, please use 'prefix'"); + return false; } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR))) { - // TODO: MONGOCRYPT-870 disallow suffixPreview - // _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'suffixPreview' is deprecated, please use 'suffix'"); - ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_SUFFIX; - ctx->opts.query_type.set = true; + _mongocrypt_ctx_fail_w_msg(ctx, "Query type 'suffixPreview' is deprecated, please use 'suffix'"); + return false; } else if (mstr_eq_ignore_case(qt_str, mstrv_lit(MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR))) { ctx->opts.query_type.value = MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW; ctx->opts.query_type.set = true; diff --git a/test/data/cleanup/text-search/collinfo.json b/test/data/cleanup/text-search/collinfo.json index a64a0e7da..6109a16dc 100644 --- a/test/data/cleanup/text-search/collinfo.json +++ b/test/data/cleanup/text-search/collinfo.json @@ -74,7 +74,7 @@ "path": "textField2", "bsonType": "string", "queries": [{ - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" }, @@ -84,7 +84,7 @@ "diacriticSensitive": false }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" }, diff --git a/test/data/cleanup/text-search/encrypted-field-config-map.json b/test/data/cleanup/text-search/encrypted-field-config-map.json index ca45b054b..21d92bf1f 100644 --- a/test/data/cleanup/text-search/encrypted-field-config-map.json +++ b/test/data/cleanup/text-search/encrypted-field-config-map.json @@ -72,7 +72,7 @@ "path": "textField2", "bsonType": "string", "queries": [{ - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" }, @@ -82,7 +82,7 @@ "diacriticSensitive": false }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" }, diff --git a/test/data/compact/text-search/collinfo.json b/test/data/compact/text-search/collinfo.json index a64a0e7da..6109a16dc 100644 --- a/test/data/compact/text-search/collinfo.json +++ b/test/data/compact/text-search/collinfo.json @@ -74,7 +74,7 @@ "path": "textField2", "bsonType": "string", "queries": [{ - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" }, @@ -84,7 +84,7 @@ "diacriticSensitive": false }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" }, diff --git a/test/data/compact/text-search/encrypted-field-config-map.json b/test/data/compact/text-search/encrypted-field-config-map.json index ca45b054b..21d92bf1f 100644 --- a/test/data/compact/text-search/encrypted-field-config-map.json +++ b/test/data/compact/text-search/encrypted-field-config-map.json @@ -72,7 +72,7 @@ "path": "textField2", "bsonType": "string", "queries": [{ - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" }, @@ -82,7 +82,7 @@ "diacriticSensitive": false }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" }, diff --git a/test/data/compact/text-search/encrypted-payload.json b/test/data/compact/text-search/encrypted-payload.json index aa51bb270..1a80b4e14 100644 --- a/test/data/compact/text-search/encrypted-payload.json +++ b/test/data/compact/text-search/encrypted-payload.json @@ -78,7 +78,7 @@ "path": "textField2", "bsonType": "string", "queries": [{ - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" }, @@ -88,7 +88,7 @@ "diacriticSensitive": false }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" }, diff --git a/test/data/efc/efc-textSearchFields-badVersionSet.json b/test/data/efc/efc-textSearchFields-badVersionSet.json index 76992885f..7bcc618f6 100644 --- a/test/data/efc/efc-textSearchFields-badVersionSet.json +++ b/test/data/efc/efc-textSearchFields-badVersionSet.json @@ -30,13 +30,13 @@ "bsonType": "string", "queries": [ { - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" } }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" } diff --git a/test/data/efc/efc-textSearchFields-goodVersionSet.json b/test/data/efc/efc-textSearchFields-goodVersionSet.json index 87fccdec6..217608fb8 100644 --- a/test/data/efc/efc-textSearchFields-goodVersionSet.json +++ b/test/data/efc/efc-textSearchFields-goodVersionSet.json @@ -30,13 +30,13 @@ "bsonType": "string", "queries": [ { - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" } }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" } diff --git a/test/data/efc/efc-textSearchFields.json b/test/data/efc/efc-textSearchFields.json index 8eda94a86..befc3d545 100644 --- a/test/data/efc/efc-textSearchFields.json +++ b/test/data/efc/efc-textSearchFields.json @@ -30,13 +30,13 @@ "bsonType": "string", "queries": [ { - "queryType": "suffixPreview", + "queryType": "suffix", "contention": { "$numberLong": "0" } }, { - "queryType": "prefixPreview", + "queryType": "prefix", "contention": { "$numberLong": "0" } diff --git a/test/data/fle2-insert-text-search-preview/cmd.json b/test/data/fle2-insert-text-search-preview/cmd.json new file mode 100644 index 000000000..ca12d021d --- /dev/null +++ b/test/data/fle2-insert-text-search-preview/cmd.json @@ -0,0 +1,9 @@ +{ + "insert": "test", + "documents": [ + { + "_id": 1, + "ssn": "value123" + } + ] +} \ No newline at end of file diff --git a/test/data/fle2-insert-text-search-preview/encrypted-field-map-prefixPreview.json b/test/data/fle2-insert-text-search-preview/encrypted-field-map-prefixPreview.json new file mode 100644 index 000000000..8e7463e34 --- /dev/null +++ b/test/data/fle2-insert-text-search-preview/encrypted-field-map-prefixPreview.json @@ -0,0 +1,24 @@ +{ + "db.test": { + "escCollection": "fle2.test.esc", + "ecocCollection": "fle2.test.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encrypted", + "bsonType": "string", + "queries": { + "queryType": "prefixPreview", + "contention": { + "$numberLong": "0" + } + } + } + ] + } +} diff --git a/test/data/fle2-insert-text-search-preview/encrypted-field-map-suffixPreview.json b/test/data/fle2-insert-text-search-preview/encrypted-field-map-suffixPreview.json new file mode 100644 index 000000000..ae525fd43 --- /dev/null +++ b/test/data/fle2-insert-text-search-preview/encrypted-field-map-suffixPreview.json @@ -0,0 +1,24 @@ +{ + "db.test": { + "escCollection": "fle2.test.esc", + "ecocCollection": "fle2.test.ecoc", + "fields": [ + { + "keyId": { + "$binary": { + "base64": "EjRWeBI0mHYSNBI0VniQEg==", + "subType": "04" + } + }, + "path": "encrypted", + "bsonType": "string", + "queries": { + "queryType": "suffixPreview", + "contention": { + "$numberLong": "0" + } + } + } + ] + } +} diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index a3f046f17..1647788d0 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -2478,7 +2478,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { ee_testcase tc = {0}; tc.desc = "find suffix"; tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; - tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR; + tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -2496,7 +2496,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { ee_testcase tc = {0}; tc.desc = "find prefix"; tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; - tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR; + tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -2673,7 +2673,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; tc.keys_to_feed[0] = keyABC; - tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR; + tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR; tc.text_opts = TEST_BSON(RAW_STRING({ "caseSensitive" : true, "diacriticSensitive" : true, @@ -4410,6 +4410,80 @@ static void _test_rangePreview_fails(_mongocrypt_tester_t *tester) { bson_free(local_kek); } +// `_test_prefixPreview_suffixPreview_fails` tests that use of "prefixPreview" or "suffixPreview" errors. +static void _test_prefixPreview_suffixPreview_fails(_mongocrypt_tester_t *tester) { +#define TF(suffix) TEST_FILE("./test/data/fle2-insert-text-search-preview/" suffix) + + // local_kek is the KEK used to encrypt the keyMaterial in ./test/data/key-document-local.json + uint8_t local_kek_raw[MONGOCRYPT_KEY_LEN] = {0}; + char *local_kek = kms_message_raw_to_b64(local_kek_raw, sizeof(local_kek_raw)); + mongocrypt_binary_t *kms_providers = + TEST_BSON(BSON_STR({"local" : {"key" : {"$binary" : {"base64" : "%s", "subType" : "00"}}}}), local_kek); + + // Test setting 'prefixPreview' as an explicit encryption queryType results in error. + { + mongocrypt_t *crypt = mongocrypt_new(); + mongocrypt_setopt_kms_providers(crypt, kms_providers); + ASSERT_OK(mongocrypt_init(crypt), crypt); + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + ASSERT_OK(ctx, crypt); + ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR, -1), + ctx, + "Query type 'prefixPreview' is deprecated"); + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + } + + // Test setting 'suffixPreview' as an explicit encryption queryType results in error. + { + mongocrypt_t *crypt = mongocrypt_new(); + mongocrypt_setopt_kms_providers(crypt, kms_providers); + ASSERT_OK(mongocrypt_init(crypt), crypt); + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + ASSERT_OK(ctx, crypt); + ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR, -1), + ctx, + "Query type 'suffixPreview' is deprecated"); + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + } + + // Test setting 'prefixPreview' from encryptedFields results in error. + { + mongocrypt_t *crypt = mongocrypt_new(); + mongocrypt_setopt_kms_providers(crypt, kms_providers); + ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TF("encrypted-field-map-prefixPreview.json")), + crypt); + ASSERT_OK(mongocrypt_init(crypt), crypt); + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + ASSERT_OK(ctx, crypt); + ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), + ctx, + "Cannot use field 'encrypted' with 'prefixPreview' queries"); + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + } + + // Test setting 'suffixPreview' from encryptedFields results in error. + { + mongocrypt_t *crypt = mongocrypt_new(); + mongocrypt_setopt_kms_providers(crypt, kms_providers); + ASSERT_OK(mongocrypt_setopt_encrypted_field_config_map(crypt, TF("encrypted-field-map-suffixPreview.json")), + crypt); + ASSERT_OK(mongocrypt_init(crypt), crypt); + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + ASSERT_OK(ctx, crypt); + ASSERT_FAILS(mongocrypt_ctx_encrypt_init(ctx, "db", -1, TF("cmd.json")), + ctx, + "Cannot use field 'encrypted' with 'suffixPreview' queries"); + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + } + + bson_free(local_kek); +#undef TF +} + // `autoencryption_test` defines a test for the automatic encryption context. typedef struct { const char *desc; @@ -6819,6 +6893,7 @@ void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_encrypt_fle2_insert_text_search_payload_with_str_encode_version); INSTALL_TEST(_test_bulkWrite); INSTALL_TEST(_test_rangePreview_fails); + INSTALL_TEST(_test_prefixPreview_suffixPreview_fails); INSTALL_TEST(_test_no_trimFactor); INSTALL_TEST(_test_range_sends_cryptoParams); INSTALL_TEST(_test_encrypt_retry); diff --git a/test/test-mongocrypt-ctx-setopt.c b/test/test-mongocrypt-ctx-setopt.c index 990147254..4032fb937 100644 --- a/test/test-mongocrypt-ctx-setopt.c +++ b/test/test-mongocrypt-ctx-setopt.c @@ -1013,7 +1013,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { /* Set key ID to get past the 'either key id or key alt name required' * error */ ASSERT_KEY_ID_OK(uuid); - ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR, -1), ctx); + ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIX_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx); ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires textPreview index type"); @@ -1021,7 +1021,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { /* Set key ID to get past the 'either key id or key alt name required' * error */ ASSERT_KEY_ID_OK(uuid); - ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR, -1), ctx); + ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIX_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires textPreview index type"); @@ -1042,6 +1042,25 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires textPreview index type"); } + // Can't use "prefixPreview" or "suffixPreview". + { + mongocrypt_destroy(crypt); + crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT); + REFRESH_CTX; + ASSERT_KEY_ID_OK(uuid); + ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIXPREVIEW_DEPRECATED_STR, -1), + ctx, + "'prefixPreview' is deprecated"); + + mongocrypt_destroy(crypt); + crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT); + REFRESH_CTX; + ASSERT_KEY_ID_OK(uuid); + ASSERT_FAILS(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIXPREVIEW_DEPRECATED_STR, -1), + ctx, + "'suffixPreview' is deprecated"); + } + /* It is an error to set a text algorithm without setting text options */ { REFRESH; From 54e085d97627b5536b62c6d74670a55c6c745fec Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 28 May 2026 11:15:29 -0400 Subject: [PATCH 2/4] add "string" algorithm for "suffix" and "prefix" --- CHANGELOG.md | 5 ++- src/mongocrypt-ctx-encrypt.c | 32 +++++++++------ src/mongocrypt-ctx.c | 4 ++ src/mongocrypt-private.h | 1 + src/mongocrypt.h | 6 ++- test/test-mongocrypt-ctx-encrypt.c | 63 +++++++++++++++++++++++++----- test/test-mongocrypt-ctx-setopt.c | 33 ++++++++-------- 7 files changed, 105 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc6b04f5..743275bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,10 @@ ## Unreleased ### Added -- Stable support for `prefix` and `suffix` query types. +- Stable support for prefix and suffix queries: + - The `prefixPreview` query type is replaced with `prefix`. + - The `suffixPreview` query type is replaced with `suffix`. + - Use the `string` algorithm (formerly `textPreview`) for `prefix` and `suffix` query types. ### Removed - Support for the experimental `prefixPreview` and `suffixPreview` query types. diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index 4df7042ec..5d6b140f1 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -1539,7 +1539,8 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t * goto fail; // fallthrough case MONGOCRYPT_INDEX_TYPE_RANGE: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_RANGE; break; - case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH; break; + case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: + case MONGOCRYPT_INDEX_TYPE_STRING: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH; break; default: // This might be unreachable because of other validation. Better safe than // sorry. @@ -1632,6 +1633,7 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t * case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED: case MONGOCRYPT_INDEX_TYPE_RANGE: case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: + case MONGOCRYPT_INDEX_TYPE_STRING: // All QE indexed algorithms require contention factor. BSON_ASSERT(ctx->opts.contention_factor.set); // Checked earlier in explicit_encrypt_init. marking.u.fle2.maxContentionFactor = ctx->opts.contention_factor.value; @@ -2088,13 +2090,13 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms if (ctx->opts.query_type.set) { const mongocrypt_query_type_t qt = ctx->opts.query_type.value; if (qt == MONGOCRYPT_QUERY_TYPE_PREFIX) { - if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW)) { - return _mongocrypt_ctx_fail_w_msg(ctx, "prefix query type requires textPreview index type"); + if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) { + return _mongocrypt_ctx_fail_w_msg(ctx, "prefix query type requires string index type"); } } if (qt == MONGOCRYPT_QUERY_TYPE_SUFFIX) { - if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW)) { - return _mongocrypt_ctx_fail_w_msg(ctx, "suffix query type requires textPreview index type"); + if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) { + return _mongocrypt_ctx_fail_w_msg(ctx, "suffix query type requires string index type"); } } if (qt == MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW) { @@ -2114,13 +2116,17 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms } if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW) { - return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with textPreview index type"); + return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with 'textPreview' algorithm"); + } + if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING) { + return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with 'string' algorithm"); } } if (ctx->opts.textopts.set && ctx->opts.index_type.set) { - if (ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW) { - return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set text opts without textPreview index type"); + if (ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW + && ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_STRING) { + return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set string opts without string or textPreview index type"); } } @@ -2147,13 +2153,15 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms } // Check required options for text algorithm are set: - if (ctx->opts.index_type.set && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW)) { + if (ctx->opts.index_type.set + && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW + || ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) { if (!ctx->opts.contention_factor.set) { - return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for textPreview algorithm"); + return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for string algorithm"); } if (!ctx->opts.textopts.set) { - return _mongocrypt_ctx_fail_w_msg(ctx, "text opts are required for textPreview algorithm"); + return _mongocrypt_ctx_fail_w_msg(ctx, "string opts are required for string algorithm"); } } @@ -2182,6 +2190,8 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms // fallthrough case MONGOCRYPT_QUERY_TYPE_PREFIX: case MONGOCRYPT_QUERY_TYPE_SUFFIX: + matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING); + break; case MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW: matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW); break; diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index 00f742b80..5082dde78 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -248,6 +248,9 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR))) { ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW; ctx->opts.index_type.set = true; + } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_STRING_STR))) { + ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_STRING; + ctx->opts.index_type.set = true; } else { char *error = bson_strdup_printf("unsupported algorithm string \"%.*s\"", algo_str.len <= (size_t)INT_MAX ? (int)algo_str.len : INT_MAX, @@ -1055,6 +1058,7 @@ const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val) { case MONGOCRYPT_INDEX_TYPE_RANGE: return "Range"; case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED: return "RangePreview"; case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: return "TextPreview"; + case MONGOCRYPT_INDEX_TYPE_STRING: return "String"; default: return "Unknown"; } } diff --git a/src/mongocrypt-private.h b/src/mongocrypt-private.h index 9f59888fd..cd3b16bf1 100644 --- a/src/mongocrypt-private.h +++ b/src/mongocrypt-private.h @@ -157,6 +157,7 @@ typedef enum { MONGOCRYPT_INDEX_TYPE_RANGE = 3, MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED = 4, MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW = 5, + MONGOCRYPT_INDEX_TYPE_STRING = 6, } mongocrypt_index_type_t; bool _mongocrypt_validate_and_copy_string(const char *in, int32_t in_len, char **out) MONGOCRYPT_WARN_UNUSED_RESULT; diff --git a/src/mongocrypt.h b/src/mongocrypt.h index 334d082d7..b32cde422 100644 --- a/src/mongocrypt.h +++ b/src/mongocrypt.h @@ -718,6 +718,8 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith #define MONGOCRYPT_ALGORITHM_RANGE_STR "Range" /// NOTE: "textPreview" is experimental only and may be removed in a future non-major release. #define MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR "textPreview" +// String constant for setopt_algorithm "string" explicit encryption. +#define MONGOCRYPT_ALGORITHM_STRING_STR "string" /** * Identify the AWS KMS master key to use for creating a data key. @@ -1555,7 +1557,7 @@ MONGOCRYPT_EXPORT bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts); /** - * Set options for explicit encryption with the "textPreview" algorithm. + * Set options for explicit encryption with the "textPreview" algorithm or "string" algorithm. * * NOTE: "textPreview" is experimental only and may be removed in a future non-major release. * @p opts is a BSON document of the form: @@ -1578,6 +1580,8 @@ bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_bin * } * * "prefix" and "suffix" can both be set. + * + * NOTE: Driver public APIs should use the name "string" rather than "text" to refer to options. */ MONGOCRYPT_EXPORT bool mongocrypt_ctx_setopt_algorithm_text(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts); diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index 1647788d0..faf4a7f4e 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -2477,7 +2477,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { { ee_testcase tc = {0}; tc.desc = "find suffix"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); @@ -2495,7 +2495,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { { ee_testcase tc = {0}; tc.desc = "find prefix"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); @@ -2513,7 +2513,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { { ee_testcase tc = {0}; tc.desc = "find suffix"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); @@ -2531,7 +2531,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { { ee_testcase tc = {0}; tc.desc = "find prefix"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); @@ -2570,7 +2570,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { #include "./data/fle2-insert-text-search/RNG_DATA.h" tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}}; #undef RNG_DATA - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -2590,7 +2590,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { #include "./data/fle2-insert-text-search/RNG_DATA.h" tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}}; #undef RNG_DATA - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -2630,7 +2630,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { #include "./data/fle2-insert-text-search/RNG_DATA.h" tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}}; #undef RNG_DATA - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -2651,7 +2651,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { #include "./data/fle2-insert-text-search/RNG_DATA.h" tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}}; #undef RNG_DATA - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -2668,7 +2668,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { { ee_testcase tc = {0}; tc.desc = "find prefix on a field with prefix+suffix"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -4484,6 +4484,50 @@ static void _test_prefixPreview_suffixPreview_fails(_mongocrypt_tester_t *tester #undef TF } +// `_test_textPreview_fails` tests that using "textPreview" algorithm with suffix or prefix errors. +static void _test_textPreview_fails(_mongocrypt_tester_t *tester) { + _mongocrypt_buffer_t keyABC_id; + _mongocrypt_buffer_copy_from_hex(&keyABC_id, "ABCDEFAB123498761234123456789012"); + + // textPreview with suffix query type fails. + { + ee_testcase tc = {0}; + tc.desc = "textPreview with suffix fails"; + tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR; + tc.contention_factor = OPT_I64(1); + tc.msg = TEST_BSON("{'v': 'abc'}"); + tc.user_key_id = &keyABC_id; + tc.text_opts = TEST_BSON(RAW_STRING({ + "caseSensitive" : true, + "diacriticSensitive" : true, + "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100} + })); + tc.expect_init_error = "suffix query type requires string index type"; + ee_testcase_run(&tc); + } + + // textPreview with prefix query type fails. + { + ee_testcase tc = {0}; + tc.desc = "textPreview with prefix fails"; + tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR; + tc.contention_factor = OPT_I64(1); + tc.msg = TEST_BSON("{'v': 'abc'}"); + tc.user_key_id = &keyABC_id; + tc.text_opts = TEST_BSON(RAW_STRING({ + "caseSensitive" : true, + "diacriticSensitive" : true, + "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100} + })); + tc.expect_init_error = "prefix query type requires string index type"; + ee_testcase_run(&tc); + } + + _mongocrypt_buffer_cleanup(&keyABC_id); +} + // `autoencryption_test` defines a test for the automatic encryption context. typedef struct { const char *desc; @@ -6894,6 +6938,7 @@ void _mongocrypt_tester_install_ctx_encrypt(_mongocrypt_tester_t *tester) { INSTALL_TEST(_test_bulkWrite); INSTALL_TEST(_test_rangePreview_fails); INSTALL_TEST(_test_prefixPreview_suffixPreview_fails); + INSTALL_TEST(_test_textPreview_fails); INSTALL_TEST(_test_no_trimFactor); INSTALL_TEST(_test_range_sends_cryptoParams); INSTALL_TEST(_test_encrypt_retry); diff --git a/test/test-mongocrypt-ctx-setopt.c b/test/test-mongocrypt-ctx-setopt.c index 4032fb937..6d1f7c4ab 100644 --- a/test/test-mongocrypt-ctx-setopt.c +++ b/test/test-mongocrypt-ctx-setopt.c @@ -925,16 +925,15 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { "'rangePreview' is deprecated"); } - /* It is an error to set range opts with index_type == - * MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW */ + /* It is an error to set range opts with the 'string' algorithm */ { REFRESH; /* Set key ID to get past the 'either key id or key alt name required' * error */ ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_range(ctx, rangeopts), ctx); - ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set range opts with textPreview index type"); + ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_STRING_STR, -1), ctx); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set range opts with 'string' algorithm"); } /* If query type == algorithm == "range", succeeds. */ @@ -971,7 +970,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set text opts without textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string or textPreview index type"); } /* It is an error to set text opts with index_type == @@ -983,7 +982,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set text opts without textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string or textPreview index type"); } /* It is an error to set text opts with index_type == @@ -995,7 +994,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set text opts without textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string or textPreview index type"); } /* It is an error to set a text query_type with index_type != @@ -1015,7 +1014,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIX_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires string index type"); REFRESH; /* Set key ID to get past the 'either key id or key alt name required' @@ -1023,7 +1022,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIX_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires string index type"); REFRESH; /* Set key ID to get past the 'either key id or key alt name required' @@ -1031,7 +1030,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_PREFIX_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "prefix query type requires string index type"); REFRESH; /* Set key ID to get past the 'either key id or key alt name required' @@ -1039,7 +1038,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUFFIX_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "suffix query type requires string index type"); } // Can't use "prefixPreview" or "suffixPreview". @@ -1061,24 +1060,24 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { "'suffixPreview' is deprecated"); } - /* It is an error to set a text algorithm without setting text options */ + /* It is an error to set a string algorithm without setting text options */ { REFRESH; /* Set key ID to get past the 'either key id or key alt name required' error */ ASSERT_KEY_ID_OK(uuid); - ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR, -1), ctx); + ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_STRING_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_contention_factor(ctx, 0), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "text opts are required for textPreview algorithm"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "string opts are required for string algorithm"); } - /* It is an error to set a text algorithm without setting contention */ + /* It is an error to set a string algorithm without setting contention */ { REFRESH; /* Set key ID to get past the 'either key id or key alt name required' error */ ASSERT_KEY_ID_OK(uuid); - ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR, -1), ctx); + ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_STRING_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "contention factor is required for textPreview algorithm"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "contention factor is required for string algorithm"); } mongocrypt_ctx_destroy(ctx); From ac76b817f75ae4e4568495e6400b75505cda7b98 Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Thu, 28 May 2026 11:29:21 -0400 Subject: [PATCH 3/4] fix pymongocrypt tests --- bindings/python/test/test_mongocrypt.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/python/test/test_mongocrypt.py b/bindings/python/test/test_mongocrypt.py index 6a3be5cb9..c49f18b3b 100644 --- a/bindings/python/test/test_mongocrypt.py +++ b/bindings/python/test/test_mongocrypt.py @@ -1030,9 +1030,9 @@ async def test_text_query(self): value = bson.encode({"v": "foo"}) encrypted = await encrypter.encrypt( value, - "textPreview", + "string", key_id=key_id, - query_type="suffixPreview", + query_type="suffix", contention_factor=0, text_opts=text_opts, ) @@ -1500,9 +1500,9 @@ def test_text_query(self): value = bson.encode({"v": "foo"}) encrypted = encrypter.encrypt( value, - "textPreview", + "string", key_id=key_id, - query_type="suffixPreview", + query_type="suffix", contention_factor=0, text_opts=text_opts, ) From f1df06ed5b23e6637221ace16f90fb65fad844dc Mon Sep 17 00:00:00 2001 From: Kevin Albertson Date: Fri, 29 May 2026 14:23:20 -0400 Subject: [PATCH 4/4] drop textPreview algorithm --- CHANGELOG.md | 2 +- src/mongocrypt-ctx-encrypt.c | 22 ++++--------- src/mongocrypt-ctx.c | 4 --- src/mongocrypt-private.h | 3 +- src/mongocrypt.h | 8 ++--- test/test-mongocrypt-ctx-encrypt.c | 53 ++++++------------------------ test/test-mongocrypt-ctx-setopt.c | 10 +++--- 7 files changed, 27 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 743275bb5..0c4558b73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Stable support for prefix and suffix queries: - The `prefixPreview` query type is replaced with `prefix`. - The `suffixPreview` query type is replaced with `suffix`. - - Use the `string` algorithm (formerly `textPreview`) for `prefix` and `suffix` query types. + - Use the `string` algorithm (formerly `textPreview`) for `prefix`, `suffix`, and `substringPreview` query types. ### Removed - Support for the experimental `prefixPreview` and `suffixPreview` query types. diff --git a/src/mongocrypt-ctx-encrypt.c b/src/mongocrypt-ctx-encrypt.c index 5d6b140f1..5d967db36 100644 --- a/src/mongocrypt-ctx-encrypt.c +++ b/src/mongocrypt-ctx-encrypt.c @@ -1539,7 +1539,6 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t * goto fail; // fallthrough case MONGOCRYPT_INDEX_TYPE_RANGE: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_RANGE; break; - case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: case MONGOCRYPT_INDEX_TYPE_STRING: marking.u.fle2.algorithm = MONGOCRYPT_FLE2_ALGORITHM_TEXT_SEARCH; break; default: // This might be unreachable because of other validation. Better safe than @@ -1632,7 +1631,6 @@ static bool _fle2_finalize_explicit(mongocrypt_ctx_t *ctx, mongocrypt_binary_t * case MONGOCRYPT_INDEX_TYPE_EQUALITY: case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED: case MONGOCRYPT_INDEX_TYPE_RANGE: - case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: case MONGOCRYPT_INDEX_TYPE_STRING: // All QE indexed algorithms require contention factor. BSON_ASSERT(ctx->opts.contention_factor.set); // Checked earlier in explicit_encrypt_init. @@ -2100,8 +2098,8 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms } } if (qt == MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW) { - if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW)) { - return _mongocrypt_ctx_fail_w_msg(ctx, "substringPreview query type requires textPreview index type"); + if (!(ctx->opts.index_type.set && ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) { + return _mongocrypt_ctx_fail_w_msg(ctx, "substringPreview query type requires string index type"); } } } @@ -2115,18 +2113,14 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with equality index type"); } - if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW) { - return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with 'textPreview' algorithm"); - } if (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING) { return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set range opts with 'string' algorithm"); } } if (ctx->opts.textopts.set && ctx->opts.index_type.set) { - if (ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW - && ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_STRING) { - return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set string opts without string or textPreview index type"); + if (ctx->opts.index_type.value != MONGOCRYPT_INDEX_TYPE_STRING) { + return _mongocrypt_ctx_fail_w_msg(ctx, "cannot set string opts without string index type"); } } @@ -2153,9 +2147,7 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms } // Check required options for text algorithm are set: - if (ctx->opts.index_type.set - && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW - || ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) { + if (ctx->opts.index_type.set && (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING)) { if (!ctx->opts.contention_factor.set) { return _mongocrypt_ctx_fail_w_msg(ctx, "contention factor is required for string algorithm"); } @@ -2190,10 +2182,8 @@ static bool explicit_encrypt_init(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *ms // fallthrough case MONGOCRYPT_QUERY_TYPE_PREFIX: case MONGOCRYPT_QUERY_TYPE_SUFFIX: - matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING); - break; case MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW: - matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW); + matches = (ctx->opts.index_type.value == MONGOCRYPT_INDEX_TYPE_STRING); break; default: CLIENT_ERR("unsupported value for query_type: %d", (int)ctx->opts.query_type.value); diff --git a/src/mongocrypt-ctx.c b/src/mongocrypt-ctx.c index 5082dde78..ab8009214 100644 --- a/src/mongocrypt-ctx.c +++ b/src/mongocrypt-ctx.c @@ -245,9 +245,6 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR))) { _mongocrypt_ctx_fail_w_msg(ctx, "Algorithm 'rangePreview' is deprecated, please use 'range'"); return false; - } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR))) { - ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW; - ctx->opts.index_type.set = true; } else if (mstr_eq_ignore_case(algo_str, mstrv_lit(MONGOCRYPT_ALGORITHM_STRING_STR))) { ctx->opts.index_type.value = MONGOCRYPT_INDEX_TYPE_STRING; ctx->opts.index_type.set = true; @@ -1057,7 +1054,6 @@ const char *_mongocrypt_index_type_to_string(mongocrypt_index_type_t val) { case MONGOCRYPT_INDEX_TYPE_EQUALITY: return "Equality"; case MONGOCRYPT_INDEX_TYPE_RANGE: return "Range"; case MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED: return "RangePreview"; - case MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW: return "TextPreview"; case MONGOCRYPT_INDEX_TYPE_STRING: return "String"; default: return "Unknown"; } diff --git a/src/mongocrypt-private.h b/src/mongocrypt-private.h index cd3b16bf1..b1717fbab 100644 --- a/src/mongocrypt-private.h +++ b/src/mongocrypt-private.h @@ -156,8 +156,7 @@ typedef enum { MONGOCRYPT_INDEX_TYPE_EQUALITY = 2, MONGOCRYPT_INDEX_TYPE_RANGE = 3, MONGOCRYPT_INDEX_TYPE_RANGEPREVIEW_DEPRECATED = 4, - MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW = 5, - MONGOCRYPT_INDEX_TYPE_STRING = 6, + MONGOCRYPT_INDEX_TYPE_STRING = 5, } mongocrypt_index_type_t; bool _mongocrypt_validate_and_copy_string(const char *in, int32_t in_len, char **out) MONGOCRYPT_WARN_UNUSED_RESULT; diff --git a/src/mongocrypt.h b/src/mongocrypt.h index b32cde422..052f08bcb 100644 --- a/src/mongocrypt.h +++ b/src/mongocrypt.h @@ -716,8 +716,8 @@ bool mongocrypt_ctx_setopt_algorithm(mongocrypt_ctx_t *ctx, const char *algorith // DEPRECATED: support "RangePreview" has been removed in favor of "range". #define MONGOCRYPT_ALGORITHM_RANGEPREVIEW_DEPRECATED_STR "RangePreview" #define MONGOCRYPT_ALGORITHM_RANGE_STR "Range" -/// NOTE: "textPreview" is experimental only and may be removed in a future non-major release. -#define MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR "textPreview" +/// DEPRECATED: "textPreview" has been removed. Use "string". +#define MONGOCRYPT_ALGORITHM_TEXTPREVIEW_DEPRECATED_STR "textPreview" // String constant for setopt_algorithm "string" explicit encryption. #define MONGOCRYPT_ALGORITHM_STRING_STR "string" @@ -1557,9 +1557,9 @@ MONGOCRYPT_EXPORT bool mongocrypt_ctx_setopt_algorithm_range(mongocrypt_ctx_t *ctx, mongocrypt_binary_t *opts); /** - * Set options for explicit encryption with the "textPreview" algorithm or "string" algorithm. + * Set options for explicit encryption with the "string" algorithm. * - * NOTE: "textPreview" is experimental only and may be removed in a future non-major release. + * NOTE: Use of the "substringPreview" query type is experimental only and may be removed in a future non-major release. * @p opts is a BSON document of the form: * { * "caseSensitive": bool, diff --git a/test/test-mongocrypt-ctx-encrypt.c b/test/test-mongocrypt-ctx-encrypt.c index faf4a7f4e..eb7dd2a01 100644 --- a/test/test-mongocrypt-ctx-encrypt.c +++ b/test/test-mongocrypt-ctx-encrypt.c @@ -2549,7 +2549,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { { ee_testcase tc = {0}; tc.desc = "insert substring"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.query_type = MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); @@ -2610,7 +2610,7 @@ static void _test_encrypt_fle2_explicit(_mongocrypt_tester_t *tester) { #include "./data/fle2-insert-text-search/RNG_DATA.h" tc.rng_data = (_test_rng_data_source){.buf = {.data = (uint8_t *)RNG_DATA, .len = sizeof(RNG_DATA) - 1}}; #undef RNG_DATA - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; + tc.algorithm = MONGOCRYPT_ALGORITHM_STRING_STR; tc.contention_factor = OPT_I64(1); tc.msg = TEST_BSON("{'v': 'abc'}"); tc.user_key_id = &keyABC_id; @@ -4484,48 +4484,15 @@ static void _test_prefixPreview_suffixPreview_fails(_mongocrypt_tester_t *tester #undef TF } -// `_test_textPreview_fails` tests that using "textPreview" algorithm with suffix or prefix errors. +// `_test_textPreview_fails` tests that using "textPreview" algorithm errors. static void _test_textPreview_fails(_mongocrypt_tester_t *tester) { - _mongocrypt_buffer_t keyABC_id; - _mongocrypt_buffer_copy_from_hex(&keyABC_id, "ABCDEFAB123498761234123456789012"); - - // textPreview with suffix query type fails. - { - ee_testcase tc = {0}; - tc.desc = "textPreview with suffix fails"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; - tc.query_type = MONGOCRYPT_QUERY_TYPE_SUFFIX_STR; - tc.contention_factor = OPT_I64(1); - tc.msg = TEST_BSON("{'v': 'abc'}"); - tc.user_key_id = &keyABC_id; - tc.text_opts = TEST_BSON(RAW_STRING({ - "caseSensitive" : true, - "diacriticSensitive" : true, - "suffix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100} - })); - tc.expect_init_error = "suffix query type requires string index type"; - ee_testcase_run(&tc); - } - - // textPreview with prefix query type fails. - { - ee_testcase tc = {0}; - tc.desc = "textPreview with prefix fails"; - tc.algorithm = MONGOCRYPT_ALGORITHM_TEXTPREVIEW_STR; - tc.query_type = MONGOCRYPT_QUERY_TYPE_PREFIX_STR; - tc.contention_factor = OPT_I64(1); - tc.msg = TEST_BSON("{'v': 'abc'}"); - tc.user_key_id = &keyABC_id; - tc.text_opts = TEST_BSON(RAW_STRING({ - "caseSensitive" : true, - "diacriticSensitive" : true, - "prefix" : {"strMinQueryLength" : 1, "strMaxQueryLength" : 100} - })); - tc.expect_init_error = "prefix query type requires string index type"; - ee_testcase_run(&tc); - } - - _mongocrypt_buffer_cleanup(&keyABC_id); + mongocrypt_t *crypt = _mongocrypt_tester_mongocrypt(TESTER_MONGOCRYPT_DEFAULT); + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + ASSERT_FAILS(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_TEXTPREVIEW_DEPRECATED_STR, -1), + ctx, + "unsupported algorithm"); + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); } // `autoencryption_test` defines a test for the automatic encryption context. diff --git a/test/test-mongocrypt-ctx-setopt.c b/test/test-mongocrypt-ctx-setopt.c index 6d1f7c4ab..ecd955c57 100644 --- a/test/test-mongocrypt-ctx-setopt.c +++ b/test/test-mongocrypt-ctx-setopt.c @@ -970,7 +970,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_UNINDEXED_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string or textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string index type"); } /* It is an error to set text opts with index_type == @@ -982,7 +982,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_INDEXED_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string or textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string index type"); } /* It is an error to set text opts with index_type == @@ -994,11 +994,11 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_algorithm_text(ctx, textopts), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string or textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "cannot set string opts without string index type"); } /* It is an error to set a text query_type with index_type != - * MONGOCRYPT_INDEX_TYPE_TEXTPREVIEW */ + * MONGOCRYPT_INDEX_TYPE_STRING */ { REFRESH; /* Set key ID to get past the 'either key id or key alt name required' @@ -1006,7 +1006,7 @@ static void _test_setopt_for_explicit_encrypt(_mongocrypt_tester_t *tester) { ASSERT_KEY_ID_OK(uuid); ASSERT_OK(mongocrypt_ctx_setopt_query_type(ctx, MONGOCRYPT_QUERY_TYPE_SUBSTRINGPREVIEW_STR, -1), ctx); ASSERT_OK(mongocrypt_ctx_setopt_algorithm(ctx, MONGOCRYPT_ALGORITHM_RANGE_STR, -1), ctx); - ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "substringPreview query type requires textPreview index type"); + ASSERT_EX_ENCRYPT_INIT_FAILS(bson, "substringPreview query type requires string index type"); REFRESH; /* Set key ID to get past the 'either key id or key alt name required'