} // getTargets /* ** Name: getHost ** ** Description: ** Extracts and returns the name of the host from the target ** string: ** ** <host>[:<port>] ** ** Input: ** target ** ** Output: ** None. ** ** Returns: ** String Host name. ** ** History: ** 7-Jun-99 (gordy) ** Created. ** 19-Oct-06 (lunbr01) Sir 116548 ** Allow for IPv6 addresses enclosed in square brackets '[]'. */ /// <summary> /// Extracts and returns the name of the host from the target. /// </summary> /// <param name="target"></param> /// <returns>Name of the host from the target.</returns> private String getHost(String target) { int index; int start = 0; if (target.StartsWith("[")) { start = 1; index = target.IndexOf(']'); if (index < 0) { if (trace.enabled(1)) { trace.write(title + ": right bracket ']' missing at end of IPv6 @ in '" + target + "'"); } throw SqlEx.get(ERR_GC4000_BAD_URL); } } else { index = target.IndexOf(':'); } return(index < 0 ? target : target.Substring(start, index - start)); } // getHost
} // readData /* ** Name: readInfo ** ** Description: ** Read an INFO message. The INFO message parameters are read ** and processed. Currently, the only INFO parameter supported ** is Trace Text which is simply written to the trace file. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 21-Apr-06 (gordy) ** Created. ** 3-Jul-06 (gordy) ** Support dedicated DBMS trace log. */ protected internal virtual void readInfo() { while (msg.moreData()) { short param_id = msg.readShort(); short param_len = msg.readShort(); switch (param_id) { case MSG_IP_TRACE: { if (trace.enabled() || conn.dbms_log.enabled()) { String txt = msg.readString(param_len); trace.log("DBMS TRACE: " + txt); conn.dbms_log.write(txt); } else { msg.skip(param_len); } break; } default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid info param ID " + param_id); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } } return; } // readInfo
} // setSegSize /* ** Name: begin ** ** Description: ** Begin a new Ingres NL data segment in the output buffer. ** reserves space for the Ingres TCP-IP header. Flushes ** current buffer if insufficient space for next segment. ** ** Input: ** size Minimum amount of space required for next segment. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 9-Jun-99 (gordy) ** Created. */ /// <summary> /// Begin a new Ingres NL data segment in the output buffer. /// reserves space for the Ingres TCP-IP header. Flushes /// current buffer if insufficient space for next segment. /// </summary> /// <param name="size">Minimum amount of space required for /// next segment.</param> protected internal virtual void begin(int size) { lock (this) { if (closed || outputStream == null) { throw SqlEx.get(ERR_GC4004_CONNECTION_CLOSED); } setSegSize(); // Update the current segment length (if any). seg_beg = buf_len; // New segment starts at end of current data. /* ** Flush current segments if insufficient space ** for next segment. Note that flushBuffer() ** handles the case of no current segments. */ if ((buf_len + size + 2) > buf_max) { flushBuffer(false); } /* ** Reserve space for the Ingres TCP-IP header. */ data_beg = data_ptr = seg_beg + 2; data_end = buf_max; return; } } // begin
} // write /* ** Name: write (byteArray) at a position ** ** Description: ** Append a byte array to the output buffer. This ** routine does not split arrays across buffers, the ** current buffer is flushed and the array must fit ** in the new buffer, or an exception is thrown. ** ** Input: ** byteArray Byte array. ** offset Position of data in array. ** length Length of data in array. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 16-Jun-99 (gordy) ** Created. */ /// <summary> /// Append a byte array to the output buffer. This /// routine does not split arrays across buffers, the /// current buffer is flushed and the array must fit /// in the new buffer, or an exception is thrown. /// </summary> /// <param name="byteArray">Byte array.</param> /// <param name="offset">Position of data in array to read from.</param> /// <param name="length">Length of data in array.</param> public virtual void write(byte[] byteArray, int offset, int length) { if (!need(length)) { if (trace.enabled(1)) { trace.write(title + ": array length " + length + " too long"); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } if (trace.enabled(5)) { trace.write(title + ": appending " + length); } try { Array.Copy(byteArray, offset, buffer, data_ptr, length); } catch (System.Exception ex) { // Should not happen! throw SqlEx.get(title + ": array copy exception. " + ex.Message, "HY000", 0); } data_ptr += length; return; } // write
} // need /* ** Name: check ** ** Description: ** Validate a buffer write request. ** ** Input: ** position: Position in buffer. ** length: Length of data. ** ** Output: ** None. ** ** Returns: ** void. ** ** History: ** 28-Jan-00 (gordy) ** Created to replace redundant code and provide tracing. */ /// <summary> /// Validate a buffer write request. /// </summary> /// <param name="position">Position in buffer.</param> /// <param name="size">Length of data.</param> private void check(int position, int size) { if (position < data_beg || (position + size) > data_end) { if (trace.enabled(1)) { trace.write(title + ": position out-of-bounds"); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } else if (trace.enabled(5)) { if (position != data_ptr) { trace.write(title + ": writing " + size + " @ " + position); } else { trace.write(title + ": appending " + size); } } return; } // check
} // flush /* ** Name: next ** ** Description: ** Read next segment from input stream. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 9-Jun-99 (gordy) ** Created. ** 28-Jan-00 (gordy) ** Added TL segment tracing.*/ /// <summary> /// Read next segment from input stream. /// </summary> protected internal virtual void next() { lock (this) { int size; if (closed || inputStream == null) { throw SqlEx.get(ERR_GC4004_CONNECTION_CLOSED); } if (seg_beg < seg_end) { seg_beg = seg_end; } if (seg_beg >= buf_len) { clearBuffer(); } fillBuffer(2); // Make sure length is available size = getSegSize(); if (trace.enabled(5)) { trace.write(title + ": read segment " + size); } fillBuffer(size); // Make sure entire segment is available seg_end = seg_beg + size; data_beg = data_ptr = seg_beg + 2; data_end = seg_end; return; } } // next
columnMap(int index) { if (index < 0 || index >= count) { throw SqlEx.get(ERR_GC4011_INDEX_RANGE); } return(index); } // columnMap
} // readDesc /* ** Name: readData ** ** Description: ** Read a data message. The default action is to throw an exception. ** A sub-class should override this method if a data message is a ** valid request result. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** boolean Interrupt reading results. ** ** History: ** 8-Sep-99 (gordy) ** Created. ** 29-Sep-99 (gordy) ** Changed return type to permit BLOB segments to ** interrupt the normal processing of messages. */ protected internal virtual bool readData() { if (trace.enabled(1)) { trace.write(tr_id + ": unexpected result data."); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } // readData
} // readResults /* ** Name: readDesc ** ** Description: ** Read a data descriptor message. The default action is to ** throw an exception. A sub-class should override this method ** if a data descriptor message is a valid request result. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** AdvanRSMD Result set data descriptor. ** ** History: ** 8-Sep-99 (gordy) ** Created. */ protected internal virtual AdvanRSMD readDesc() { if (trace.enabled(1)) { trace.write(tr_id + ": unexpected result set"); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } // readDesc
public virtual void LockConnection(bool nesting_allowed) { lock (lock_obj) { System.Threading.Thread currentThread = System.Threading.Thread.CurrentThread; while (lock_active) // if already locked by someone { // if recursively locked by the same thread, // make sure it was intentional as in the case // of the DTCEnlistment's TxResourceAsyncThread. if (lock_thread_id == currentThread.ManagedThreadId) { if (lock_nesting_allowed) { lock_nest_count++; return; } if (trace.enabled(1)) // unintentional nested locking! { trace.write(title + ".lock(): nested locking!!, current " + lock_thread_id.ToString()); } throw SqlEx.get(ERR_GC4005_SEQUENCING); } try { System.Threading.Monitor.Wait(lock_obj); // wait for a Pulse from another // thread's UnlockConnection } catch (System.Exception /* ignore */) { } } // we have the lock! lock_thread_id = currentThread.ManagedThreadId; lock_active = true; lock_nesting_allowed = nesting_allowed; lock_nest_count = 1; if (trace.enabled(5)) { trace.write(title + ".lock(): locked by " + lock_thread_id.ToString()); } } // end lock(lock_obj) return; }
// skip /* ** Name: need ** ** Description: ** Determines if sufficient data is available to read an ** object of a particular size. Atomic objects may not be ** split across buffers. Non-atomic objects may be split ** and may need to be read piece by piece. ** ** Input: ** size Amount of data required. ** atomic True if data may not be split. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 16-Jun-99 (gordy) ** Created. ** 10-Sep-99 (gordy) ** Moved check for TL disconnect request to receive(). ** 22-Sep-99 (gordy) ** Added atomic indicating to permit handling of split arrays. ** 25-Jan-00 (gordy) ** Added tracing.*/ /// <summary> /// Determines if sufficient data is available to read an /// object of a particular size. Atomic objects may not be /// split across buffers. Non-atomic objects may be split /// and may need to be read piece by piece. /// </summary> /// <param name="size">Amount of data required.</param> /// <param name="atomic">True if data may not be split.</param> private void need(int size, bool atomic) { int available; while ((available = avail()) < size) { short tl_id; if (available > 0) { if (!atomic) { break; } else { if (trace.enabled(1)) { trace.write(title + ": atomic value split (" + available + "," + size + ")"); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } } if ((tl_id = receive()) != DAM_TL_DT) { if (trace.enabled(1)) { trace.write(title + ": invalid TL packet ID 0x" + System.Convert.ToString(tl_id, 16)); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } } if (trace.enabled(5)) { if (available < size) { trace.write(title + ": reading " + available + " (" + size + " requested)"); } else { trace.write(title + ": reading " + size); } } return; } // need
RsltByref ( DrvConn conn, AdvanCall stmt, AdvanRSMD rsmd, long stmt_id, bool do_preLoad ) : base(conn, stmt, rsmd, stmt_id, 1, false) /* ** BYREF parameters produce a single result row. ** Even if row may be pre-loaded, we don't want ** the super-class to do the loading. */ { rs_max_rows = 1; tr_id = "Byref[" + inst_id + "]"; /* ** Pre-load the row if available, otherwise disable pre- ** fetching to better handle the single expected row ** (pre-loading requires pre-fetch to be enabled). */ if (do_preLoad) { preLoad(); } else { disablePreFetch(); } /* ** Load the single expected row and close the ** server statement to unlock the connection. */ try { if (!next()) { throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } } catch (SqlEx) { throw; } finally { try { closeCursor(); } catch (SqlEx) {} } return; } // RsltByref
} // setProcResult /* ** Name DataTruncation ** ** Description: ** Constructs an IngresError for a data truncation warning. ** ** Input: ** colName column name that was truncated. ** colOrdinal ordinal number of column. ** parm if true, it's a parameter, else a result column. ** reading if ture, truncated on read, else truncated on write ** ** Output: ** Build an error ** "Data truncation on read/write of parameter/result column." + ** "<column name> at ordinal nn" ** ** Returns: ** IngresError. ** ** History: ** 29-Aug-02 (thoda04) ** Created. */ internal Exception DataTruncation( string colName, int colOrdinal, bool parm, bool reading) { return(SqlEx.get( "Data truncation on " + (reading ? "read of " : "write of ") + (parm ? "parameter " : "result column ") + (colName == null ? "" : colName.ToString()) + " at ordinal " + colOrdinal + ".", "01004", 0)); // SQLState and native code } // DataTruncation
} // begin /* ** Name: flush ** ** Description: ** Send output buffer to server. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 9-Jun-99 (gordy) ** Created. ** 29-Sep-99 (gordy) ** Moved socket flush to flushBuffer().*/ /// <summary> /// Send output buffer to server. /// </summary> protected internal virtual void flush() { lock (this) { if (closed || outputStream == null) { throw SqlEx.get(ERR_GC4004_CONNECTION_CLOSED); } setSegSize(); // Update current segment length. flushBuffer(true); return; } } // flush
} // readBytes /* ** Name: readString ** ** Description: ** Read a string from the input stream. The string is treated ** as an atomic value and the entire requested length must be ** available. The input bytes are converted to Unicode using ** the character encoding provided. Strings which are split ** must be read as byte arrays. ** ** Input: ** length Number of bytes in input string. ** char_set Character encoding for conversion. ** ** Output: ** None. ** ** Returns: ** String String value from input stream. ** ** History: ** 16-Jun-99 (gordy) ** Created. ** 22-Sep-99 (gordy) ** Added character set/encoding.*/ /// <summary> /// Read a string from the input stream. The string is treated /// as an atomic value and the entire requested length must be /// available. The input bytes are converted to Unicode using /// the character encoding provided. Strings which are split /// must be read as byte arrays. /// </summary> /// <param name="length">Number of bytes in input string.</param> /// <param name="char_set">Character encoding for conversion.</param> /// <returns>String value from input stream.</returns> public virtual String readString(int length, CharSet char_set) { String str; need(length, true); try { str = char_set.getString(buffer, data_ptr, length); } catch (Exception ex) { throw SqlEx.get(ERR_GC401E_CHAR_ENCODE, ex); // Should not happen! } data_ptr += length; return(str); } // readString
} // DataTruncation /* ** Name DataTruncation ** ** Description: ** Constructs an IngresError for a data truncation warning. ** ** Input: ** colOrdinal ordinal number of column. ** parm if true, it's a parameter, else a result column. ** reading if ture, truncated on read, else truncated on write ** ** Output: ** Build an error ** "Data truncation on read/write of parameter/result column." + ** "at ordinal nn" ** ** Returns: ** IngresError. ** ** History: ** 29-Aug-02 (thoda04) ** Created. */ internal Exception DataTruncation( int colOrdinal, bool parm, bool reading, int dataSize, int transferSize) { return(SqlEx.get( "Data truncation on " + (reading ? "read of " : "write of ") + (parm ? "parameter " : "result column ") + "at ordinal " + colOrdinal + ". Original size of data = " + dataSize + ". Size after truncation = " + transferSize + ".", "01004", 0)); // SQLState and native code } // DataTruncation
check(int index, bool expand) { lock (this) { if (index < 0 || (index >= param_cnt && !expand)) { throw SqlEx.get(ERR_GC4011_INDEX_RANGE); } /* ** Expand parameter array if necessary. */ if (index >= parameters.Length) { int new_max; for ( new_max = parameters.Length + ARRAY_INC; index >= new_max; new_max += ARRAY_INC ) { ; } Param[] new_array = new Param[new_max]; Array.Copy(parameters, 0, new_array, 0, parameters.Length); parameters = new_array; } /* ** Adjust parameter counter if index references a new parameter. ** Allocate new parameter objects (if needed) for parameters in ** the range not yet initialized. */ for ( ; param_cnt <= index; param_cnt++) { if (parameters[param_cnt] == null) { parameters[param_cnt] = new Param(); } } return; } } // check
/* ** Name: encode ** ** Description: ** Encode a string using a key. The key and string are translated ** to the Server character set prior to encoding. An optional key ** mask, CompatCI.CRYPT_SIZE in length, will be combined with the ** key if provided. ** ** Input: ** key Encryption key. ** mask Key mask, may be NULL. ** data Data to be encrypted ** ** Output: ** None. ** ** Returns: ** byte [] Encrypted data. ** ** History: ** 22-Sep-99 (gordy) ** Created. ** 6-Sep-02 (gordy) ** Character encoding now encapsulated in CharSet class. ** 6-jul-04 (gordy; ported by thoda04) ** Added key mask. */ public virtual byte[] encode(String key, byte[] mask, String data) { byte[] buff; try { // encode password using obfuscated encode name method buff = encode(getCharSet().getBytes(key), mask, getCharSet().getBytes(data)); } catch (Exception ex) { throw SqlEx.get(ERR_GC401E_CHAR_ENCODE, ex); // Should not happen! } return(buff); }
} // close /* ** Name: cancel ** ** Description: ** Issues an interrupt to the Data Access Server which ** will attempt to cancel any active query. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** void. ** ** History: ** 4-Nov-99 (gordy) ** Created. ** 26-Dec-02 (gordy) ** Allocate cncl buffer on first request. */ public virtual void cancel() { lock (this) { /* ** Allocate cancel buffer on first request. */ if (cncl == null) { try { // wrap the socket's NetworkStream in our OutputStream OutputStream outputStream = new OutputStream(socket.GetStream()); cncl = new OutBuff(outputStream, ConnID, 16); cncl.TL_ProtocolLevel = this.TL_ProtocolLevel; } catch (Exception ex) { if (trace.enabled(1)) { trace.write(title + ": error creating cancel buffer: " + ex.Message); } disconnect(); throw SqlEx.get(ERR_GC4001_CONNECT_ERR, ex); } } try { if (trace.enabled(2)) { trace.write(title + ": interrupt network connection"); } cncl.begin(DAM_TL_INT, 0); cncl.flush(); } catch (SqlEx ex) { disconnect(); throw ex; } return; } } // cancel
} // clearBuffer /* ** Name: flushBuffer ** ** Description: ** Write buffer to output stream and clear output buffer. ** The output stream is not actually flushed, since this ** routine may be called between a pair of buffers. ** ** Input: ** force Force socket to flush buffers? ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 9-Jun-99 (gordy) ** Created. ** 29-Sep-99 (gordy) ** Moved socket flush (optional) to this routine so ** it can be requested from local methods without the ** overhead of calling flush().*/ /// <summary> /// Write buffer to output stream and clear output buffer. The /// output stream is not actually flushed, since this routine /// may be called between a pair of buffers. /// </summary> /// <param name="force">True to force socket to flush buffers</param> private void flushBuffer(bool force) { if (buf_len <= 0) { return; } if (trace.enabled(4)) { trace.write(title + ": sending " + buf_len + " bytes."); trace.hexdump(buffer, 0, buf_len); } try { outputStream.write(buffer, 0, buf_len); } catch (Exception ex) { if (trace.enabled(1)) { trace.write(title + ": write error: " + ex.Message); } clearBuffer(); close(); throw SqlEx.get(ERR_GC4003_CONNECT_FAIL, ex); } clearBuffer(); if (force) { try { outputStream.flush(); } catch (System.Exception ex) { if (trace.enabled(1)) { trace.write(title + ": flush error: " + ex.Message); } close(); throw SqlEx.get(ERR_GC4003_CONNECT_FAIL, ex); } } return; } // flushBuffer
} // InBuff /* ** Name: receive ** ** Description: ** Read the next message. Any data remaining in the current ** message is discarded. A server abort (TL disconnect request) ** is processed here and results in an exception being raised. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** short TL Message type. ** ** History: ** 16-Jun-99 (gordy) ** Created. ** 10-Sep-99 (gordy) ** Check for TL disconnect request. ** 28-Jan-00 (gordy) ** Added TL msg ID tracing. ** 20-Dec-02 (gordy) ** Header ID now protocol level dependent. Moved TL DR ** processing to MSG classes where it is more appropriatte. */ /// <summary> /// Read the next message. Any data remaining in the current /// message is discarded. /// </summary> /// <returns>TL Message type.</returns> public virtual short receive() { int hdr_id; short msg_id; /* ** Get the next segment in the input buffer. ** If buffer is empty, receive next buffer. */ base.next(); /* ** Read and validate packet header: ID and type. */ hdr_id = readInt(); if ( (proto_lvl <= DAM_TL_PROTO_1 && hdr_id != DAM_TL_ID_1) || (proto_lvl >= DAM_TL_PROTO_2 && hdr_id != DAM_TL_ID_2) ) { if (trace.enabled(1)) { trace.write(title + ": invalid TL header ID"); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } /* ** Determine type of message. A disconnect request is only ** issued by the server when aborting the connection. Handle ** this exceptional case directly. */ msg_id = readShort(); if (trace.enabled(3)) { trace.write(title + ": recv TL packet " + IdMap.map(msg_id, tlMap) + " length " + avail()); } return(msg_id); } // receive
/* ** Name: MsgConn ** ** Description: ** Class constructor. Opens a socket connection to target ** host and initializes the I/O buffers. Sends the DAM-ML ** connection parameters and returns the response. ** ** The DAM-ML connection parameters are formatted as an ** array of bytes. The byte array reference is provided as ** the first entry in a multi-dimensional array parameter, ** msg_cp. The input array reference is sent to the ** server and the response is placed in a new byte array ** whose reference is returned in place of the input ** reference, msg_cp. ** ** Input: ** targetList Hostname/port target list. ** msg_cp Message layer parameter info. ** ** Output: ** msg_cp DAM-ML connection parameter response. ** ** History: ** 7-Jun-99 (gordy) ** Created. ** 10-Sep-99 (gordy) ** Parameterized the initial TL connection data. ** 17-Nov-99 (gordy) ** Extracted I/O functionality to DbConnIo, DbConnOut, DbConnIn. ** 1-Mar-07 (gordy, ported by thoda04) ** Dropped char_set parameter, don't default CharSet. ** 3-Nov-08 (gordy, ported by thoda04) ** Separated connection logic from super-class constructors ** to facilitate multiple server targets. ** 5-May-09 (gordy, ported by thoda04) ** Multiple host/port target list is parsed into host/port pairs. */ /// <summary> /// Class constructor. Opens a socket connection to target /// host and initializes the I/O buffers. Sends the DAM-ML /// connection parameters and returns the response. /// The DAM-ML connection parameters are formatted as an /// array of bytes. The byte array reference is provided as /// the first entry in a multi-dimensional array parameter, /// msg_cp. The input array reference is sent to the /// server and the response is placed in a new byte array /// whose reference is returned in place of the input /// reference, msg_cp. /// </summary> /// <param name="targetListString">Hostname/port target list.</param> /// <param name="msg_cp">Message layer parameter info.</param> public MsgConn(String targetListString, ref byte[] msg_cp) { title = "Msg[" + ConnID + "]"; TargetList targets; SqlEx lastEx = null; try { targets = getTargets(targetListString); } catch (SqlEx) { throw; } catch (Exception ex) { if (trace.enabled(1)) { trace.write(title + ": URL parsing error - " + ex.Message); } throw SqlEx.get(ERR_GC4000_BAD_URL, ex); } targets.RandomizeOrder(); foreach (Target target in targets) { try { connect(target.Host, target.Port); sendCR(msg_cp); msg_cp = recvCC(); return; // Connection established } catch (SqlEx ex) { lastEx = ex; } } // end for loop thru targets throw (lastEx != null) ? lastEx : SqlEx.get(ERR_GC4000_BAD_URL); } // MsgConn
write(IByteArray bytes, int offset, int length) { if (!need(length)) { if (trace.enabled(1)) { trace.write(title + ": ByteArray length " + length + " too long"); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } if (trace.enabled(4)) { trace.write(title + ": appending " + length); } length = bytes.get(offset, length, buffer, data_ptr); data_ptr += length; return(length); } // write
} // getHost /* ** Name: getPort ** ** Description: ** Extracts and returns the port number from the target string: ** ** <host>:<port>{,<port>} ** ** The port may be specified as a numeric string or in standard ** Ingres installation ID format such as II0. ** ** Input: ** target Host/port list target string. ** ** Output: ** None. ** ** Returns: ** String [] Port ID list. ** ** History: ** 7-Jun-99 (gordy) ** Created. ** 1-Oct-02 (gordy) ** Extracted symbolic port translation to separate method. ** 19-Oct-06 (lunbr01) Sir 116548 ** Allow for IPv6 addresses enclosed in square brackets '[]'. ** 3-Nov-08 (gordy, ported by thoda04) ** List of port ID's now supported and returned as individual ** entries in an array. Ports are no longer translated into ** numeric values. */ private String[] getPort(String target, String defaultPorts) { int index = 0; /* ** Skip past IPv6 host address if present. */ if (target.StartsWith("[")) { if ((index = target.IndexOf(']')) < 0) { if (trace.enabled(1)) { trace.write(title + ": right bracket ']' missing " + "in IPv6 address '" + target + "'"); } throw SqlEx.get(ERR_GC4000_BAD_URL); } } index = target.IndexOf(':', index); if (index < 0 || target.Length <= (index + 1)) { if (trace.enabled(1)) { trace.write(title + ":port number missing '" + target + "'"); } if (defaultPorts == null || defaultPorts.Length == 0) { return new String[] { "II7" } } ; // default else { return(parseList(defaultPorts, ',')); } } return(parseList(target, ',', index + 1, 0)); } // getPort
} // write /* ** Name: write (inputStream) ** ** Description: ** Append input stream to the output buffer. This ** routine does not split buffers, the input stream ** is read until the buffer is filled or the stream ** is exhausted. The number of bytes read from the ** input stream is returned. It is the callers ** responsibility to assure that there is space ** available in the output buffer. ** ** Input: ** inputStream InputStream ** ** Output: ** None. ** ** Returns: ** short Number of bytes written. ** ** History: ** 29-Sep-99 (gordy) ** Created. */ /// <summary> /// Append input stream to the output buffer. This /// routine does not split buffers, the input stream /// is read until the buffer is filled or the stream /// is exhausted. The number of bytes read from the /// input stream is returned. It is the callers /// responsibility to assure that there is space /// available in the output buffer. /// </summary> /// <param name="inputStream"></param> /// <returns></returns> public virtual short write(InputStream inputStream) { int length = avail(); try { length = inputStream.read(buffer, data_ptr, length); } catch (System.Exception ex) { throw SqlEx.get(ERR_GC4007_BLOB_IO, ex); } if (length < 0) { length = 0; } data_ptr += length; if (trace.enabled(5)) { trace.write(title + ": appending " + length); } return((short)length); } // write
} // flushBuffer /* ** Name: fillBuffer ** ** Description: ** Read buffer from input stream if requested amount of data ** is not currently available ((seg_beg + size) > buf_len). ** ** Input: ** size Amount of data required. ** ** Output: ** None. ** ** Returns: ** void ** ** History: ** 9-Jun-99 (gordy) ** Created. ** 17-nov-04 (thoda04) Bug 113485 ** Trap closed connection (EOF returned) on Read of server data. */ /// <summary> /// Read buffer from input stream if requested amount of data /// is not currently available ((seg_beg + size) > buf_len). /// </summary> /// <param name="size">Amount of data required.</param> private void fillBuffer(int size) { /* ** Check to see if data is currently available. */ if ((seg_beg + size) <= buf_len) { return; } /* ** The buffer size should have been negotiated ** at connection time and any segment should ** fit in the buffer. */ if (size > buf_max) { if (trace.enabled(1)) { trace.write(title + ": buffer overflow (" + size + "," + buf_max + ")"); } close(); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } /* ** Maximize space available for new data. */ if (seg_beg > 0) { int offset; for (offset = 0; (seg_beg + offset) < buf_len; offset++) { buffer[offset] = buffer[seg_beg + offset]; } buf_len -= seg_beg; seg_end -= seg_beg; seg_beg = 0; } while (buf_len < size) { int length; if (trace.enabled(5)) { trace.write(title + ": reading " + (buf_max - buf_len) + " bytes (" + size + " requested, " + buf_len + " buffered)"); } try { length = inputStream.Read(buffer, buf_len, buf_max - buf_len); } catch (Exception ex) { if (trace.enabled(1)) { trace.write(title + ": read error: " + ex.Message); } close(); throw SqlEx.get(ERR_GC4003_CONNECT_FAIL, ex); } if (length < 1) // if EOF due to server closing the connection { if (trace.enabled(1)) { trace.write(title + ": connection closed by server"); } close(); throw SqlEx.get(ERR_GC4003_CONNECT_FAIL); } if (trace.enabled(4)) { trace.write(title + ": received " + length + " bytes."); trace.hexdump(buffer, buf_len, length); } buf_len += length; } return; } // fillBuffer
} // setCharSet /* ** Name: translatePortID ** ** Description: ** Translates a symbolic or numeric port ID to a port number. ** A negative value is returned if an error occurs. ** ** Input: ** port Port ID. ** ** Ouptut: ** None. ** ** Returns: ** int Port number. ** ** History: ** 1-Oct-02 (gordy) ** Extracted from getPort(). ** 3-Nov-08 (gordy, ported by thoda04) ** Throws exception if symbolic port ID cannot be translated ** into numeric value. ** 3-Nov-08 (gordy, ported by thoda04) ** Support extended subports. */ private int translatePortID(String port) { int portnum; int subport = 0; char c0, c1; /* ** Check for Ingres Installation ID format: [a-zA-Z][a-zA-Z0-9]{n} ** where n is [0-9] | 0[0-9] | 1[0-5] */ switch (port.Length) { case 4: /* ** Check most significant digit of subport. */ c0 = port[2]; if (!Char.IsDigit(c0)) // symbolic like II15 ? { break; /* Not a symbolic port ID */ } /* ** Save most significant digit so that it may be ** combined with least significant digit below. */ subport = Int32.Parse(c0.ToString()) * 10; goto case 3; // Fall through to process least significant digit... case 3: /* ** Check least significant digit of subport ** (Optional most significant digit may be ** present and was processed above). */ c0 = port[port.Length - 1]; // look at last digit if (!Char.IsDigit(c0)) // symbolic like II15 or II7? { break; /* Not a symbolic port ID */ } /* ** Combine least significant digit with optional ** most significant digit processed in prior case ** and check for valid range. */ subport += Int32.Parse(c0.ToString()); if (subport > 15) { break; /* Not a symbolic port ID */ } goto case 2; // Fall through to process instance ID... case 2: /* ** Check for valid instance ID. */ c0 = port[0]; c1 = port[1]; if (!(Char.IsLetter(c0) && // symbolic like II ? Char.IsLetterOrDigit(c1))) { break; /* Not a symbolic port ID */ } /* ** Map symbolic port ID to numeric port. ** Subport was generated in prior cases ** or defaulted to 0. */ c0 = Char.ToUpper(c0); c1 = Char.ToUpper(c1); portnum = (((subport > 0x07) ? 0x02 : 0x01) << 14) | ((c0 & 0x1f) << 9) | ((c1 & 0x3f) << 3) | (subport & 0x07); if (trace.enabled(5)) { trace.write(title + ": " + port + " => " + portnum); } return(portnum); } try { portnum = Int32.Parse(port); } catch (Exception) { if (trace.enabled(1)) { trace.write(title + ": invalid port ID '" + port + "'"); } throw SqlEx.get(ERR_GC4000_BAD_URL); } return(portnum); } // translatePortID
connect(String host, String portID) { int i; System.Net.IPAddress[] ipAddresses; System.Net.IPEndPoint ipEndPoint = null; socket = null; ipAddresses = null; ipAddress = null; socketIsLocal = false; try { if (trace.enabled(2)) { trace.write(title + ": opening network connection '" + host + "', '" + portID + "'"); } if (host == null || host.Length == 0 || // if local machine host.ToLowerInvariant() == "(local)") { ipAddresses = System.Net.Dns.GetHostAddresses(System.Net.Dns.GetHostName()); socketIsLocal = true; } else { ipAddresses = System.Net.Dns.GetHostAddresses(host); } int port = translatePortID(portID); for (i = 0; i < ipAddresses.Length; i++) // loop addresses { ipAddress = ipAddresses[i]; if (trace.enabled(3)) { string address = ipAddress.ToString() + " "; address += ipAddress.AddressFamily.ToString(); if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { address += "(IPv4)"; } else if (ipAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) { address += "(IPv6),"; address += "ScopeId=" + ipAddress.ScopeId.ToString(); } trace.write(title + ": host address " + address + "; port=" + port); } ipEndPoint = new System.Net.IPEndPoint(ipAddress, port); socket = new MsgSocket(ipAddress.AddressFamily); try { socket.Connect(ipEndPoint); if (trace.enabled(3)) { trace.write(title + ": connected to host address and port"); } return; // success! } catch (Exception ex) { if (trace.enabled(1)) { trace.write(title + ": connection failed to address[" + i + "]=" + ipAddress + " - " + ex.Message); } if (i == (ipAddresses.Length - 1)) { throw; // continue throw of last exception up chain } // else try next address on list. } } // end for loop through ipHostInfo.AddressList if (i == ipAddresses.Length) { // should not have reached this point but we will bullet-proof trace.write(title + ": connection error - all addresses failed"); throw SqlEx.get(ERR_GC4001_CONNECT_ERR); } } // end try block catch (System.Net.Sockets.SocketException ex) { if (trace.enabled(1)) { trace.write(title + ": error connecting to host - (SocketException) " + ex.Message); } SqlEx pex = (SqlEx) SqlEx.get(ERR_GC4001_CONNECT_ERR, ex); // "Unable to establish connection due to communications error." if (ipAddresses == null) // is it the GetHostAddresses( ) { pex = SqlEx.get(pex, SqlEx.get( "SocketException thrown " + "by System.Net.Dns.GetHostAddresses(" + (host == null?"<null>":("\"" + host + "\"")) + ") " + "in resolving the hostname", "08001", ex.ErrorCode)); } else // must be TcpClient( ) call { pex = SqlEx.get(pex, SqlEx.get( "SocketException thrown " + "by System.Net.Sockets.TcpClient( ) " + "while connecting to host " + (host == null ? "<null>" : ("\"" + host + "\".")), "08001", ex.ErrorCode)); } socket = null; ipAddress = null; throw pex; } catch (System.Exception ex) { if (trace.enabled(1)) { trace.write(title + ": error connecting to host - " + ex.Message); } socket = null; ipAddress = null; throw SqlEx.get(ERR_GC4001_CONNECT_ERR, ex); } } // connect
} // readError /* ** Name: readResult ** ** Description: ** Read a RESULT message. The RESULT message parameters are read ** and saved in instance fields. ** ** Returning a value of TRUE will interrupt the processing of ** server messages by readResults() and cause an immediate return. ** ** Input: ** None. ** ** Output: ** None. ** ** Returns: ** boolean True for interrupt, False to continue. ** ** History: ** 31-Oct-02 (gordy) ** Extracted from readResults(). ** 21-Feb-03 (gordy) ** Added table and object keys. ** 20-Aug-03 (gordy) ** Consolidated result flags into single parameter. ** 6-Oct-03 (gordy) ** Renamed and changed return type to reflect ability ** for intermediate RESULT messages to be processed. */ protected internal virtual bool readResult() { while (msg.moreData()) { short param_id = msg.readShort(); short param_len = msg.readShort(); switch (param_id) { case MSG_RP_ROWCOUNT: switch (param_len) { case 1: rslt_val_rowcnt = msg.readByte(); break; case 2: rslt_val_rowcnt = msg.readShort(); break; case 4: rslt_val_rowcnt = msg.readInt(); break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid rowcount len " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_rowcount = true; break; case MSG_RP_XACT_END: if (param_len > 0) { if (trace.enabled(1)) { trace.write(tr_id + ": Invalid XACT param len " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_flags |= MSG_RF_XACT_END; conn.endXact(); break; case MSG_RP_STMT_ID: if (param_len != 8) { if (trace.enabled(1)) { trace.write(tr_id + ": Invalid STMT_ID len " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_val_stmt = (((long)msg.readInt()) << 32) | (((long)msg.readInt()) & (int)(-(0x100000000 - 0xffffffff))); break; case MSG_RP_FETCH_LIMIT: switch (param_len) { case 1: rslt_val_fetch = msg.readByte(); break; case 2: rslt_val_fetch = msg.readShort(); break; case 4: rslt_val_fetch = msg.readInt(); break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid fetchlimit len " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_prefetch = true; break; case MSG_RP_EOD: if (param_len != 0) { trace.write(tr_id + ": Invalid EOD len " + param_len); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_flags |= MSG_RF_EOD; break; case MSG_RP_PROC_RESULT: switch (param_len) { case 1: setProcResult((int)msg.readByte()); break; case 2: setProcResult((int)msg.readShort()); break; case 4: setProcResult(msg.readInt()); break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid proc result len" + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } break; case MSG_RP_READ_ONLY: if (param_len != 0) { trace.write(tr_id + ": Invalid READ_ONLY len " + param_len); throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_flags |= MSG_RF_READ_ONLY; break; case MSG_RP_TBLKEY: if (param_len != MSG_RPV_TBLKEY_LEN) { if (trace.enabled(1)) { trace.write(tr_id + ": Invalid table key length: " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } if (rslt_val_tblkey == null) { rslt_val_tblkey = new byte[param_len]; } msg.readBytes(rslt_val_tblkey, 0, param_len); rslt_tblkey = true; break; case MSG_RP_OBJKEY: if (param_len != MSG_RPV_OBJKEY_LEN) { if (trace.enabled(1)) { trace.write(tr_id + ": Invalid object key length: " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } if (rslt_val_objkey == null) { rslt_val_objkey = new byte[param_len]; } msg.readBytes(rslt_val_objkey, 0, param_len); rslt_objkey = true; break; case MSG_RP_FLAGS: { int flags; switch (param_len) { case 1: flags = msg.readByte(); break; case 2: flags = msg.readShort(); break; case 4: flags = msg.readInt(); break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid result flags length: " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_flags |= flags; if ((flags & MSG_RF_XACT_END) != 0) { conn.endXact(); } break; } case MSG_RP_ROW_STATUS: switch (param_len) { case 1: rslt_val_rowstat = msg.readByte(); break; case 2: rslt_val_rowstat = msg.readShort(); break; case 4: rslt_val_rowstat = msg.readInt(); break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid row status length: " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_items |= RSLT_ROW_STAT; break; case MSG_RP_ROW_POS: switch (param_len) { case 1: rslt_val_rowpos = msg.readByte(); break; case 2: rslt_val_rowpos = msg.readShort(); break; case 4: rslt_val_rowpos = msg.readInt(); break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid row position length: " + param_len); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } rslt_items |= RSLT_ROW_POS; break; default: if (trace.enabled(1)) { trace.write(tr_id + ": Invalid result param ID " + param_id); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } // end switch } // end while return(false); } // readResult
getColumnClassName(int column) { String name; System.Type oc = typeof(System.Object); if (trace.enabled()) { trace.log(title + ".getColumnClassName( " + column + " )"); } switch (desc[(column = columnMap(column))].sql_type) { case ProviderType.Boolean: oc = typeof(System.Boolean); break; case ProviderType.TinyInt: case ProviderType.SmallInt: case ProviderType.Integer: oc = typeof(System.Int32); break; case ProviderType.BigInt: oc = typeof(System.Int64); break; case ProviderType.Single: oc = typeof(System.Single); break; case ProviderType.Double: oc = typeof(System.Double); break; case ProviderType.Numeric: case ProviderType.Decimal: oc = typeof(System.Decimal); break; case ProviderType.Char: case ProviderType.VarChar: case ProviderType.LongVarChar: oc = typeof(String); break; case ProviderType.Binary: case ProviderType.VarBinary: case ProviderType.LongVarBinary: oc = typeof(System.Byte); break; case ProviderType.Date: case ProviderType.Time: case ProviderType.DateTime: oc = typeof(System.DateTime); break; default: if (trace.enabled(1)) { trace.write(title + ": invalid SQL type " + desc[column].sql_type); } throw SqlEx.get(ERR_GC4002_PROTOCOL_ERR); } name = oc.FullName; if (trace.enabled()) { trace.log(title + ".getColumnClassName: " + name); } return(name); } // getColumnClassName