oracle

package
v3.21.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package oracle proxies Oracle Net (TNS) so that a spec can assert what a service asked its database, without the service being changed and without this package pretending to be Oracle.

It observes and relays. Bytes travel to a real Oracle in both directions untouched; only the client-to-server direction is read, and only far enough to recover the statement. That is why the channel is a few hundred lines rather than a reimplementation of a database: nothing here synthesises a result set, negotiates a data type, or answers a handshake. The cost is that RETURNS cannot work on this channel - a response this package did not author is one it cannot replace - and that is refused when a spec is parsed rather than ignored while it runs.

The framing below was measured against a real Oracle 23ai, and the captured packets in testdata are what the tests read. Two details would have been wrong if taken from the protocol's reputation instead, and both are noted where they are implemented.

Index

Constants

View Source
const (
	TypeConnect  byte = 1
	TypeAccept   byte = 2
	TypeRefuse   byte = 4
	TypeRedirect byte = 5
	TypeData     byte = 6
	TypeResend   byte = 11
	TypeMarker   byte = 12
)

Packet types, of which only Data can carry a statement.

View Source
const HeaderLen = 8

HeaderLen is the fixed TNS header: length, checksum, type, flags, header checksum.

View Source
const MaxPacketLen = 0xFFFF

MaxPacketLen is the largest packet this package frames, and it is a property of the framing rather than a policy: a wide length is only recognised when the header's first two bytes are zero, so the size it can express stops at 64 KiB. A session negotiated with a large SDU could exceed that, and such a packet is refused rather than misread - its high bytes would be non-zero, the narrow reading would name a length far too small, and PacketLen returns ErrImplausibleLength. Loud, and in the safe direction.

Variables

View Source
var (
	// ErrShortHeader means fewer than HeaderLen bytes were available.
	ErrShortHeader = errors.New("oracle: packet shorter than a TNS header")
	// ErrImplausibleLength means the length field named a size this package
	// will not treat as a packet.
	ErrImplausibleLength = errors.New("oracle: implausible TNS packet length")
)

Functions

func PacketLen

func PacketLen(header []byte) (int, error)

PacketLen reads the packet length from a TNS header.

Oracle uses two framings on one connection and the captured packets show both. A CONNECT is sent before any version has been agreed, so its length is the 16-bit field the protocol has always had, with a checksum in the next two bytes. Once the session negotiates, DATA packets put a 32-bit length across all four bytes and leave the checksum zero.

The two are told apart by the high half rather than by tracking connection state, which would make framing depend on having seen the handshake - no use to a proxy that attaches to a connection already in progress. A packet is at least HeaderLen bytes, so a 16-bit length never leaves its first two bytes zero; a 32-bit length is only plausible below the maximum below, so its high two bytes are always zero. The halves therefore cannot both be right, and which one is is decidable from the header alone.

func PacketType

func PacketType(header []byte) byte

PacketType returns the packet's type byte.

func Statement

func Statement(packet []byte) (string, bool)

Statement returns the SQL text carried by a packet, and whether one was found. A packet with no statement in it is the normal case rather than an error: Oracle carries a great deal over one connection that is not a statement.

A statement is stored as a length followed by its bytes. Below 254 the length is a single byte and every client agrees on that. At or above it, 0xFE introduces a wide length - and the clients do NOT agree on what follows it:

sqlplus  fe 15 01 00 00      four bytes LITTLE-endian (277)
ODP.NET  fe 02 01 15         marshalled: one byte saying how many follow,
                             then that many BIG-endian (277)

Both are read, because both were captured from the client that sends them, and the .NET driver is the one a service under test actually uses. Reading only the first form is silent rather than loud: no length agrees, the packet contributes nothing, and an expectation written against a statement past 254 bytes is reported as never called.

The search is for a length that agrees with what follows it, which is a strong constraint: the length must be matched by exactly that many printable bytes, and those bytes must open with a SQL verb. Anything else is not a statement as far as this package is concerned - which is also why two competing readings of the same bytes are safe to try in turn.

Types

type Packet

type Packet struct {
	Type byte
	Raw  []byte
}

Packet is one framed TNS packet, header included.

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

Proxy sits between a service and a real Oracle, relaying both directions untouched and reading the client's statements as they pass.

It does not answer for Oracle. Every byte the service receives came from the database, so a statement's result is whatever the database really returned - which is why this channel supports the assertions about what was asked (ACCESSING_TABLES, VERIFY_OPERATION, VERIFY_WHERE_COLUMNS, VERIFY_WHERE, VERIFY_WRITTEN_VALUES, EXPECT_NOT) and not RETURNS.

func NewProxy

func NewProxy(addr, upstreamAddr string, reg *registry.MockRegistry) *Proxy

NewProxy builds a proxy that listens on addr and relays to upstreamAddr.

func (*Proxy) SetDatabaseName

func (p *Proxy) SetDatabaseName(name string)

SetDatabaseName scopes this proxy's expectations, so that a spec naming a database is only satisfied by the proxy serving it.

func (*Proxy) Start

func (p *Proxy) Start(ctx context.Context) error

Start accepts connections until ctx is done.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL