org.hsqldb.server.OdbcUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sqltool Show documentation
Show all versions of sqltool Show documentation
HSQLDB - Lightweight 100% Java SQL Database Engine
/* Copyright (c) 2001-2021, 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.
*/
package org.hsqldb.server;
import java.io.IOException;
import java.util.Locale;
import org.hsqldb.ColumnBase;
import org.hsqldb.lib.DataOutputStream;
import org.hsqldb.result.ResultMetaData;
/**
* Static values and methods to facilitate servicing ODBC clients.
*
* @author Blaine Simpson (blaine dot simpson at admc dot com)
* @since 1.9.0
*/
public class OdbcUtil {
static void validateInputPacketSize(OdbcPacketInputStream p)
throws RecoverableOdbcFailure {
int remaining = -1;
try {
remaining = p.available();
} catch (IOException ioe) {
// Just ignore here and we will send notification below.
// If there really is an I/O problem, it will be handled better
// on the next read.
}
if (remaining < 1) {
return;
}
throw new RecoverableOdbcFailure(
"Client supplied bad length for " + p.packetType
+ " packet. " + remaining + " bytes available after processing",
"Bad length for " + p.packetType
+ " packet. " + remaining + " extra bytes", "08P01");
// Code here means Protocol Violation
}
static String echoBackReplyString(String inCommand, int retval) {
String uc = inCommand.trim().toUpperCase(Locale.ENGLISH);
int firstWhiteSpace;
for (firstWhiteSpace = 0; firstWhiteSpace < uc.length();
firstWhiteSpace++) {
if (Character.isWhitespace(uc.charAt(firstWhiteSpace))) {
break;
}
}
String keyword = uc.substring(0, firstWhiteSpace);
StringBuilder replyString = new StringBuilder(keyword);
if (keyword.equals("UPDATE") || keyword.equals("DELETE")) {
replyString.append(' ').append(retval);
} else if (keyword.equals("CREATE") || keyword.equals("DROP")) {
// This case is significantly missing from the spec., yet
// PostgreSQL Server echo's these commands as implemented here.
// TODO: Add error-checking
int wordStart;
for (wordStart = firstWhiteSpace; wordStart < uc.length();
wordStart++) {
if (!Character.isWhitespace(uc.charAt(wordStart))) {
break;
}
}
int wordEnd;
for (wordEnd = wordStart; wordEnd < uc.length();
wordEnd++) {
if (!Character.isWhitespace(uc.charAt(wordEnd))) {
break;
}
}
replyString.append(' ').append(uc, wordStart, wordEnd);
} else if (keyword.equals("INSERT")) {
replyString.append(' ').append(0).append(' ').append(retval);
// The number is supposed to be the oid for single-row
// inserts into a table that has row oids.
// Since the requirement is conditional, it's very likely that the
// client will make any use of the value we pass.
}
// If we ever implement following SQL commands, add echo's for these
// strings too: MOVE, FETCH, COPY.
return replyString.toString();
}
static void writeParam(
String key, String val, DataOutputStream hOutStream) throws IOException {
OdbcPacketOutputStream alertPacket =
OdbcPacketOutputStream.newOdbcPacketOutputStream();
alertPacket.write(key);
alertPacket.write(val);
alertPacket.xmit('S', hOutStream);
alertPacket.close();
}
// Constants taken from connection.h
static final int ODBC_SM_DATABASE = 64;
static final int ODBC_SM_USER = 32;
static final int ODBC_SM_OPTIONS = 64;
static final int ODBC_SM_UNUSED = 64;
static final int ODBC_SM_TTY = 64;
static final int ODBC_AUTH_REQ_PASSWORD = 3;
static final int ODBC_AUTH_REQ_OK = 0;
static void alertClient(int severity, String message,
DataOutputStream hOutStream) throws IOException {
alertClient(severity, message, null, hOutStream);
}
static void alertClient(int severity, String message,
String sqlStateCode, DataOutputStream hOutStream) throws IOException {
if (sqlStateCode == null) {
sqlStateCode = "XX000";
// This default code means INTERNAL ERROR
}
if (!odbcSeverityMap.containsKey(severity)) {
throw new IllegalArgumentException(
"Unknown severity value (" + severity + ')');
}
OdbcPacketOutputStream alertPacket =
OdbcPacketOutputStream.newOdbcPacketOutputStream();
alertPacket.write("S" + odbcSeverityMap.get(severity));
if (severity < ODBC_SEVERITY_NOTICE) {
alertPacket.write("C" + sqlStateCode);
}
alertPacket.write("M" + message);
alertPacket.writeByte(0);
alertPacket.xmit((severity < ODBC_SEVERITY_NOTICE) ? 'E' : 'N',
hOutStream);
alertPacket.close();
}
static String[][] hardcodedParams = new String[][] {
new String[] { "client_encoding", "SQL_ASCII" },
new String[] { "DateStyle", "ISO, MDY" },
new String[] { "integer_datetimes", "on" },
new String[] { "is_superuser", "on" },
new String[] { "server_encoding", "SQL_ASCII" },
new String[] { "server_version", "8.3.1" },
new String[] { "session_authorization", "blaine" },
new String[] { "standard_conforming_strings", "off" },
new String[] { "TimeZone", "US/Eastern" },
};
static final int ODBC_SIMPLE_MODE = 0;
static final int ODBC_EXTENDED_MODE = 1;
static final int ODBC_EXT_RECOVER_MODE = 2;
static final int ODBC_SEVERITY_FATAL = 1;
static final int ODBC_SEVERITY_ERROR = 2;
static final int ODBC_SEVERITY_PANIC = 3;
static final int ODBC_SEVERITY_WARNING = 4;
static final int ODBC_SEVERITY_NOTICE = 5;
static final int ODBC_SEVERITY_DEBUG = 6;
static final int ODBC_SEVERITY_INFO = 7;
static final int ODBC_SEVERITY_LOG = 8;
static final org.hsqldb.lib.IntKeyHashMap odbcSeverityMap =
new org.hsqldb.lib.IntKeyHashMap();
static {
odbcSeverityMap.put(ODBC_SEVERITY_FATAL, "FATAL");
odbcSeverityMap.put(ODBC_SEVERITY_ERROR, "ERROR");
odbcSeverityMap.put(ODBC_SEVERITY_PANIC, "PANIC");
odbcSeverityMap.put(ODBC_SEVERITY_WARNING, "WARNING");
odbcSeverityMap.put(ODBC_SEVERITY_NOTICE, "NOTICE");
odbcSeverityMap.put(ODBC_SEVERITY_DEBUG, "DEBUG");
odbcSeverityMap.put(ODBC_SEVERITY_INFO, "INFO");
odbcSeverityMap.put(ODBC_SEVERITY_LOG, "LOG");
}
/**
* TODO: Eliminate the mungling on the client-side instead of
* attempting very problematic correction here!
*/
static String revertMungledPreparedQuery(String inQuery) {
// THIS PURPOSEFULLY USING Java 1.4!
return inQuery.replaceAll("\\$\\d+", "?");
}
public static int getTableOidForColumn(int colIndex, ResultMetaData md) {
if (!md.isTableColumn(colIndex)) {
return 0;
}
ColumnBase col = md.columns[colIndex];
int hashCode = (col.getSchemaNameString() + '.'
+ col.getTableNameString()).hashCode();
if (hashCode < 0) {
hashCode *= -1;
}
return hashCode;
}
/**
* Temporary hack.
*
* This ID should stick with the table
* column. Here, it will change based on user-specified column label.
* The int has is also being truncated into a short.
*/
public static short getIdForColumn(int colIndex, ResultMetaData md) {
if (!md.isTableColumn(colIndex)) {
return 0;
}
short hashCode =
(short) md.getGeneratedColumnNames()[colIndex].hashCode();
if (hashCode < 0) {
hashCode *= -1;
}
return hashCode;
//return (short) (colIndex + 1);
}
/**
* @param hexChars A String containing an EVEN number of hex
* characters.
*/
public static String hexCharsToOctalOctets(String hexChars) {
int chars = hexChars.length();
if (chars != (chars / 2) * 2) {
throw new IllegalArgumentException("Hex character lists contains "
+ "an odd number of characters: " + chars);
}
StringBuilder sb = new StringBuilder();
char c;
int octet;
for (int i = 0; i < chars; i++) {
octet = 0;
c = hexChars.charAt(i);
if (c >= 'a' && c <= 'f') {
octet += 10 + c - 'a';
} else if (c >= 'A' && c <= 'F') {
octet += 10 + c - 'A';
} else if (c >= '0' && c <= '9') {
octet += c - '0';
} else {
throw new IllegalArgumentException(
"Non-hex character in input at offset " + i + ": " + c);
}
octet = octet << 4;
c = hexChars.charAt(++i);
if (c >= 'a' && c <= 'f') {
octet += 10 + c - 'a';
} else if (c >= 'A' && c <= 'F') {
octet += 10 + c - 'A';
} else if (c >= '0' && c <= '9') {
octet += c - '0';
} else {
throw new IllegalArgumentException(
"Non-hex character in input at offset " + i + ": " + c);
}
sb.append('\\');
sb.append((char) ('0' + (octet >> 6)));
sb.append((char) ('0' + ((octet >> 3) & 7)));
sb.append((char) ('0' + (octet & 7)));
}
return sb.toString();
}
public static void main(String[] sa) {
System.out.println("(" + OdbcUtil.hexCharsToOctalOctets(sa[0]) + ')');
}
}