/* Copyright (c) 2001-2019, 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.
/** * Static values and methods to facilitate servicing ODBC clients. */
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!
/** * 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.
/** * 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); }
Params:
  • hexChars – A String containing an EVEN number of hex characters.
/** * @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]) + ')'); } }