/*
* For work developed by the HSQL Development Group:
*
* Copyright (c) 2001-2016, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
*
* For work originally developed by the Hypersonic SQL Group:
*
* Copyright (c) 1995-2000, The Hypersonic SQL Group.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Hypersonic SQL Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Hypersonic SQL Group.
*/
package org.hsqldb;
import org.hsqldb.error.
Error;
import org.hsqldb.error.
ErrorCode;
import org.hsqldb.lib.
HsqlByteArrayOutputStream;
import org.hsqldb.lib.
StringUtil;
import org.hsqldb.types.
BinaryData;
import org.hsqldb.types.
BlobData;
import org.hsqldb.types.
CharacterType;
import org.hsqldb.types.
ClobData;
import org.hsqldb.types.
LobData;
import org.hsqldb.types.
Type;
/**
* Reusable object for processing LIKE queries.
*
* Rewritten in HSQLDB based on original Hypersonic code.<p>
*
* @author Fred Toussi (fredt@users dot sourceforge dot net)
* @author Thomas Mueller (Hypersonic SQL Group)
* @version 2.3.1
* @since 1.6.2
*/
// campbell-burnet@users 20030930 - patch 1.7.2 - optimize into joins if possible
// fredt@users 20031006 - patch 1.7.2 - reuse Like objects for all rows
// fredt@users 1.9.0 - LIKE for binary strings
// fredt@users 1.9.0 - CompareAt() changes for performance suggested by Gary Frost
class
Like implements
Cloneable {
private static final
BinaryData maxByteValue =
new
BinaryData(new byte[]{ -128 }, false);
private char[]
cLike;
private int[]
wildCardType;
private int
iLen;
private boolean
isIgnoreCase;
private int
iFirstWildCard;
private boolean
isNull;
int
escapeChar;
boolean
hasCollation;
static final int
UNDERSCORE_CHAR = 1;
static final int
PERCENT_CHAR = 2;
boolean
isVariable = true;
boolean
isBinary = false;
Type dataType;
Like() {}
void
setParams(boolean
collation) {
hasCollation =
collation;
}
void
setIgnoreCase(boolean
flag) {
isIgnoreCase =
flag;
}
private
Object getStartsWith() {
if (
iLen == 0) {
return
isBinary ? (
Object)
BinaryData.
zeroLengthBinary
: "";
}
StringBuffer sb = null;
HsqlByteArrayOutputStream os = null;
if (
isBinary) {
os = new
HsqlByteArrayOutputStream();
} else {
sb = new
StringBuffer();
}
int
i = 0;
for (;
i <
iLen &&
wildCardType[
i] == 0;
i++) {
if (
isBinary) {
os.
writeByte(
cLike[
i]);
} else {
sb.
append(
cLike[
i]);
}
}
if (
i == 0) {
return null;
}
return
isBinary ? (
Object) new
BinaryData(
os.
toByteArray(), false)
:
sb.
toString();
}
Boolean compare(
Session session,
Object o) {
if (
o == null) {
return null;
}
if (
isNull) {
return null;
}
if (
isIgnoreCase) {
o = ((
CharacterType)
dataType).
upper(
session,
o);
}
int
length =
getLength(
session,
o);
if (
o instanceof
ClobData) {
o = ((
ClobData)
o).
getChars(
session, 0,
(int) ((
ClobData)
o).
length(
session));
}
return
compareAt(
session,
o, 0, 0,
iLen,
length,
cLike,
wildCardType)
?
Boolean.
TRUE
:
Boolean.
FALSE;
}
char
getChar(
Session session,
Object o, int
i) {
char
c;
if (
isBinary) {
c = (char) ((
BlobData)
o).
getBytes()[
i];
} else {
if (
o instanceof char[]) {
c = ((char[])
o)[
i];
} else if (
o instanceof
ClobData) {
c = ((
ClobData)
o).
getChars(
session,
i,1)[0];
} else {
c = ((
String)
o).
charAt(
i);
}
}
return
c;
}
int
getLength(
SessionInterface session,
Object o) {
int
l;
if (
o instanceof
LobData) {
l = (int) ((
LobData)
o).
length(
session);
} else {
l = ((
String)
o).
length();
}
return
l;
}
private boolean
compareAt(
Session session,
Object o, int
i, int
j,
int
iLen, int
jLen, char[]
cLike,
int[]
wildCardType) {
for (;
i <
iLen;
i++) {
switch (
wildCardType[
i]) {
case 0 : // general character
if ((
j >=
jLen)
|| (
cLike[
i] !=
getChar(
session,
o,
j++))) {
return false;
}
break;
case
UNDERSCORE_CHAR : // underscore: do not test this character
if (
j++ >=
jLen) {
return false;
}
break;
case
PERCENT_CHAR : // percent: none or any character(s)
if (++
i >=
iLen) {
return true;
}
while (
j <
jLen) {
if ((
cLike[
i] ==
getChar(
session,
o,
j))
&&
compareAt(
session,
o,
i,
j,
iLen,
jLen,
cLike,
wildCardType)) {
return true;
}
j++;
}
return false;
}
}
if (
j !=
jLen) {
return false;
}
return true;
}
void
setPattern(
Session session,
Object pattern,
Object escape,
boolean
hasEscape) {
isNull =
pattern == null;
if (!
hasEscape) {
escapeChar = -1;
} else {
if (
escape == null) {
isNull = true;
return;
} else {
int
length =
getLength(
session,
escape);
if (
length != 1) {
if (
isBinary) {
throw
Error.
error(
ErrorCode.
X_2200D);
} else {
throw
Error.
error(
ErrorCode.
X_22019);
}
}
escapeChar =
getChar(
session,
escape, 0);
}
}
if (
isNull) {
return;
}
if (
isIgnoreCase) {
pattern = (
String) ((
CharacterType)
dataType).
upper(null,
pattern);
}
iLen = 0;
iFirstWildCard = -1;
int
l =
getLength(
session,
pattern);
cLike = new char[
l];
wildCardType = new int[
l];
boolean
bEscaping = false,
bPercent = false;
for (int
i = 0;
i <
l;
i++) {
char
c =
getChar(
session,
pattern,
i);
if (!
bEscaping) {
if (
escapeChar ==
c) {
bEscaping = true;
continue;
} else if (
c == '_') {
wildCardType[
iLen] =
UNDERSCORE_CHAR;
if (
iFirstWildCard == -1) {
iFirstWildCard =
iLen;
}
} else if (
c == '%') {
if (
bPercent) {
continue;
}
bPercent = true;
wildCardType[
iLen] =
PERCENT_CHAR;
if (
iFirstWildCard == -1) {
iFirstWildCard =
iLen;
}
} else {
bPercent = false;
}
} else {
if (
c ==
escapeChar ||
c == '_' ||
c == '%') {
bPercent = false;
bEscaping = false;
} else {
throw
Error.
error(
ErrorCode.
X_22025);
}
}
cLike[
iLen++] =
c;
}
if (
bEscaping) {
throw
Error.
error(
ErrorCode.
X_22025);
}
for (int
i = 0;
i <
iLen - 1;
i++) {
if ((
wildCardType[
i] ==
PERCENT_CHAR)
&& (
wildCardType[
i + 1] ==
UNDERSCORE_CHAR)) {
wildCardType[
i] =
UNDERSCORE_CHAR;
wildCardType[
i + 1] =
PERCENT_CHAR;
}
}
}
boolean
isEquivalentToUnknownPredicate() {
return !
isVariable &&
isNull;
}
boolean
isEquivalentToEqualsPredicate() {
return !
isVariable &&
iFirstWildCard == -1;
}
boolean
isEquivalentToNotNullPredicate() {
if (
isVariable ||
isNull ||
iFirstWildCard == -1) {
return false;
}
for (int
i = 0;
i <
wildCardType.length;
i++) {
if (
wildCardType[
i] !=
PERCENT_CHAR) {
return false;
}
}
return true;
}
int
getFirstWildCardIndex() {
return
iFirstWildCard;
}
Object getRangeLow() {
return
getStartsWith();
}
Object getRangeHigh(
Session session) {
Object o =
getStartsWith();
if (
o == null) {
return null;
}
if (
isBinary) {
return new
BinaryData(
session, (
BinaryData)
o,
maxByteValue);
} else {
return
dataType.
concat(
session,
o, "\uffff");
}
}
public
String describe(
Session session) {
StringBuffer sb = new
StringBuffer();
sb.
append(super.toString()).
append("[\n");
sb.
append("escapeChar=").
append(
escapeChar).
append('\n');
sb.
append("isNull=").
append(
isNull).
append('\n');
// sb.append("optimised=").append(optimised).append('\n');
sb.
append("isIgnoreCase=").
append(
isIgnoreCase).
append('\n');
sb.
append("iLen=").
append(
iLen).
append('\n');
sb.
append("iFirstWildCard=").
append(
iFirstWildCard).
append('\n');
sb.
append("cLike=");
if (
cLike != null) {
sb.
append(
StringUtil.
arrayToString(
cLike));
}
sb.
append('\n');
sb.
append("wildCardType=");
if (
wildCardType != null) {
sb.
append(
StringUtil.
arrayToString(
wildCardType));
}
sb.
append(']');
return
sb.
toString();
}
public
Like duplicate() {
try {
return (
Like) super.clone();
} catch (
CloneNotSupportedException ex) {
throw
Error.
runtimeError(
ErrorCode.
U_S0500, "Expression");
}
}
}