diff --git a/.changeset/fix-empty-string-params.md b/.changeset/fix-empty-string-params.md new file mode 100644 index 000000000..b2a8e65ee --- /dev/null +++ b/.changeset/fix-empty-string-params.md @@ -0,0 +1,5 @@ +--- +'@tanstack/electric-db-collection': patch +--- + +Fix empty string values being incorrectly omitted from SQL query params. Queries like `eq(column, '')` now correctly include the empty string parameter instead of producing a malformed query with a missing `$1` value. diff --git a/packages/electric-db-collection/src/sql-compiler.ts b/packages/electric-db-collection/src/sql-compiler.ts index d7d021c89..2593d0697 100644 --- a/packages/electric-db-collection/src/sql-compiler.ts +++ b/packages/electric-db-collection/src/sql-compiler.ts @@ -38,9 +38,9 @@ export function compileSQL(options: LoadSubsetOptions): SubsetParams { const paramsRecord = params.reduce( (acc, param, index) => { const serialized = serialize(param) - // Only include non-empty values in params - // Empty strings from null/undefined should be omitted - if (serialized !== ``) { + // Empty strings are valid query values (e.g., WHERE column = '') + // Only omit null/undefined values from params + if (param != null) { acc[`${index + 1}`] = serialized } return acc diff --git a/packages/electric-db-collection/tests/sql-compiler.test.ts b/packages/electric-db-collection/tests/sql-compiler.test.ts index 9989d47f9..5dc1cac59 100644 --- a/packages/electric-db-collection/tests/sql-compiler.test.ts +++ b/packages/electric-db-collection/tests/sql-compiler.test.ts @@ -68,6 +68,54 @@ describe(`sql-compiler`, () => { expect(result.where).toBe(`"rating" <= $1`) expect(result.params).toEqual({ '1': `5` }) }) + + // Regression test for https://github.com/TanStack/db/issues/1147 + it(`should compile eq with empty string value`, () => { + const result = compileSQL({ + where: func(`eq`, [ref(`status`), val(``)]), + }) + expect(result.where).toBe(`"status" = $1`) + expect(result.params).toEqual({ '1': `` }) + }) + + it(`should compile eq with empty string in AND clause`, () => { + const result = compileSQL({ + where: func(`and`, [ + func(`eq`, [ref(`projectId`), val(`uuid-123`)]), + func(`eq`, [ref(`status`), val(``)]), + ]), + }) + expect(result.where).toBe(`"projectId" = $1 AND "status" = $2`) + expect(result.params).toEqual({ '1': `uuid-123`, '2': `` }) + }) + + it(`should handle multiple empty strings with correct param indices`, () => { + const result = compileSQL({ + where: func(`and`, [ + func(`eq`, [ref(`field1`), val(``)]), + func(`eq`, [ref(`field2`), val(``)]), + ]), + }) + expect(result.where).toBe(`"field1" = $1 AND "field2" = $2`) + // Both empty strings should be present with correct indices + expect(result.params).toEqual({ '1': ``, '2': `` }) + }) + + it(`should compile like with empty string pattern`, () => { + const result = compileSQL({ + where: func(`like`, [ref(`description`), val(``)]), + }) + expect(result.where).toBe(`"description" LIKE $1`) + expect(result.params).toEqual({ '1': `` }) + }) + + it(`should compile ilike with empty string pattern`, () => { + const result = compileSQL({ + where: func(`ilike`, [ref(`title`), val(``)]), + }) + expect(result.where).toBe(`"title" ILIKE $1`) + expect(result.params).toEqual({ '1': `` }) + }) }) describe(`compound where clauses`, () => { diff --git a/packages/vue-db/tests/useLiveQuery.test-d.ts b/packages/vue-db/tests/useLiveQuery.test-d.ts index 514a2b913..df54ec2be 100644 --- a/packages/vue-db/tests/useLiveQuery.test-d.ts +++ b/packages/vue-db/tests/useLiveQuery.test-d.ts @@ -132,6 +132,8 @@ describe(`useLiveQuery type assertions`, () => { ) // Regular queries should return an array - expectTypeOf(data.value).toEqualTypeOf>() + expectTypeOf(data.value).toEqualTypeOf< + Array<{ id: string; name: string }> + >() }) })