Permalink
Browse files

feat: support 10+ version parsing

Note:
  10.2 means 10 major, 2 minor, that is 10_00_02
  9.2 means 9.2 major, that is 9_02_00

It is assumed that versions would be 10head -> 10 -> 10.1head -> ...
postgres/postgres@ca9112a

Relevant libpq parsing logic: postgres/postgres@69dc5ae

closes #631
  • Loading branch information...
vlsi committed Aug 28, 2016
1 parent 07c7902 commit a639431d41dd92d0467d4ca618a35a2b5dc1e2d8
@@ -28,6 +28,7 @@
v9_4("9.4.0"),
v9_5("9.5.0"),
v9_6("9.6.0"),
v10("10")
;
private final int version;
@@ -101,94 +102,76 @@ public String toString() {
* @return server version in number form
*/
static int parseServerVersionStr(String serverVersion) throws NumberFormatException {
int vers;
NumberFormat numformat = NumberFormat.getIntegerInstance();
numformat.setGroupingUsed(false);
ParsePosition parsepos = new ParsePosition(0);
Long parsed;
if (serverVersion == null) {
return 0;
}
/* Get first major version part */
parsed = (Long) numformat.parseObject(serverVersion, parsepos);
if (parsed == null) {
return 0;
int[] parts = new int[3];
int versionParts;
for (versionParts = 0; versionParts < 3; versionParts++) {
Number part = (Number) numformat.parseObject(serverVersion, parsepos);
if (part == null) {
break;
}
parts[versionParts] = part.intValue();
if (parsepos.getIndex() == serverVersion.length()
|| serverVersion.charAt(parsepos.getIndex()) != '.') {
break;
}
// Skip .
parsepos.setIndex(parsepos.getIndex() + 1);
}
if (parsed.intValue() >= 10000) {
versionParts++;
if (parts[0] >= 10000) {
/*
* PostgreSQL version 1000? I don't think so. We're seeing a version like 90401; return it
* verbatim, but only if there's nothing else in the version. If there is, treat it as a parse
* error.
*/
if (parsepos.getIndex() == serverVersion.length()) {
return parsed.intValue();
if (parsepos.getIndex() == serverVersion.length() && versionParts == 1) {
return parts[0];
} else {
throw new NumberFormatException(
"First major-version part equal to or greater than 10000 in invalid version string: "
+ serverVersion);
}
}
vers = parsed.intValue() * 10000;
/* Did we run out of string? */
if (parsepos.getIndex() == serverVersion.length()) {
return 0;
}
/* Skip the . */
if (serverVersion.charAt(parsepos.getIndex()) == '.') {
parsepos.setIndex(parsepos.getIndex() + 1);
} else {
/* Unexpected version format */
return 0;
}
/*
* Get second major version part. If this isn't purely an integer, accept the integer part and
* return with a minor version of zero, so we cope with 8.1devel, etc.
*/
parsed = (Long) numformat.parseObject(serverVersion, parsepos);
if (parsed == null) {
/*
* Failed to parse second part of minor version at all. Half a major version is useless,
* return 0.
*/
return 0;
}
if (parsed.intValue() > 99) {
throw new NumberFormatException(
"Unsupported second part of major version > 99 in invalid version string: "
+ serverVersion);
}
vers = vers + parsed.intValue() * 100;
/* Did we run out of string? Return just the major. */
if (parsepos.getIndex() == serverVersion.length()) {
return vers;
}
/* Skip the . */
if (serverVersion.charAt(parsepos.getIndex()) == '.') {
parsepos.setIndex(parsepos.getIndex() + 1);
} else {
/* Doesn't look like an x.y.z version, return what we have */
return vers;
if (versionParts == 3) {
if (parts[1] > 99) {
throw new NumberFormatException(
"Unsupported second part of major version > 99 in invalid version string: "
+ serverVersion);
}
if (parts[2] > 99) {
throw new NumberFormatException(
"Unsupported second part of minor version > 99 in invalid version string: "
+ serverVersion);
}
return (parts[0] * 100 + parts[1]) * 100 + parts[2];
}
/* Try to parse any remainder as a minor version */
parsed = (Long) numformat.parseObject(serverVersion, parsepos);
if (parsed != null) {
if (parsed.intValue() > 99) {
if (versionParts == 2) {
if (parts[0] >= 10) {
return parts[0] * 100 * 100 + parts[1];
}
if (parts[1] > 99) {
throw new NumberFormatException(
"Unsupported minor version value > 99 in invalid version string: " + serverVersion);
"Unsupported second part of major version > 99 in invalid version string: "
+ serverVersion);
}
vers = vers + parsed.intValue();
return (parts[0] * 100 + parts[1]) * 100;
}
return vers;
if (versionParts == 1) {
if (parts[0] >= 10) {
return parts[0] * 100 * 100;
}
}
return 0; /* unknown */
}
}
@@ -14,6 +14,8 @@
import org.postgresql.test.CursorFetchBinaryTest;
import org.postgresql.test.TestUtil;
import org.postgresql.test.core.NativeQueryBindLengthTest;
import org.postgresql.test.util.ServerVersionParseTest;
import org.postgresql.test.util.ServerVersionTest;
import junit.framework.JUnit4TestAdapter;
import junit.framework.TestSuite;
@@ -49,7 +51,8 @@ public static TestSuite suite() throws Exception {
suite.addTestSuite(EncodingTest.class);
suite.addTestSuite(ColumnSanitiserDisabledTest.class);
suite.addTestSuite(ColumnSanitiserEnabledTest.class);
suite.addTestSuite(VersionTest.class);
suite.addTest(new JUnit4TestAdapter(ServerVersionParseTest.class));
suite.addTest(new JUnit4TestAdapter(ServerVersionTest.class));
// Connectivity/Protocols

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,90 @@
package org.postgresql.test.util;
import org.postgresql.core.ServerVersion;
import org.postgresql.core.Version;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
@RunWith(Parameterized.class)
public class ServerVersionParseTest {
private final String versionString;
private final int versionNum;
private final String rejectReason;
public ServerVersionParseTest(String versionString, int versionNum, String rejectReason) {
this.versionString = versionString;
this.versionNum = versionNum;
this.rejectReason = rejectReason;
}
@Parameterized.Parameters(name = "str = {0}, expected = {1}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][]{
{"7.4.0", 70400, null},
{"9.0.0", 90000, null},
{"9.0.1", 90001, null},
{"9.2.1", 90201, null},
/* Major only */
{"7.4", 70400, null},
{"9.0", 90000, null},
{"9.2", 90200, null},
{"9.6", 90600, null},
{"10", 100000, null},
{"11", 110000, null},
/* Multidigit */
{"9.4.10", 90410, null},
{"9.20.10", 92010, null},
/* After 10 */
{"10.1", 100001, null},
{"10.10", 100010, null},
{"11.1", 110001, null},
{"123.20", 1230020, null},
/* Fail cases */
{"9.20.100", -1, "Should've rejected three-digit minor version"},
{"9.100.10", -1, "Should've rejected three-digit second part of major version"},
{"10.100.10", -1, "10+ version should have 2 components only"},
{"12345.1", -1, "Too big version number"},
/* Preparsed */
{"90104", 90104, null},
{"090104", 90104, null},
{"070400", 70400, null},
{"100004", 100004, null},
{"10000", 10000, null},
/* --with-extra-version or beta/devel tags */
{"9.4devel", 90400, null},
{"9.4beta1", 90400, null},
{"10devel", 100000, null},
{"10beta1", 100000, null},
{"10.1devel", 100001, null},
{"10.1beta1", 100001, null},
{"9.4.1bobs", 90401, null},
{"9.4.1bobspatched9.4", 90401, null},
{"9.4.1-bobs-patched-postgres-v2.2", 90401, null},
});
}
@Test
public void run() {
try {
Version version = ServerVersion.from(versionString);
if (rejectReason == null) {
Assert.assertEquals("Parsing " + versionString, versionNum, version.getVersionNum());
} else {
Assert.fail("Should fail to parse " + versionString + ", " + rejectReason);
}
} catch (NumberFormatException e) {
if (rejectReason != null) {
return;
}
throw e;
}
}
}
@@ -0,0 +1,19 @@
package org.postgresql.test.util;
import org.postgresql.core.ServerVersion;
import org.junit.Assert;
import org.junit.Test;
public class ServerVersionTest {
@Test
public void versionIncreases() {
ServerVersion prev = null;
for (ServerVersion serverVersion : ServerVersion.values()) {
if (prev != null) {
Assert.assertTrue(prev + " should be less than " + serverVersion,
prev.getVersionNum() < serverVersion.getVersionNum());
}
}
}
}

0 comments on commit a639431

Please sign in to comment.