Python is awesome. I like Python very much, but there are some cases where Python’s performance is just not enough. What other options do we have?
Well, there’s for example GoLang
Problem with GoLang is that there is not a lot on the Internet about using it with Oracle Databases. Let me show you some basic steps for configuring the environment.
The first step is to install GoLang – instructions can be found in here: https://golang.org/doc/install
To connect to Oracle, you need Oracle Client (it can be Oracle Instant Client).
I will configure my environment on my MAC and use Oracle Instant Client for MAC
piclerick:~ inter$ mkdir oracle piclerick:~ inter$ cd oracle piclerick:oracle inter$ ls piclerick:oracle inter$ unzip ~/Downloads/instantclient-basic-macos.x64-18.1.0.0.0.zip Archive: /Users/inter/Downloads/instantclient-basic-macos.x64-18.1.0.0.0.zip inflating: instantclient_18_1/BASIC_README inflating: instantclient_18_1/adrci inflating: instantclient_18_1/genezi linking: instantclient_18_1/libclntsh.dylib -> libclntsh.dylib.18.1 inflating: instantclient_18_1/libclntsh.dylib.18.1 inflating: instantclient_18_1/libclntshcore.dylib.18.1 inflating: instantclient_18_1/libnnz18.dylib linking: instantclient_18_1/libocci.dylib -> libocci.dylib.18.1 inflating: instantclient_18_1/libocci.dylib.18.1 inflating: instantclient_18_1/libociei.dylib inflating: instantclient_18_1/libocijdbc18.dylib inflating: instantclient_18_1/libons.dylib inflating: instantclient_18_1/liboramysql18.dylib creating: instantclient_18_1/network/ inflating: instantclient_18_1/ojdbc8.jar inflating: instantclient_18_1/ucp.jar inflating: instantclient_18_1/uidrvci inflating: instantclient_18_1/xstreams.jar creating: instantclient_18_1/network/admin/ inflating: instantclient_18_1/network/admin/README finishing deferred symbolic links: instantclient_18_1/libclntsh.dylib -> libclntsh.dylib.18.1 instantclient_18_1/libocci.dylib -> libocci.dylib.18.1 piclerick:oracle inter$ unzip ~/Downloads/instantclient-sqlplus-macos.x64-18.1.0.0.0.zip Archive: /Users/inter/Downloads/instantclient-sqlplus-macos.x64-18.1.0.0.0.zip inflating: instantclient_18_1/SQLPLUS_README inflating: instantclient_18_1/glogin.sql inflating: instantclient_18_1/libsqlplus.dylib inflating: instantclient_18_1/libsqlplusic.dylib inflating: instantclient_18_1/sqlplus piclerick:oracle inter$ unzip ~/Downloads/instantclient-tools-macos.x64-18.1.0.0.0.zip Archive: /Users/inter/Downloads/instantclient-tools-macos.x64-18.1.0.0.0.zip inflating: instantclient_18_1/TOOLS_README inflating: instantclient_18_1/exp inflating: instantclient_18_1/expdp inflating: instantclient_18_1/imp inflating: instantclient_18_1/impdp inflating: instantclient_18_1/libnfsodm18.dylib inflating: instantclient_18_1/sqlldr inflating: instantclient_18_1/wrc piclerick:oracle inter$ unzip ~/Downloads/instantclient-sdk-macos.x64-18.1.0.0.0-2.zip Archive: /Users/inter/Downloads/instantclient-sdk-macos.x64-18.1.0.0.0-2.zip creating: instantclient_18_1/sdk/ creating: instantclient_18_1/sdk/admin/ inflating: instantclient_18_1/sdk/admin/oraaccess.xsd creating: instantclient_18_1/sdk/demo/ inflating: instantclient_18_1/sdk/demo/cdemo81.c inflating: instantclient_18_1/sdk/demo/demo.mk inflating: instantclient_18_1/sdk/demo/occidemo.sql inflating: instantclient_18_1/sdk/demo/occidemod.sql inflating: instantclient_18_1/sdk/demo/occidml.cpp inflating: instantclient_18_1/sdk/demo/occiobj.cpp inflating: instantclient_18_1/sdk/demo/occiobj.typ inflating: instantclient_18_1/sdk/demo/oraaccess.xml inflating: instantclient_18_1/sdk/demo/setuporamysql.sh creating: instantclient_18_1/sdk/include/ inflating: instantclient_18_1/sdk/include/ldap.h inflating: instantclient_18_1/sdk/include/nzerror.h inflating: instantclient_18_1/sdk/include/nzt.h inflating: instantclient_18_1/sdk/include/occi.h inflating: instantclient_18_1/sdk/include/occiAQ.h inflating: instantclient_18_1/sdk/include/occiCommon.h inflating: instantclient_18_1/sdk/include/occiControl.h inflating: instantclient_18_1/sdk/include/occiData.h inflating: instantclient_18_1/sdk/include/occiObjects.h inflating: instantclient_18_1/sdk/include/oci.h inflating: instantclient_18_1/sdk/include/oci1.h inflating: instantclient_18_1/sdk/include/oci8dp.h inflating: instantclient_18_1/sdk/include/ociap.h inflating: instantclient_18_1/sdk/include/ociapr.h inflating: instantclient_18_1/sdk/include/ocidef.h inflating: instantclient_18_1/sdk/include/ocidem.h inflating: instantclient_18_1/sdk/include/ocidfn.h inflating: instantclient_18_1/sdk/include/ociextp.h inflating: instantclient_18_1/sdk/include/ocikpr.h inflating: instantclient_18_1/sdk/include/ociver.h inflating: instantclient_18_1/sdk/include/ocixmldb.h inflating: instantclient_18_1/sdk/include/ocixstream.h inflating: instantclient_18_1/sdk/include/odci.h inflating: instantclient_18_1/sdk/include/orastruc.h inflating: instantclient_18_1/sdk/include/oratypes.h inflating: instantclient_18_1/sdk/include/oraxml.h inflating: instantclient_18_1/sdk/include/oraxml.hpp inflating: instantclient_18_1/sdk/include/oraxmlcg.h inflating: instantclient_18_1/sdk/include/oraxsd.h inflating: instantclient_18_1/sdk/include/oraxsd.hpp inflating: instantclient_18_1/sdk/include/ori.h inflating: instantclient_18_1/sdk/include/orid.h inflating: instantclient_18_1/sdk/include/orl.h inflating: instantclient_18_1/sdk/include/oro.h inflating: instantclient_18_1/sdk/include/ort.h inflating: instantclient_18_1/sdk/include/xa.h inflating: instantclient_18_1/sdk/include/xml.h inflating: instantclient_18_1/sdk/include/xml.hpp inflating: instantclient_18_1/sdk/include/xmlctx.hpp inflating: instantclient_18_1/sdk/include/xmldav.h inflating: instantclient_18_1/sdk/include/xmldf.h inflating: instantclient_18_1/sdk/include/xmlerr.h inflating: instantclient_18_1/sdk/include/xmlev.h inflating: instantclient_18_1/sdk/include/xmlotn.h inflating: instantclient_18_1/sdk/include/xmlotn.hpp inflating: instantclient_18_1/sdk/include/xmlproc.h inflating: instantclient_18_1/sdk/include/xmlsch.h inflating: instantclient_18_1/sdk/include/xmlsoap.h inflating: instantclient_18_1/sdk/include/xmlsoap.hpp inflating: instantclient_18_1/sdk/include/xmlsoapc.hpp inflating: instantclient_18_1/sdk/include/xmlurl.h inflating: instantclient_18_1/sdk/include/xmlxptr.h inflating: instantclient_18_1/sdk/include/xmlxsl.h inflating: instantclient_18_1/sdk/include/xmlxvm.h inflating: instantclient_18_1/sdk/ott extracting: instantclient_18_1/sdk/ottclasses.zip inflating: instantclient_18_1/sdk/SDK_README
To connect to Oracle, we will use goracle – it is a package which is a database/sql/driver.Driver for connecting to Oracle DB, using Anthony Tuininga’s excellent OCI wrapper, ODPI-C.
Let’s get the package:
piclerick:~ inter$ go get gopkg.in/goracle.v2
Now we can set the environment variables and create our source code:
piclerick:~ inter$ export ORACLE_HOME=/Users/inter/oracle/instantclient_18_1 piclerick:~ inter$ export DYLD_LIBRARY_PATH=/Users/inter/oracle/instantclient_18_1 piclerick:~ inter$ vim chatt_with_oracle.go
The base for our script will look like this:
package main import ( "fmt" "database/sql" "log" _ "gopkg.in/goracle.v2" ) func main() { }
Please note that we have to import goracle a bit differently than other packages – with "_" prefix. That’s because we will connect to the database like this: sql.Open("goracle", conn). So the "goracle" won’t be used directly in a code but as a string literal to SQL driver.
Let’s create a connection to the database and execute a simple query
package main import ( "fmt" "database/sql" "log" _ "gopkg.in/goracle.v2" ) func main() { //connect to database db, err := sql.Open("goracle", "hr/hr@192.168.56.18:1521/rokoko") //close database connection at the end of program execution defer db.Close() //check if there were some errors during connection if err != nil { log.Panic(err) } //execute a query and get pointer to the rows rows, err := db.Query("select count(1) from employees") //check if there were some errors during execution if err != nil { log.Panic(err) } //define variable for the output var emps_cnt int //if there are some rows - put the output to the variable address by reference if rows.Next() { rows.Scan(&emps_cnt) } fmt.Println(emps_cnt) }
Now we can compile our program and execute it:
piclerick:~ inter$ go build chatt_with_oracle.go piclerick:~ inter$ ./chatt_with_oracle 107
Great! Let’s add more rows to the output and additional bind variable:
package main import ( "fmt" "database/sql" "log" _ "gopkg.in/goracle.v2" ) func main() { //connect to database db, err := sql.Open("goracle", "hr/hr@192.168.56.18:1521/rokoko") //close database connection at the end of program execution defer db.Close() //check if there were some errors during connection if err != nil { log.Panic(err) } //execute a query and get pointer to the rows deptId := 20 rows, err := db.Query("select first_name, last_name, salary from employees where department_id=:1", deptId) //check if there were some errors during execution if err != nil { log.Panic(err) } //define variable for the output var salary int var fname, lname string //if there are some rows - put the output to the variable address by reference for rows.Next() { rows.Scan(&fname, &lname, &salary) fmt.Println(fname, lname, salary) } }
And here’s the result:
piclerick:~ inter$ ./chatt_with_oracle Michael Hartstein 26000 Pat Fay 12000
If you want to execute PL/SQL function or procedure and get the results, you can use sql.Out interface. For example, let’s create a sample PL/SQL function:
SQL> ed Zapisano file afiedt.buf 1 create or replace function f_get_dept_avg_sal(p_dept_id number) return number is 2 v_avg_sal number; 3 begin 4 select avg(salary) into v_avg_sal 5 from employees e 6 where e.department_id=p_dept_id; 7 return v_avg_sal; 8* end; 9 / Utworzono funkcje.
Now we can add a simple PL/SQL block to our GO function and use Exec method and sql.Out interface:
package main import ( "fmt" "database/sql" "log" _ "gopkg.in/goracle.v2" ) func main() { //connect to database db, err := sql.Open("goracle", "hr/hr@192.168.56.18:1521/rokoko") //close database connection at the end of program execution defer db.Close() //check if there were some errors during connection if err != nil { log.Panic(err) } //execute a query and get pointer to the rows deptId := 20 rows, err := db.Query("select first_name, last_name, salary from employees where department_id=:1", deptId) //check if there were some errors during execution if err != nil { log.Panic(err) } //define variable for the output var salary int var fname, lname string //if there are some rows - put the output to the variable address by reference for rows.Next() { rows.Scan(&fname, &lname, &salary) fmt.Println(fname, lname, salary) } //define variable for average salary var avgSal float64 //execute anonymous block and use sql.Out interface for extracting bind variable value db.Exec("begin :1 := f_get_dept_avg_sal(:2); end;", sql.Out{Dest: &avgSal}, deptId) fmt.Printf("AVG salary in department %d is %f\n", deptId, avgSal) }
And here’s the result:
piclerick:~ inter$ go build chatt_with_oracle.go piclerick:~ inter$ ./chatt_with_oracle Michael Hartstein 26000 Pat Fay 12000 AVG salary in department 20 is 19000.000000
And it’s simple as that 🙂