Skip to content

Commit

Permalink
JDBC-535 isc_info_firebird_version returns multiple segments
Browse files Browse the repository at this point in the history
  • Loading branch information
mrotteveel committed Jun 9, 2018
1 parent 7dff69a commit 0bccd74
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 163 deletions.
1 change: 1 addition & 0 deletions src/etc/release_notes.md
Expand Up @@ -63,6 +63,7 @@ Changelog
The following has been changed or fixed in Jaybird 2.2.15:

- Fixed: Jaybird cannot parse Firebird version numbers with revisions ([JDBC-534](http://tracker.firebirdsql.org/browse/JDBC-534))
- Fixed: Incorrect parsing of Firebird version numbers ([JDBC-535](http://tracker.firebirdsql.org/browse/JDBC-535))

**Known issues in Jaybird 2.2.15**

Expand Down
9 changes: 7 additions & 2 deletions src/main/org/firebirdsql/gds/IscDbHandle.java
Expand Up @@ -130,13 +130,18 @@ public interface IscDbHandle {
* @return database server version.
*/
String getVersion();

/**
*
* @param version Version string
* @throws GDSException For failures to parse or process the version string
*/
void setVersion(String version) throws GDSException;

/**
* @param version Version strings
* @throws GDSException For failures to parse or process the version string
*/
void setVersion(String... version) throws GDSException;

/**
* Retrieve whether this handle is valid.
Expand Down
6 changes: 5 additions & 1 deletion src/main/org/firebirdsql/gds/impl/AbstractIscDbHandle.java
Expand Up @@ -119,8 +119,12 @@ public void setODSMinorVersion(int value) {
public String getVersion() {
return serverVersion.toString();
}

public void setVersion(String version) throws GDSException {
setVersion(new String[] { version });
}

public void setVersion(String... version) throws GDSException {
this.serverVersion = GDSServerVersion.parseRawVersion(version);
}

Expand Down
126 changes: 81 additions & 45 deletions src/main/org/firebirdsql/gds/impl/GDSServerVersion.java
Expand Up @@ -25,6 +25,7 @@
package org.firebirdsql.gds.impl;

import java.io.Serializable;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand All @@ -33,23 +34,27 @@
* in response to the <code>isc_info_firebird_version</code> information call.
* Expected version format is:
* <p>
* <platform>-<type><majorVersion>.<minorVersion>.<variant>.<buildNum> <serverName>[,<extended server info>]
* <code>&lt;platform&gt;-&lt;type&gt;&lt;majorVersion&gt;.&lt;minorVersion&gt;.&lt;variant&gt;.&lt;buildNum&gt;
* &lt;serverName&gt;</code>, and additional version string elements if present.
* </p>
* where <code>platform</code> is a two-character platform identification string,
* Windows for example is "WI", <code>type</code> is one of the three characters:
* "V" - production version, "T" - beta version, "X" - development version.
*
*/
public class GDSServerVersion implements Serializable {
public final class GDSServerVersion implements Serializable {

private static final long serialVersionUID = -153657557318248541L;

public static final String TYPE_PRODUCTION = "V";
@SuppressWarnings("unused")
public static final String TYPE_PRODUCTION = "V";
@SuppressWarnings("unused")
public static final String TYPE_BETA = "T";
@SuppressWarnings("unused")
public static final String TYPE_DEVELOPMENT = "X";

private static final Pattern VERSION_PATTERN =
Pattern.compile("((\\w{2})-(\\w)(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)(?:-\\S+)?) ([^-,]+)(?:[-,](.*))?");
Pattern.compile("((\\w{2})-(\\w)(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)(?:-\\S+)?) (.+)");

private static final int FULL_VERSION_IDX = 1;
private static final int PLATFORM_IDX = 2;
Expand All @@ -59,23 +64,31 @@ public class GDSServerVersion implements Serializable {
private static final int VARIANT_IDX = 6;
private static final int BUILD_IDX = 7;
private static final int SERVER_NAME_IDX = 8;
private static final int EXTENDED_INFO_IDX = 9;

private String rawStr;
private final String[] rawVersions;

private String platform;
private String type;
private final String platform;
private final String type;

private String fullVersion;
private int majorVersion;
private int minorVersion;
private int variant;
private int buildNumber;
private final String fullVersion;
private final int majorVersion;
private final int minorVersion;
private final int variant;
private final int buildNumber;

private String serverName;
private String extendedServerName;

private GDSServerVersion() {
private GDSServerVersion(String[] rawVersions, String platform, String type, String fullVersion, int majorVersion,
int minorVersion, int variant, int buildNumber, String serverName) {
this.rawVersions = rawVersions.clone();
this.platform = platform;
this.type = type;
this.fullVersion = fullVersion;
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.variant = variant;
this.buildNumber = buildNumber;
this.serverName = serverName;
}

public int getBuildNumber() {
Expand Down Expand Up @@ -107,15 +120,28 @@ public int getVariant() {
}

public String getExtendedServerName() {
return extendedServerName;
if (rawVersions.length < 2) {
return null;
} else if (rawVersions.length == 2) {
return rawVersions[1];
} else {
StringBuilder sb = new StringBuilder();
for (int idx = 1; idx < rawVersions.length; idx++) {
if (idx > 1) {
sb.append(',');
}
sb.append(rawVersions[idx]);
}
return sb.toString();
}
}

public String getFullVersion() {
return fullVersion;
}

public int hashCode() {
return rawStr.hashCode();
return Arrays.hashCode(rawVersions);
}

public boolean equals(Object obj) {
Expand All @@ -125,42 +151,52 @@ public boolean equals(Object obj) {

GDSServerVersion that = (GDSServerVersion) obj;

return rawStr.equals(that.rawStr);
return Arrays.equals(this.rawVersions, that.rawVersions);
}

public String toString() {
return rawStr;
if (rawVersions.length == 1) {
return rawVersions[0];
}
StringBuilder sb = new StringBuilder();
int idx = 0;
sb.append(rawVersions[idx++]);
do {
sb.append(',');
sb.append(rawVersions[idx++]);
} while (idx < rawVersions.length);
return sb.toString();
}

/**
* Parse the raw version string and create a GDSServerVersion object.
*
* @param versionString string to parse.
*
* @throws GDSServerVersionException if versionString does not match expected pattern
* @param versionStrings
* strings to parse, expects a non-empty array with at least 1, and usually 2 version strings
* @throws GDSServerVersionException
* if versionString does not match expected pattern
*/
public static GDSServerVersion parseRawVersion(String versionString) throws GDSServerVersionException {
Matcher matcher = VERSION_PATTERN.matcher(versionString);
if (!matcher.matches()) {
throw new GDSServerVersionException(String.format("Version string \"%s\" does not match expected format",
versionString));
}

GDSServerVersion version = new GDSServerVersion();

version.rawStr = versionString;

version.serverName = matcher.group(SERVER_NAME_IDX);
version.extendedServerName = matcher.group(EXTENDED_INFO_IDX);
version.platform = matcher.group(PLATFORM_IDX);
version.type = matcher.group(TYPE_IDX);

version.fullVersion = matcher.group(FULL_VERSION_IDX);
version.majorVersion = Integer.parseInt(matcher.group(MAJOR_IDX));
version.minorVersion = Integer.parseInt(matcher.group(MINOR_IDX));
version.variant = Integer.parseInt(matcher.group(VARIANT_IDX));
version.buildNumber = Integer.parseInt(matcher.group(BUILD_IDX));

return version;
public static GDSServerVersion parseRawVersion(String... versionStrings) throws GDSServerVersionException {
if (versionStrings == null || versionStrings.length == 0 || versionStrings[0] == null) {
throw new GDSServerVersionException("No version string information present");
}

Matcher matcher = VERSION_PATTERN.matcher(versionStrings[0]);
if (!matcher.matches()) {
throw new GDSServerVersionException(
String.format("Version string \"%s\" does not match expected format", versionStrings[0]));
}

return new GDSServerVersion(
versionStrings,
matcher.group(PLATFORM_IDX),
matcher.group(TYPE_IDX),
matcher.group(FULL_VERSION_IDX),
Integer.parseInt(matcher.group(MAJOR_IDX)),
Integer.parseInt(matcher.group(MINOR_IDX)),
Integer.parseInt(matcher.group(VARIANT_IDX)),
Integer.parseInt(matcher.group(BUILD_IDX)),
matcher.group(SERVER_NAME_IDX));
}

}
46 changes: 14 additions & 32 deletions src/main/org/firebirdsql/gds/impl/jni/BaseGDSImpl.java
Expand Up @@ -19,6 +19,7 @@
package org.firebirdsql.gds.impl.jni;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import org.firebirdsql.encodings.EncodingFactory;
import org.firebirdsql.gds.*;
Expand All @@ -43,9 +44,6 @@ public abstract class BaseGDSImpl extends AbstractGDS {
ISCConstants.isc_info_firebird_version,
ISCConstants.isc_info_ods_version,
ISCConstants.isc_info_ods_minor_version,
ISCConstants.isc_info_implementation,
ISCConstants.isc_info_db_class,
ISCConstants.isc_info_base_level,
ISCConstants.isc_info_end };


Expand Down Expand Up @@ -1168,38 +1166,22 @@ private void parseAttachDatabaseInfo(byte[] info, IscDbHandle handle)
if (debug)
log.debug("isc_info_ods_minor_version:" + value);
break;
case ISCConstants.isc_info_firebird_version:
case ISCConstants.isc_info_firebird_version:{
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] fb_vers = new byte[len - 2];
System.arraycopy(info, i + 2, fb_vers, 0, len - 2);
i += len;
String fb_versS = new String(fb_vers);
db.setVersion(fb_versS);
if (debug)
log.debug("isc_info_firebird_version:" + fb_versS);
break;
case ISCConstants.isc_info_implementation:
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] impl = new byte[len - 2];
System.arraycopy(info, i + 2, impl, 0, len - 2);
i += len;
break;
case ISCConstants.isc_info_db_class:
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] db_class = new byte[len - 2];
System.arraycopy(info, i + 2, db_class, 0, len - 2);
i += len;
break;
case ISCConstants.isc_info_base_level:
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] base_level = new byte[len - 2];
System.arraycopy(info, i + 2, base_level, 0, len - 2);
i += len;
final int expectedIndex = i + len;
final int versionCount = info[i++] & 0xFF;
final String[] versionParts = new String[versionCount];
for (int versionIndex = 0; versionIndex < versionCount; versionIndex++) {
int versionLength = info[i++] & 0xFF;
versionParts[versionIndex] = new String(info, i, versionLength);
i += versionLength;
}
assert i == expectedIndex : "Parsing version information lead to wrong index";
db.setVersion(versionParts);
if (debug) log.debug("isc_info_firebird_version: " + Arrays.toString(versionParts));
break;
}
case ISCConstants.isc_info_truncated:
if (debug) log.debug("isc_info_truncated ");
return;
Expand Down
45 changes: 14 additions & 31 deletions src/main/org/firebirdsql/gds/impl/wire/AbstractJavaGDSImpl.java
Expand Up @@ -34,6 +34,7 @@

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;

Expand Down Expand Up @@ -414,8 +415,6 @@ public void iscAttachDatabase(String connectString, IscDbHandle db_handle,
ISCConstants.isc_info_firebird_version,
ISCConstants.isc_info_ods_version,
ISCConstants.isc_info_ods_minor_version,
ISCConstants.isc_info_implementation,
ISCConstants.isc_info_db_class, ISCConstants.isc_info_base_level,
ISCConstants.isc_info_end };

protected void internalAttachDatabase(DbAttachInfo dbai, IscDbHandle db_handle,
Expand Down Expand Up @@ -643,38 +642,22 @@ private void parseAttachDatabaseInfo(byte[] info, IscDbHandle handle)
if (debug)
log.debug("isc_info_ods_minor_version:" + value);
break;
case ISCConstants.isc_info_firebird_version:
case ISCConstants.isc_info_firebird_version: {
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] fb_vers = new byte[len - 2];
System.arraycopy(info, i + 2, fb_vers, 0, len - 2);
i += len;
String fb_versS = new String(fb_vers);
db.setVersion(fb_versS);
if (debug)
log.debug("isc_info_firebird_version:" + fb_versS);
break;
case ISCConstants.isc_info_implementation:
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] impl = new byte[len - 2];
System.arraycopy(info, i + 2, impl, 0, len - 2);
i += len;
break;
case ISCConstants.isc_info_db_class:
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] db_class = new byte[len - 2];
System.arraycopy(info, i + 2, db_class, 0, len - 2);
i += len;
break;
case ISCConstants.isc_info_base_level:
len = iscVaxInteger(info, i, 2);
i += 2;
byte[] base_level = new byte[len - 2];
System.arraycopy(info, i + 2, base_level, 0, len - 2);
i += len;
final int expectedIndex = i + len;
final int versionCount = info[i++] & 0xFF;
final String[] versionParts = new String[versionCount];
for (int versionIndex = 0; versionIndex < versionCount; versionIndex++) {
int versionLength = info[i++] & 0xFF;
versionParts[versionIndex] = new String(info, i, versionLength);
i += versionLength;
}
assert i == expectedIndex : "Parsing version information lead to wrong index";
db.setVersion(versionParts);
if (debug) log.debug("isc_info_firebird_version: " + Arrays.toString(versionParts));
break;
}
case ISCConstants.isc_info_truncated:
if (debug)
log.debug("isc_info_truncated ");
Expand Down

0 comments on commit 0bccd74

Please sign in to comment.