Oracle
The sq Oracle driver implements connectivity for
Oracle Database
.
sakiladb/oracle
image); broader
version coverage may come over time.The driver uses the pure-Go go-ora driver and does not require Oracle Instant Client , OCI , or CGo .
Add source
Use sq add
to add an Oracle source.
sq add 'oracle://user:password@host:1521/service_name'
For the Sakila Oracle test image :
sq add 'oracle://sakila:p_ssW0rd@localhost:1521/SAKILA' --handle @sakila_ora
Connection string format
Use URL-style locations:
oracle://username:password@hostname:1521/service_name
oracle://username:password@hostname/service_name
Query parameters are passed through to go-ora. Useful examples include SSL,
wallet, trace, and timeout settings:
sq add 'oracle://user:password@host:1521/service_name?SSL=true'
sq add 'oracle://user:password@host:1521/service_name?CONNECTION%20TIMEOUT=30'
Quote the location string when it contains ?, &, spaces, or shell-special
characters.
TNSNAMES.ora aliases and Kerberos are not handled by sq directly. Use an
Oracle URL location for sq add.
Notes
Schema and catalog
Oracle’s catalog and schema concepts differ from other databases:
- Catalog: in 12c and later, Oracle is multitenant — a Container
Database (CDB) hosts one or more Pluggable Databases (PDBs), and each
PDB is functionally an independent database with its own schemas, users,
and tables. The PDB is the catalog-equivalent. In a non-CDB or pre-12c
deployment there is just one database, which serves the same role.
In
sq,catalog()returnsSYS_CONTEXT('USERENV', 'DB_NAME'), which yields the PDB name in multitenant deployments and the database name otherwise. - Schema: Oracle schemas are users. In
sq,schema()returns the current Oracle schema (the connected user).sqcan list schemas fromALL_USERS, butCREATE SCHEMAandDROP SCHEMAare not Oracle operations; create or drop Oracle users instead.
The connection URL’s service name is a connection-time routing identifier
the listener uses to pick which database/PDB to attach you to. It is not
itself the catalog — once the session is established, catalog() reflects
the database/PDB the service routed you into.
Unquoted Oracle identifiers are stored uppercase. sq follows that convention
when rendering quoted identifiers, so table and column names created through
sq are typically visible in uppercase in Oracle metadata.
Cross-source operations such as --insert=@dest.tbl from an Oracle source
to a case-sensitive destination (Postgres, ClickHouse) translate column
names case-insensitively against the destination table’s actual columns
before quoting them, so Oracle’s UPPERCASE column names match the
destination’s stored case (typically lowercase) transparently.
Metadata visibility
sq inspect loads base tables, views, and materialized views from
USER_* dictionary views for the connected schema. View rows use
TableType view; materialized views use TableType table with
DBTableType MATERIALIZED VIEW (so they contribute to TableCount).
ListTableNames(schema=...) reads ALL_TABLES, ALL_MVIEWS, and ALL_VIEWS
filtered by owner. TableExists checks USER_OBJECTS for TABLE, VIEW, or
MATERIALIZED VIEW.
Synonyms (resolving through ALL_SYNONYMS to base objects, including DB
links) are not implemented yet.
DBProperties always returns db_name and current_schema from
SYS_CONTEXT. The version field prefers v$instance and falls back to
v$version when v$instance is not readable.
Inspect field provenance
sq inspect populates the fields below from the Oracle data dictionary.
Every column listed is readable by an ordinary user — no DBA privileges
required.
Source-level fields
| Field | Source |
|---|---|
name, schema | SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') |
catalog | SYS_CONTEXT('USERENV', 'DB_NAME') (PDB name in multitenant; database name otherwise) |
user | SYS_CONTEXT('USERENV', 'SESSION_USER') |
db_product | BANNER from V$VERSION (the full descriptive string, e.g. Oracle Database 23ai Free Release …) |
db_version | VERSION_FULL from PRODUCT_COMPONENT_VERSION (e.g. 23.26.1.0.0); falls back to V$INSTANCE.VERSION (DBA-only) and finally to the banner |
size | SUM(bytes) over USER_SEGMENTS — bytes occupied by segments owned by the connected user. The PDB- or database-wide equivalents (DBA_DATA_FILES) require DBA privileges and are not used. |
Per-table fields
| Field | Source |
|---|---|
row_count (tables, materialized views) | NUM_ROWS from USER_TABLES / USER_MVIEWS, with a live SELECT COUNT(*) fallback when the dictionary value is NULL (it stays NULL until DBMS_STATS / ANALYZE has run) |
row_count (views) | always live SELECT COUNT(*) — USER_VIEWS carries no row count |
size (tables, materialized views) | SUM(bytes) from USER_SEGMENTS for the matching segment name |
size (views) | not reported — views have no underlying segment |
SQL rendering
Oracle SQL rendering differs from several other SQL drivers:
- Bind placeholders use Oracle’s numbered form:
:1,:2,:3, and so on. rownum()renders as the portablerow_number() OVER (ORDER BY ...)window function, threading the query’sORDER BYthrough the window definition. Oracle’sROWNUMpseudo-column is intentionally not used because it is assigned at fetch time, beforeORDER BYis applied, which silently produces wrong row numbers when the query also sorts.catalog()renders asSYS_CONTEXT('USERENV', 'DB_NAME'), which yields the PDB name in multitenant deployments and the database name in non-CDB deployments.schema()renders asSYS_CONTEXT('USERENV', 'CURRENT_SCHEMA').avg()andsum()are wrapped inCAST(... AS BINARY_DOUBLE). Oracle returns these aggregates asNUMBER(38, 255)regardless of operand type, whichsqwould otherwise classify asint; the cast pins the result to a float so fractional values scan cleanly. Tradeoff: integer-valued sums lose precision past ~15-17 significant digits — use raw SQL for lossless big-integer aggregation.- Row ranges render using
OFFSET ... FETCH NEXT ... ROWS ONLYfor Oracle 12c and newer. When a row range has no explicit sort,sqadds an Oracle-compatibleORDER BYexpression before the row range. - Scalar selections that need a table source use
FROM DUALwhere required. - The
ASkeyword is stripped from table-alias positions inFROM/JOINclauses (e.g.FROM "tbl" AS "alias"becomesFROM "tbl" "alias"). Oracle acceptsFROM tbl aliasbut rejectsFROM tbl AS alias. Column aliases (e.g.SELECT col AS alias) are unaffected.
Type mapping
Common Oracle types map to sq kinds as follows:
| Oracle type | sq kind |
|---|---|
VARCHAR2, NVARCHAR2, CHAR, NCHAR, CLOB, NCLOB, ROWID | text |
NUMBER(p,0) where p is 1-19 | int |
Other NUMBER values | decimal |
BINARY_FLOAT, BINARY_DOUBLE, FLOAT | float |
DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE | datetime |
BLOB, RAW, LONG RAW | bytes |
| Interval types | text |
When sq creates Oracle tables, it uses Oracle-native equivalents such as
NUMBER(19,0) for int, NUMBER(1,0) for bool, TIMESTAMP for
datetime, and BLOB for bytes.
Database-specific quirks
- Transactions: Same defaults as other SQL drivers via
database/sql; DDL commits open transactions. - TRUNCATE (
sq tbltruncate): Oracle ignores identity sequence reset in the sense of other databases; the driver’sresetflag maps toDROP STORAGEvsREUSE STORAGEonTRUNCATE TABLE. - Empty strings: Oracle treats empty string as
NULL. CREATE TABLE: Defaults avoid unsupported constructs (for example,EMPTY_BLOB()cannot be used as a literal default); Oracle rejects defaults some drivers accept elsewhere.- Boolean values: Oracle has no database-wide boolean column type in the
same sense as other SQL engines, so
sqstores boolean columns asNUMBER(1,0). - DATE and TIME round-tripping: Oracle
DATEincludes time-of-day, and Oracle has no standalone time-only column type. Adateortimecolumn created bysqcan inspect back asdatetime. - Column type changes:
sq tblcolumn-kind alteration is not implemented for Oracle yet.
Local Sakila database
For local development and integration tests, use
sakiladb/oracle
:
docker run -d -p 1521:1521 sakiladb/oracle:latest
sq add 'oracle://sakila:p_ssW0rd@localhost:1521/SAKILA' --handle @sakila_ora
The image uses Oracle Database Free with the Sakila sample schema. Startup can
take several minutes; wait until the database is accepting connections before
running sq ping @sakila_ora or integration tests.
Requirements
No Oracle Instant Client is required. The driver speaks Oracle Net in pure Go.