Repository navigation
hasmember: one rule, searched by brute force as in DID-python (#236) - #239
Merged
Merged
Conversation
query_struct_to_sql_str put a hasmember value into the SQL unquoted, and
also compared it as a double-quoted token. A text value was then read as
SQL: 'name17' as a column (no such column), 'value' as doc_data.value
(every document matched) and '1 OR 1=1' as an expression (SQL
injection). The double-quoted test had the same problem for "value".
The list-member regex and the whole-value test now use single-quoted
literals (sqlStringList). doc2sql stores a number as a number, which never
equals a text literal, so a finite numeric value is also compared as a
number, written by sprintf('%.17g') so it can only be a numeric literal.
TestHasMemberQuoting compares db.search with fieldsearch for text values
(including 'value', '1 OR 1=1' and values holding " and '), numbers and
~hasmember, alone, inside an AND (restricted to candidates) and inside an
OR (searched in a batch).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7XsYS2DKSy2ucFzEEWR3D
Contributor
CI showed that fieldsearch is no oracle for a text hasmember: ismember(param1, value) compares two char arrays character by character, and the logical array it returns is negated and ANDed element by element, so '~hasmember value' matched only names sharing no character with 'value' and an AND matched any name sharing one. The SQL results in those runs were the intended ones. The expected documents now come from matches/memberOf: hasmember holds when the value is the whole field or an item of its comma-separated list, and every other operation is still tested by fieldsearch. Number searches are also compared with fieldsearch directly, which agrees for them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C7XsYS2DKSy2ucFzEEWR3D
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #239 +/- ##
==========================================
+ Coverage 67.40% 67.53% +0.13%
==========================================
Files 67 67
Lines 4807 4821 +14
==========================================
+ Hits 3240 3256 +16
+ Misses 1567 1565 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
fieldsearch tested hasmember with ismember(param1, value), which compares two texts character by character and returns a logical array, so it matched another rule than the SQL search did (and DID-python's field_search, a substring test, a third). All three now match one rule, in a new local function fieldsearch/hasMember: - text is a member of text that is the whole value or an item of its comma-separated list (white space around the comma ignored); - anything is a member of a cell array that holds an equal element, and a number of a numeric or logical array that holds it; - text is never a member of a number, nor a number of text. query_struct_to_sql_str matches the same rule: a text member is tested only against text values (typeof), with the pattern (^|,\s*)M\s*(,|$), and a numeric member only against numbers. It no longer extends the field to its subfields (fields.field_name like 'field.%'), so a struct has no members, as in fieldsearch. TestHasMemberQuoting now compares db.search with fieldsearch again, and testRule pins fieldsearch to a table of cases shared with DID-python. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C7XsYS2DKSy2ucFzEEWR3D
hasmember Inf / -Inf (written as 9e999 in the SQL) and NaN (a member of nothing) match no document, and ~hasmember matches every one; this covers the two SQL branches the other cases did not reach. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C7XsYS2DKSy2ucFzEEWR3D
doc2sql stores an array or cell of more than one element only as a summary
of its size and class ('[1 3] double'), so the SQL search could not see the
members of such fields: hasmember missed them and ~hasmember matched them.
search_doc_ids now tests every hasmember and ~hasmember term with
fieldsearch (brute_force_search), as DID-python's SQLiteDB does, so both
languages apply the same rule the same way and no hasmember value goes into
SQL. The new isBruteForceTerm names the terms searched this way (hasmember,
and the depends_on queries isBruteForceDependsOn already sent there);
search_doc_ids still searches them last in an AND, against the candidates
the other terms leave, and search_or_operands keeps them out of its
batches. query_struct_to_sql_str's 'hasmember' case is now an error, as no
search reaches it.
Tests: TestHasMemberQuoting checks that an AND loads only its candidates,
that an OR of hasmembers runs no SQL search, and covers array and cell
fields and a missing field; TestBatchedOr moves hasmember from its SQL
terms to a brute-force-in-an-OR test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7XsYS2DKSy2ucFzEEWR3D
stevewds
approved these changes
Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #236.
Problem
query_struct_to_sql_strput a texthasmembervalue into the SQL unquoted:'name17'→no such column'value'→ the columndoc_data.value, so every document matched'1 OR 1=1'→ injection, every document matched"or'→ syntax errordoc2sqlstores an array or cell with more than one element only as a summary, e.g.'[1 3] double'. So in SQL,hasmember 2missed[1 2 3], and~hasmember 2matched it.fieldsearchusedismember, which compares two texts character by character. It returns a logical array, and~/AND then treat that array element by element.field_searchtested for a substring.Fix
One rule (
src/did/+did/+datastructures/fieldsearch.m)hasmembernow calls a new local function,hasMember:(^|,\s*)M\s*(,|$), withMescaped)DID-python's
field_searchgets the same rule (VH-Lab/DID-python#108).Brute force, as DID-python (
src/did/+did/database.m)isBruteForceTerm, is true for everyhasmember/~hasmember, and for thedepends_onqueriesisBruteForceDependsOnalready sent to brute force.search_doc_idssends those terms tobrute_force_search, which tests documents one by one withfieldsearch. They are still searched last in an AND, against the candidates the other terms leave.search_or_operandskeeps them out of its batches.query_struct_to_sql_str'shasmembercase is now an error, since no search reaches it. Nohasmembervalue goes into SQL.This is what DID-python's
SQLiteDBalready does:_query_struct_to_sql_strreturnsNoneforhasmember. The two languages now apply the same rule in the same way. Cost: ahasmemberthat isn't narrowed by an AND (to at mostmaxSearchCandidates) loads every document of the branch, as in Python.main(#238, the #237~orfix) is merged in, with no conflict.Tests
TestHasMemberQuoting(new)testRule:fieldsearchagainst a table of 19 value/member cases, each also negated. The same table is in DID-python'stest_rule.db.searchcompared withfieldsearchfor:'value','1 OR 1=1','say "hi"','O''Brien','x, value','name17','zzz','5','')~hasmember5in the text'5').~hasmembermatches every document.[1 2 3],{'alpha','beta'}), which SQL could not search.TestBatchedOrhasmembermoves out of the SQL-term list (allTerms) into a newtestBruteForceHasMemberInAnOr, next to the brute-forceddepends_ontest.Related
74ae15f1(database) andf4ed409e(fieldsearch).main.🤖 Generated with Claude Code
https://claude.ai/code/session_01C7XsYS2DKSy2ucFzEEWR3D