Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/main/java/org/z3950/zing/cql/CQLLexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class CQLLexer implements CQLTokenizer {
private String lval;
private StringBuilder buf = new StringBuilder();

static final String OPS_AND_WHITESPACE = "()/<>= \t\r\n";

public CQLLexer(String cql, boolean debug) {
qs = cql;
ql = cql.length();
Expand Down Expand Up @@ -89,8 +91,7 @@ public void move() {
} else {
what = TT_WORD;
buf.setLength(0); // reset buffer
while (qi < ql
&& !strchr("()/<>= \t\r\n", qs.charAt(qi))) {
while (qi < ql && !strchr(OPS_AND_WHITESPACE, qs.charAt(qi))) {
buf.append(qs.charAt(qi));
qi++;
}
Expand Down
35 changes: 19 additions & 16 deletions src/main/java/org/z3950/zing/cql/CQLTermNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,24 +223,27 @@ public String toPQF(Properties config) throws PQFTranslationException {
}

static String maybeQuote(String str) {
if (str == null)
if (str == null) {
Copy link
Copy Markdown
Contributor

@jakub-id jakub-id Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamdickmeiss this function name is confusing since it both quotes and escapes. Btw, we should do the same in cql-go.

Copy link
Copy Markdown
Contributor Author

@adamdickmeiss adamdickmeiss Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, the function name is bad.

There will never be a bare " as the result of a query in the current form. But it did happen in latest release until #5. This is just extra precaution to ensure that if it is bare, it will be escaped. That's all.

return null;

// There _must_ be a better way to make this test ...
if (str.length() == 0 ||
str.indexOf('"') != -1 ||
str.indexOf(' ') != -1 ||
str.indexOf('\t') != -1 ||
str.indexOf('=') != -1 ||
str.indexOf('<') != -1 ||
str.indexOf('>') != -1 ||
str.indexOf('/') != -1 ||
str.indexOf('(') != -1 ||
str.indexOf(')') != -1) {
str = '"' + str.replaceAll("(?<!\\\\)\"", "\\\\\"") + '"';
}

return str;
boolean quote = str.isEmpty();
boolean escaped = false;
StringBuilder sb = new StringBuilder();
for (char ch : str.toCharArray()) {
if (CQLLexer.OPS_AND_WHITESPACE.indexOf(ch) >= 0) {
quote = true;
}
if (ch == '"' && !escaped) {
sb.append('\\');
}
escaped = ch == '\\' && !escaped;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamdickmeiss what if \ is on the last position like in x\

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be left as is.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the resulting query will be invalid no?

sb.append(ch);
}
if (quote) {
return "\"" + sb.toString() + "\"";
} else {
return sb.toString();
}
}

@Override
Expand Down
52 changes: 52 additions & 0 deletions src/test/java/org/z3950/zing/cql/CQLTermNodeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.z3950.zing.cql;

import org.junit.Test;
import static org.junit.Assert.*;

public class CQLTermNodeTest {
@Test
public void TestMaybeQuoteNull() {
assertNull(CQLTermNode.maybeQuote(null));
}

@Test
public void TestMaybeQuoteEmpty() {
assertEquals("\"\"", CQLTermNode.maybeQuote(""));
}

@Test
public void TestMaybeQuoteRelation() {
assertEquals("\"<\"", CQLTermNode.maybeQuote("<"));
}

@Test
public void TestMaybeQuoteSimple() {
assertEquals("simple", CQLTermNode.maybeQuote("simple"));
}

@Test
public void TestMaybeQuoteBlank() {
assertEquals("\"a b\"", CQLTermNode.maybeQuote("a b"));
}

@Test
public void TestMaybeQuoteQuote1() {
assertEquals("a\\\"", CQLTermNode.maybeQuote("a\""));
}

@Test
public void TestMaybeQuoteQuote2() {
assertEquals("a\\\"", CQLTermNode.maybeQuote("a\\\""));
}

@Test
public void TestMaybeQuoteQuote3() {
assertEquals("a" + "\\\\" + "\\\"", CQLTermNode.maybeQuote("a" + "\\\\" + "\""));
}

@Test
public void TestMaybeQuoteQuote4() {
assertEquals("a" + "\\\\" + "\\\"", CQLTermNode.maybeQuote("a" + "\\\\" + "\\\""));
}

}