diff --git a/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/QuerySpec.java b/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/QuerySpec.java index 3880ae1bf3..772ccabc39 100644 --- a/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/QuerySpec.java +++ b/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/QuerySpec.java @@ -381,7 +381,7 @@ private String computeWhereClause() { case match: pred = computeFieldReference(SmartStore.SOUP_ENTRY_ID) + " IN (" + SELECT + SmartStore.ROWID_COL + " " + FROM + computeSoupFtsReference() + " " + WHERE - + computeSoupFtsReference() + " MATCH '" + qualifyMatchKey(field, matchKey) + "'" + + computeSoupFtsReference() + " MATCH '" + qualifyMatchKey(field, matchKey).replace("'", "''") + "'" // statement arg binding doesn't seem to work so inlining matchKey + ") "; break; diff --git a/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartSqlHelper.java b/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartSqlHelper.java index 73138f5492..a8c6f0d490 100644 --- a/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartSqlHelper.java +++ b/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartSqlHelper.java @@ -173,7 +173,7 @@ private String getColumnNameForPathForSmartSql(SQLiteDatabase db, String soupNam if (!indexed) { // Thanks to the json1 extension we can query the data even if it is not indexed - columnName = "json_extract(" + SmartStore.SOUP_COL + ", '$." + path + "')"; + columnName = "json_extract(" + SmartStore.SOUP_COL + ", '$." + path.replace("'", "''") + "')"; } else { try { columnName = DBHelper.getInstance(db).getColumnNameForPath(db, soupName, path); diff --git a/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartStore.java b/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartStore.java index f2862283a2..c7717b99c2 100644 --- a/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartStore.java +++ b/libs/SmartStore/src/com/salesforce/androidsdk/smartstore/store/SmartStore.java @@ -376,7 +376,7 @@ protected void registerSoupUsingTableName(String soupName, IndexSpec[] indexSpec // Column name or expression the db index is on String columnName = soupTableName + "_" + i; if (TypeGroup.value_indexed_with_json_extract.isMember(indexSpec.type)) { - columnName = "json_extract(" + SOUP_COL + ", '$." + indexSpec.path + "')"; + columnName = "json_extract(" + SOUP_COL + ", '$." + indexSpec.path.replace("'", "''") + "')"; } // for create table diff --git a/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/QuerySpecTest.java b/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/QuerySpecTest.java index 80e27a0251..41df515d07 100644 --- a/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/QuerySpecTest.java +++ b/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/QuerySpecTest.java @@ -166,6 +166,15 @@ public void testSmartQueryIdsSmartSql() { Assert.assertEquals("Wrong ids smart sql", "SELECT id FROM (select {employees:salary} from {employees} where {employees:lastName} = 'Haas')", querySpec.idsSmartSql); } + @Test + public void testMatchQuerySmartSqlWithSingleQuoteInMatchKey() { + // Single quotes in matchKey must be doubled so they don't break the surrounding MATCH '...' literal. + QuerySpec querySpec = QuerySpec.buildMatchQuerySpec("employees", "lastName", "O'Brien", "firstName", QuerySpec.Order.ascending, 1); + Assert.assertEquals("Wrong smart sql for match query spec with single quote in matchKey", + "SELECT {employees:_soup} FROM {employees} WHERE {employees:_soupEntryId} IN (SELECT rowid FROM {employees}_fts WHERE {employees}_fts MATCH '{employees:lastName}:O''Brien') ORDER BY {employees:firstName} ASC ", + querySpec.smartSql); + } + @Test public void testQualifyMatchKey() { Assert.assertEquals("Wrong qualified match query", "abc", QuerySpec.qualifyMatchKey(null, "abc")); diff --git a/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/SmartSqlTest.java b/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/SmartSqlTest.java index 087846ea06..f7fe554d08 100644 --- a/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/SmartSqlTest.java +++ b/libs/test/SmartStoreTest/src/com/salesforce/androidsdk/smartstore/store/SmartSqlTest.java @@ -231,6 +231,14 @@ public void testConvertSmartSqlForNonIndexedColumns() { store.convertSmartSql("select {employees:education}, {employees:address.zipcode} from {employees} where {employees:address.city} = 'San Francisco'")); } + @Test + public void testConvertSmartSqlForNonIndexedColumnWithSingleQuoteInPath() { + // Single quotes in non-indexed paths must be doubled so they don't break the surrounding json_extract(soup, '$.path') literal. + // The path with ' is in the WHERE clause so that the FROM {employees} token is resolved before any single-quote appears in beforeStr. + Assert.assertEquals("select TABLE_1_3 from TABLE_1 where json_extract(soup, '$.user''s.address') = 'foo'", + store.convertSmartSql("select {employees:employeeId} from {employees} where {employees:user's.address} = 'foo'")); + } + @Test public void testConvertSmartSqlWithQuotedCurlyBraces() { Assert.assertEquals("select json_extract(soup, '$.education') from TABLE_1 where json_extract(soup, '$.education') like 'Account(where: {Name: {eq: \"Jason\"}})'",