Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java (date 1421225451000) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/BasicDocumentStoreTest.java (revision ) @@ -37,6 +37,7 @@ import java.util.Set; import java.util.UUID; +import com.google.common.base.Charsets; import org.apache.jackrabbit.oak.plugins.document.cache.CachingDocumentStore; import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; import org.junit.Assume; @@ -187,9 +188,6 @@ @Test public void testInterestingStrings() { - // TODO see OAK-1913 - Assume.assumeTrue(!(super.dsname.equals("RDB-MySQL"))); - String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", "tab:a\tb", "nul:a\u0000b" }; @@ -656,7 +654,7 @@ PreparedStatement stmt = connection.prepareStatement("insert into " + table + " (ID, MODCOUNT, DATA) values (?, ?, ?)"); try { - stmt.setString(1, key); + stmt.setBytes(1, key.getBytes(Charsets.UTF_8)); stmt.setLong(2, 0); stmt.setString(3, "X"); stmt.executeUpdate(); @@ -686,12 +684,12 @@ long cnt = 0; byte bdata[] = new byte[65536]; String sdata = appendString; - boolean needsConcat = super.dsname.contains("MySQL"); - int dataInChars = (super.dsname.contains("Oracle") ? 4000 : 16384); + boolean needsConcat = super.dsname.contains(DocumentStoreFixture.RDB_MYSQL.getName()); + int dataInChars = ((super.dsname.contains(DocumentStoreFixture.RDB_ORACLE.getName()) || + super.dsname.contains(DocumentStoreFixture.RDB_MSSQL.getName())) ? 4000 : 16384); int dataInBytes = dataInChars / 3; while (System.currentTimeMillis() < end) { - try { connection = super.rdbDataSource.getConnection(); connection.setAutoCommit(false); @@ -700,7 +698,7 @@ PreparedStatement stmt = connection.prepareStatement("update " + table + " set MODCOUNT = ? where ID = ?"); try { stmt.setLong(1, cnt); - stmt.setString(2, key); + stmt.setBytes(2, key.getBytes(Charsets.UTF_8)); assertEquals(1, stmt.executeUpdate()); connection.commit(); } finally { @@ -712,7 +710,7 @@ try { stmt.setLong(1, cnt); stmt.setString(2, "JSON data " + UUID.randomUUID()); - stmt.setString(3, key); + stmt.setBytes(3, key.getBytes(Charsets.UTF_8)); assertEquals(1, stmt.executeUpdate()); connection.commit(); } finally { @@ -727,7 +725,7 @@ bdata[(int) cnt % bdata.length] = (byte) (cnt & 0xff); stmt.setString(2, "JSON data " + UUID.randomUUID()); stmt.setBytes(3, bdata); - stmt.setString(4, key); + stmt.setBytes(4, key.getBytes(Charsets.UTF_8)); assertEquals(1, stmt.executeUpdate()); connection.commit(); } finally { @@ -735,13 +733,19 @@ } } else if (mode == 3) { PreparedStatement stmt = connection.prepareStatement("update " - + table - + " set " + + table + + " set " - + (needsConcat ? "DATA = CONCAT(DATA, ?)" : "DATA = DATA || CAST(? as varchar(" + dataInChars - + "))") + " where ID = ?"); + + (super.dsname.contains(DocumentStoreFixture.RDB_MSSQL.getName()) ? + "DATA = CASE WHEN LEN(DATA) <= " + (dataInChars - appendString.length()) + + " THEN (DATA + CAST(? AS nvarchar(" + dataInChars + ")))" + + " ELSE DATA + CAST(DATA AS nvarchar(max)) END " : + (needsConcat ? + "DATA = CONCAT(DATA, ?)" : + "DATA = DATA || CAST(? as varchar(" + dataInChars + "))")) + + " where ID = ?"); try { stmt.setString(1, appendString); - stmt.setString(2, key); + stmt.setBytes(2, key.getBytes(Charsets.UTF_8)); assertEquals(1, stmt.executeUpdate()); connection.commit(); expect.append(appendString); @@ -753,7 +757,7 @@ stmt = connection.prepareStatement("update " + table + " set MODCOUNT = MODCOUNT + 1, DATA = ? where ID = ?"); stmt.setString(1, "X"); - stmt.setString(2, key); + stmt.setBytes(2, key.getBytes(Charsets.UTF_8)); assertEquals(1, stmt.executeUpdate()); connection.commit(); expect = new StringBuffer("X"); @@ -782,7 +786,7 @@ stmt.setString(si++, "null"); stmt.setBytes(si++, sdata.getBytes("UTF-8")); } - stmt.setString(si++, key); + stmt.setBytes(si++, key.getBytes(Charsets.UTF_8)); assertEquals(1, stmt.executeUpdate()); connection.commit(); sdata += appendString; @@ -813,7 +817,7 @@ connection.setAutoCommit(false); PreparedStatement stmt = connection.prepareStatement("select DATA, MODCOUNT from " + table + " where ID = ?"); try { - stmt.setString(1, key); + stmt.setBytes(1, key.getBytes(Charsets.UTF_8)); ResultSet rs = stmt.executeQuery(); assertTrue(rs.next()); String got = rs.getString(1); Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentStoreTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentStoreTest.java (date 1421225451000) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/AbstractDocumentStoreTest.java (revision ) @@ -80,7 +80,7 @@ Collection result = new ArrayList(); DocumentStoreFixture candidates[] = new DocumentStoreFixture[] { DocumentStoreFixture.MEMORY, DocumentStoreFixture.MONGO, DocumentStoreFixture.RDB_H2, DocumentStoreFixture.RDB_PG, DocumentStoreFixture.RDB_DB2, - DocumentStoreFixture.RDB_MYSQL, DocumentStoreFixture.RDB_ORACLE }; + DocumentStoreFixture.RDB_MYSQL, DocumentStoreFixture.RDB_ORACLE, DocumentStoreFixture.RDB_MSSQL }; for (DocumentStoreFixture dsf : candidates) { if (dsf.isAvailable()) { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java (date 1421225451000) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStore.java (revision ) @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.oak.plugins.document.rdb; +import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkNotNull; import java.io.ByteArrayOutputStream; @@ -315,7 +316,7 @@ private static final String MODCOUNT = "_modCount"; /** - * Optional counter for changes to {@link #COLLISIONS} map. + * Optional counter for changes to {#NodeDocument.COLLISIONS} map. */ private static final String COLLISIONSMODCOUNT = "_collisionsModCount"; @@ -434,15 +435,21 @@ + tableName + " (ID varchar(512) not null primary key, MODIFIED bigint, HASBINARY smallint, MODCOUNT bigint, CMODCOUNT bigint, DSIZE bigint, DATA varchar(16384), BDATA blob)"); } else if ("MySQL".equals(dbtype)) { + this.dataLimitInOctets = 12288; stmt.execute("create table " + tableName - + " (ID varchar(512) not null primary key, MODIFIED bigint, HASBINARY smallint, MODCOUNT bigint, CMODCOUNT bigint, DSIZE bigint, DATA varchar(16384), BDATA mediumblob)"); + + " (ID varbinary(512) not null primary key, MODIFIED bigint, HASBINARY smallint, MODCOUNT bigint, CMODCOUNT bigint, DSIZE bigint, DATA varchar(12288), BDATA mediumblob)"); } else if ("Oracle".equals(dbtype)) { // see https://issues.apache.org/jira/browse/OAK-1914 this.dataLimitInOctets = 4000; stmt.execute("create table " + tableName + " (ID varchar(512) not null primary key, MODIFIED number, HASBINARY number, MODCOUNT number, CMODCOUNT number, DSIZE number, DATA varchar(4000), BDATA blob)"); + } else if ("Microsoft SQL Server".equals(dbtype)) { + this.dataLimitInOctets = 4000; + stmt.execute("create table " + + tableName + + " (ID varbinary(512) not null primary key, MODIFIED bigint, HASBINARY smallint, MODCOUNT bigint, CMODCOUNT bigint, DSIZE bigint, DATA nvarchar(4000), BDATA varbinary(max))"); } else { stmt.execute("create table " + tableName @@ -937,12 +944,7 @@ private static byte[] asBytes(String data) { byte[] bytes; - try { - bytes = data.getBytes("UTF-8"); - } catch (UnsupportedEncodingException ex) { - LOG.error("UTF-8 not supported??", ex); - throw new DocumentStoreException(ex); - } + bytes = data.getBytes(UTF_8); if (NOGZIP) { return bytes; @@ -984,12 +986,12 @@ try { if (useCaseStatement) { - stmt.setString(1, id); + stmt.setBytes(1, id.getBytes(UTF_8)); } else { stmt.setLong(1, lastmodcount); stmt.setLong(2, lastmodcount); - stmt.setString(3, id); + stmt.setBytes(3, id.getBytes(UTF_8)); } ResultSet rs = stmt.executeQuery(); if (rs.next()) { @@ -1020,7 +1022,7 @@ private List dbQuery(Connection connection, String tableName, String minId, String maxId, String indexedProperty, long startValue, int limit) throws SQLException { - String t = "select ID, MODIFIED, MODCOUNT, CMODCOUNT, HASBINARY, DATA, BDATA from " + tableName + " where ID > ? and ID < ?"; + String t = "select %s ID, MODIFIED, MODCOUNT, CMODCOUNT, HASBINARY, DATA, BDATA from " + tableName + " where ID > ? and ID < ?"; if (indexedProperty != null) { if (MODIFIED.equals(indexedProperty)) { t += " and MODIFIED >= ?"; @@ -1033,14 +1035,23 @@ } t += " order by ID"; if (limit != Integer.MAX_VALUE) { - t += this.needsLimit ? (" LIMIT " + limit) : (" FETCH FIRST " + limit + " ROWS ONLY"); + if (connection.getMetaData().getDatabaseProductName().equals("Microsoft SQL Server")) { + t = String.format(t, "TOP " + limit); + } else { + t = String.format(t, ""); + t += this.needsLimit ? (" LIMIT " + limit) : (" FETCH FIRST " + limit + " ROWS " + + "ONLY"); - } + } + } else { + t = String.format(t, ""); + } + PreparedStatement stmt = connection.prepareStatement(t); List result = new ArrayList(); try { int si = 1; - stmt.setString(si++, minId); - stmt.setString(si++, maxId); + stmt.setBytes(si++, minId.getBytes(UTF_8)); + stmt.setBytes(si++, maxId.getBytes(UTF_8)); if (MODIFIED.equals(indexedProperty)) { stmt.setLong(si++, startValue); } @@ -1049,7 +1060,7 @@ } ResultSet rs = stmt.executeQuery(); while (rs.next() && result.size() < limit) { - String id = rs.getString(1); + String id = new String(rs.getBytes(1), UTF_8); if (id.compareTo(minId) < 0 || id.compareTo(maxId) > 0) { throw new DocumentStoreException("unexpected query result: '" + minId + "' < '" + id + "' < '" + maxId + "' - broken DB collation?"); } @@ -1087,11 +1098,11 @@ stmt.setBinaryStream(si++, null, 0); } else { stmt.setString(si++, "\"blob\""); - byte[] bytes = asBytes(data); + byte[] bytes = data.getBytes(UTF_8); stmt.setBytes(si++, bytes); } - stmt.setString(si++, id); + stmt.setBytes(si++, id.getBytes(UTF_8)); if (oldmodcount != null) { stmt.setObject(si++, oldmodcount, Types.BIGINT); } @@ -1108,9 +1119,19 @@ private boolean dbAppendingUpdate(Connection connection, String tableName, String id, Long modified, Boolean hasBinary, Long modcount, Long cmodcount, Long oldmodcount, String appendData) throws SQLException { StringBuilder t = new StringBuilder(); + if (connection.getMetaData().getDatabaseProductName().equals("Microsoft SQL Server")) { + t.append("update " + tableName + " set MODIFIED = (select MAX(mod) from (VALUES (MODIFIED), (?)) AS ALLMOD(mod)), HASBINARY = ?, MODCOUNT = ?, CMODCOUNT = ?, DSIZE = DSIZE + ?, "); + } else { - t.append("update " + tableName + " set MODIFIED = GREATEST(MODIFIED, ?), HASBINARY = ?, MODCOUNT = ?, CMODCOUNT = ?, DSIZE = DSIZE + ?, "); + t.append("update " + tableName + " set MODIFIED = GREATEST(MODIFIED, ?), HASBINARY = ?, MODCOUNT = ?, CMODCOUNT = ?, DSIZE = DSIZE + ?, "); + } + if (connection.getMetaData().getDatabaseProductName().equals("Microsoft SQL Server")) { + t.append("DATA = CASE WHEN LEN(DATA) <= " + (this.dataLimitInOctets - appendData.length()) + + " THEN (DATA + CAST(? AS nvarchar(" + this.dataLimitInOctets + ")))" + + " ELSE DATA + CAST(DATA AS nvarchar(max)) END "); + } else { - t.append(this.needsConcat ? "DATA = CONCAT(DATA, ?) " : "DATA = DATA || CAST(? AS varchar(" + this.dataLimitInOctets + t.append(this.needsConcat ? "DATA = CONCAT(DATA, ?) " : "DATA = DATA || CAST(? AS varchar(" + this.dataLimitInOctets + ")) "); + } t.append("where ID = ?"); if (oldmodcount != null) { t.append(" and MODCOUNT = ?"); @@ -1124,7 +1145,7 @@ stmt.setObject(si++, cmodcount == null ? 0 : cmodcount, Types.BIGINT); stmt.setObject(si++, 1 + appendData.length(), Types.BIGINT); stmt.setString(si++, "," + appendData); - stmt.setString(si++, id); + stmt.setBytes(si++, id.getBytes(UTF_8)); if (oldmodcount != null) { stmt.setObject(si++, oldmodcount, Types.BIGINT); } @@ -1141,9 +1162,21 @@ private boolean dbBatchedAppendingUpdate(Connection connection, String tableName, List ids, Long modified, String appendData) throws SQLException { StringBuilder t = new StringBuilder(); + if (connection.getMetaData().getDatabaseProductName().equals("Microsoft SQL Server")) { + t.append("update " + tableName + " set MODIFIED = (select MAX(mod) from (VALUES (MODIFIED), (?)) AS ALLMOD(mod)), MODCOUNT = MODCOUNT + 1, DSIZE = DSIZE + ?, "); + } else { - t.append("update " + tableName + " set MODIFIED = GREATEST(MODIFIED, ?), MODCOUNT = MODCOUNT + 1, DSIZE = DSIZE + ?, "); + t.append("update " + tableName + " set MODIFIED = GREATEST(MODIFIED, ?), MODCOUNT = MODCOUNT + 1, DSIZE = DSIZE + ?, "); - t.append(this.needsConcat ? "DATA = CONCAT(DATA, ?) " : "DATA = DATA || CAST(? AS varchar(" + this.dataLimitInOctets - + ")) "); + } + + if (connection.getMetaData().getDatabaseProductName().equals("Microsoft SQL Server")) { + t.append("DATA = CASE WHEN LEN(DATA) <= " + (this.dataLimitInOctets - appendData.length()) + + " THEN (DATA + CAST(? AS nvarchar(" + this.dataLimitInOctets + ")))" + + " ELSE DATA + CAST(DATA AS nvarchar(max)) END "); + } else { + t.append(this.needsConcat ? + "DATA = CONCAT(DATA, ?) " : + "DATA = DATA || CAST(? AS varchar(" + this.dataLimitInOctets + ")) "); + } t.append("where ID in ("); for (int i = 0; i < ids.size(); i++) { if (i != 0) { @@ -1159,7 +1192,7 @@ stmt.setObject(si++, 1 + appendData.length(), Types.BIGINT); stmt.setString(si++, "," + appendData); for (String id : ids) { - stmt.setString(si++, id); + stmt.setBytes(si++, id.getBytes(UTF_8)); } int result = stmt.executeUpdate(); if (result != ids.size()) { @@ -1178,7 +1211,7 @@ + "(ID, MODIFIED, HASBINARY, MODCOUNT, CMODCOUNT, DSIZE, DATA, BDATA) values (?, ?, ?, ?, ?, ?, ?, ?)"); try { int si = 1; - stmt.setString(si++, id); + stmt.setBytes(si++, id.getBytes(UTF_8)); stmt.setObject(si++, modified, Types.BIGINT); stmt.setObject(si++, hasBinary ? 1 : 0, Types.SMALLINT); stmt.setObject(si++, modcount, Types.BIGINT); @@ -1189,7 +1222,7 @@ stmt.setBinaryStream(si++, null, 0); } else { stmt.setString(si++, "\"blob\""); - byte[] bytes = asBytes(data); + byte[] bytes = data.getBytes(UTF_8); stmt.setBytes(si++, bytes); } int result = stmt.executeUpdate(); @@ -1222,7 +1255,7 @@ try { for (int i = 0; i < cnt; i++) { - stmt.setString(i + 1, ids.get(i)); + stmt.setBytes(i + 1, ids.get(i).getBytes(UTF_8)); } int result = stmt.executeUpdate(); if (result != cnt) { Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java (date 1421225451000) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java (revision ) @@ -41,6 +41,7 @@ public static final DocumentStoreFixture RDB_DB2 = new RDBFixture("RDB-DB2", "jdbc:db2://localhost:50000/OAK", "oak", "geheim"); public static final DocumentStoreFixture RDB_MYSQL = new RDBFixture("RDB-MySQL", "jdbc:mysql://localhost:3306/oak", "root", "geheim"); public static final DocumentStoreFixture RDB_ORACLE = new RDBFixture("RDB-Oracle", "jdbc:oracle:thin:@localhost:1521:orcl", "system", "geheim"); + public static final DocumentStoreFixture RDB_MSSQL = new RDBFixture("RDB-MSSql", "jdbc:sqlserver://localhost:1433;databaseName=oak", "oak", "geheimRoot1234"); public static final DocumentStoreFixture MONGO = new MongoFixture("mongodb://localhost:27017/oak"); public static final String TABLEPREFIX = "dstest_"; Index: oak-parent/pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-parent/pom.xml (date 1421225451000) +++ oak-parent/pom.xml (revision ) @@ -533,7 +533,7 @@ mysql mysql-connector-java - 5.1.34 + 5.1.30 @@ -554,6 +554,21 @@ com.h2database h2 ${h2.version} + + + + + + + rdb-mssql + + + com.microsoft.sqlserver + sqljdbc4 + 4.1