Tutorial
Getting a Connection
import Oracle
username = "my_username"
password = "my_password"
connect_string = "//IP_ADDRESS/XE" # a valid Oracle connect string
conn = Oracle.Connection(username, password, connect_string)
Currently, this driver only supports connections using ASCII or UTF-8 encodings. All connections are created using UTF-8 encoding by default, for both CHAR
and NCHAR
data types.
To connect as SYSDBA
, use the appropriate auth_mode
parameter.
conn = Oracle.Connection(username, password, connect_string, auth_mode=Oracle.ORA_MODE_AUTH_SYSDBA)
You should always close connections using Oracle.close
method.
Oracle.close(conn)
Executing a Statement
Oracle.execute(conn, "CREATE TABLE TB_TEST ( ID INT NULL )")
Oracle.execute(conn, "INSERT INTO TB_TEST ( ID ) VALUES ( 1 )")
Oracle.execute(conn, "INSERT INTO TB_TEST ( ID ) VALUES ( null )")
Oracle.commit(conn) # will commit 2 lines
Oracle.execute(conn, "INSERT INTO TB_TEST ( ID ) VALUES ( 3 )")
Oracle.rollback(conn) # abort insertion of the third line
Binding values to a Statement
Oracle.execute(conn, "CREATE TABLE TB_BIND ( ID NUMBER(15,0) NULL, FLT NUMBER(15,4) NULL, STR VARCHAR(255) NULL, DT DATE NULL)")
# get an explicit reference to a statement
stmt = Oracle.Stmt(conn, "INSERT INTO TB_BIND ( ID, FLT, STR, DT ) VALUES ( :id, :flt, :str, :dt )")
# will add a single line to TB_BIND
stmt[:id] = 1
stmt[:flt] = 10.23
stmt[:str] = "a string"
stmt[:dt] = Date(2018,12,31)
Oracle.execute(stmt)
Oracle.commit(conn)
Oracle.close(stmt)
Whenever you get an explicit reference to a statement, you should always use Oracle.close
method when you're done with it.
The next example constructs a Statement using the do-syntax, that automatically closes the statement at the end. It also shows how to bind values by position.
Oracle.stmt(conn, "INSERT INTO TB_BIND ( ID, FLT, STR, DT ) VALUES ( :id, :flt, :str, :dt )") do stmt
stmt[1] = 1
stmt[2] = 10.1234
stmt[3] = "this is a string"
stmt[4, Date] = missing # we must inform the type when setting value as missing
Oracle.execute(stmt)
Oracle.commit(conn)
end
Executing a Query
Use Oracle.query
to execute a query. It returns a ResultSet
, which is a table-like struct. All data is fetched from the statement before returning the ResultSet
.
rs = Oracle.query(conn, "SELECT ID, FLT, STR, DT FROM TB_BIND")
println(rs[2, 1]) # will print the element at row 2, column 1.
println(rs[2, "ID"]) # will print element at row 2, column ID (same as column 1).
println(rs[:, 1]) # will print all the elements in column 1.
The last example was easy to use, but maybe your memory can't hold all the data in the ResultSet. Use Oracle.query
method with do-syntax to get a reference to a cursor, which will fetch one row at a time.
Oracle.query(conn, "SELECT * FROM TB_BIND") do cursor
for row in cursor
# row values can be accessed using column name or position
println( row["ID"] ) # same as row[1]
println( row["FLT"] )
println( row["STR"] )
println( row["DT"] ) # same as row[4]
end
end
You can also use a prepared statement to execute a query.
stmt = Oracle.Stmt(conn, "SELECT FLT FROM TB_BIND WHERE ID = :id")
stmt[:id] = 1
Oracle.query(stmt) do cursor
for row in cursor
println(row["FLT"])
end
end
Oracle.close(stmt)
There is also the possibility to fetch one row at a time manually, with a small overhead when compared to previous methods.
stmt = Oracle.Stmt(conn, "SELECT FLT FROM TB_BIND")
Oracle.execute(stmt)
row = Oracle.fetchrow(stmt)
while row != nothing
println(row[1])
row = Oracle.fetchrow(stmt)
end
Oracle.close(stmt)
Batch statement execution
If you need to execute the same statement many times but binding different values each time, pass a vector of columns to Oracle.execute
method.
This will use the ODPI-C executeMany feature.
NUM_ROWS = 1_000
column_1 = [ i for i in 1:NUM_ROWS ]
column_2 = .5 * column_1
sql = "INSERT INTO TB_BENCH_EXECUTE_MANY ( ID, FLT ) VALUES ( :1, :2 )"
Oracle.execute_many(conn, sql, [ column_1, column_2 ])
It is also possible to bind input and output variables in one go. The following example shows how to insert data to a table that generates keys with a sequence, returning the keys that were created.
Oracle.execute(conn, "CREATE TABLE TB_EXEC_MANY_INOUT ( ID NUMBER(15, 0) NOT NULL, STR VARCHAR2(4000), LB BLOB NOT NULL )")
Oracle.execute(conn, "ALTER TABLE TB_EXEC_MANY_INOUT ADD CONSTRAINT XPK_TB_EXEC_MANY_INOUT PRIMARY KEY (ID)")
Oracle.execute(conn, "CREATE SEQUENCE SQ_TB_EXEC_MANY_INOUT INCREMENT BY 1 START WITH 1001")
input_data = ["input1", "input2", "input3"]
num_iters = length(input_data)
var_input = Oracle.Variable(conn, input_data)
# example of BLOB variables
blob_data = [ rand(UInt8, 5000) for i in 1:num_iters ]
blobs = [ Oracle.Lob(conn, Oracle.ORA_ORACLE_TYPE_BLOB) for i in 1:num_iters ]
for i in 1:num_iters
write(blobs[i], blob_data[i])
end
var_blob = Oracle.Variable(conn, blobs)
var_output = Oracle.Variable(conn, Int, buffer_capacity=num_iters)
vars = Dict(:var_input => var_input, :var_blob => var_blob, :var_output => var_output)
stmt = Oracle.Stmt(conn, "INSERT INTO TB_EXEC_MANY_INOUT ( ID, STR, LB ) VALUES ( SQ_TB_EXEC_MANY_INOUT.nextval, :var_input, :var_blob ) RETURNING ID INTO :var_output")
try
Oracle.execute_many(stmt, num_iters, vars)
Oracle.commit(conn)
Oracle.query(conn, "SELECT ID, STR, LB FROM TB_EXEC_MANY_INOUT ORDER BY ID") do cursor
i = 1
for row in cursor
@test parse(Int, row["STR"][end]) + 1000 == row["ID"]
@test row["ID"] == Oracle.get_returned_data(var_output, i)[1]
@test read(row["LB"]) == blob_data[i]
i += 1
end
end
for i in 1:num_iters
@test Oracle.get_returned_data(var_output, i)[1] == 1000 + i
end
finally
Oracle.close(stmt)
Oracle.execute(conn, "DROP SEQUENCE SQ_TB_EXEC_MANY_INOUT")
Oracle.execute(conn, "DROP TABLE TB_EXEC_MANY_INOUT")
end
Session Pools
A Pool represents a pool of connections, and provides a faster way to acquire connections to the database.
# creates a pool for a maximum of 2 sessions
pool = Oracle.Pool(username, password, connect_string, max_sessions=2, session_increment=1)
conn_1 = Oracle.Connection(pool)
conn_2 = Oracle.Connection(pool) # at this point, we can't acquire more connections
# release a connection so that we can acquire another one.
Oracle.close(conn_1)
# by now, acquiring a new connection should be pretty fast
# since the new connection will be taken from the pool
conn_3 = Oracle.Connection(pool)
# release all connections that are still open
Oracle.close(conn_2)
Oracle.close(conn_3)
Oracle.close(pool)
You should always close Pools using Oracle.close
method.
LOB
Oracle LOB fields can hold up to 4GB of data.
They come in two flavors:
Binary LOBs: BLOB or BFILE.
Character LOBs: CLOB or NCLOB.
LOB values are represented as a value of type Oracle.Lob
in this package.
From a LOB value, you can use read
and write
methods to manipulate whole contents of the LOB value. For incremental reading/writing, you can use open
with do-syntax do get an IO stream out of a Oracle.Lob
.
IO Streams created on Character LOBs use the character index as its position, and only support reading/writing for Char
and String
data types.
You should always close a LOB using Oracle.close
method.
Currently, BFILE is not supported.
Reading from a BLOB
lyric = "hey you. 🎵 🎶 Out there in the cold. getting lonely, getting old. Can you feel me? 📼📼📼📼"
Oracle.execute(conn, "CREATE TABLE TB_BLOB ( b BLOB )")
Oracle.execute(conn, "INSERT INTO TB_BLOB ( B ) VALUES ( utl_raw.cast_to_raw('$lyric'))")
Oracle.query(conn, "SELECT B FROM TB_BLOB") do cursor
for row in cursor
blob = row["B"]
bytes_vector = read(blob) # Vector{UInt8}
println(String(bytes_vector))
end
end
Writing to a BLOB
Follow these steps to write to a BLOB field in the database.
Create a temporary Lob associated with the connection using
Oracle.Lob(connection, oracle_type)
.Write data to the Lob.
Wrap the Lob into a Variable.
Bind the variable to the statement.
Execute the statement.
Oracle.execute(conn, "CREATE TABLE TB_BLOB_VARIABLE ( B BLOB )")
test_data = rand(UInt8, 5000)
# creates a temporary Lob bounded to the Connection
blob = Oracle.Lob(conn, Oracle.ORA_ORACLE_TYPE_BLOB)
# replaces all Lob data with the contents of the array test_data
write(blob, test_data)
# wraps the blob in a Variable
ora_var = Oracle.Variable(conn, blob)
stmt = Oracle.Stmt(conn, "INSERT INTO TB_BLOB_VARIABLE ( B ) VALUES ( :1 )")
# binds the variable to the statement
stmt[1] = ora_var
Oracle.execute(stmt)
Oracle.close(stmt)
Transactions
The way Oracle Database works, “a transaction in Oracle begins when the first executable SQL statement is encountered”.
Use Oracle.commit
to commit and Oracle.rollback
to abort a transaction.
The following example is a valid transaction.
Oracle.execute(conn, "INSERT INTO TB_TEST ( ID ) VALUES ( 1 )") # will start a transaction
Oracle.execute(conn, "INSERT INTO TB_TEST ( ID ) VALUES ( null )")
Oracle.commit(conn) # will commit 2 lines
Oracle.execute(conn, "INSERT INTO TB_TEST ( ID ) VALUES ( 3 )") # will start a new transaction
Oracle.rollback(conn) # abort insertion of the third line