Golang chatting with Oracle


20.03.2019
by Kamil Stawiarski

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

1piclerick:~ inter$ mkdir oracle
2piclerick:~ inter$ cd oracle
3piclerick:oracle inter$ ls
4piclerick:oracle inter$ unzip ~/Downloads/instantclient-basic-macos.x64-18.1.0.0.0.zip
5Archive:  /Users/inter/Downloads/instantclient-basic-macos.x64-18.1.0.0.0.zip
6  inflating: instantclient_18_1/BASIC_README
7  inflating: instantclient_18_1/adrci
8  inflating: instantclient_18_1/genezi
9    linking: instantclient_18_1/libclntsh.dylib  -> libclntsh.dylib.18.1
10  inflating: instantclient_18_1/libclntsh.dylib.18.1
11  inflating: instantclient_18_1/libclntshcore.dylib.18.1
12  inflating: instantclient_18_1/libnnz18.dylib
13    linking: instantclient_18_1/libocci.dylib  -> libocci.dylib.18.1
14  inflating: instantclient_18_1/libocci.dylib.18.1
15  inflating: instantclient_18_1/libociei.dylib
16  inflating: instantclient_18_1/libocijdbc18.dylib
17  inflating: instantclient_18_1/libons.dylib
18  inflating: instantclient_18_1/liboramysql18.dylib
19   creating: instantclient_18_1/network/
20  inflating: instantclient_18_1/ojdbc8.jar
21  inflating: instantclient_18_1/ucp.jar
22  inflating: instantclient_18_1/uidrvci
23  inflating: instantclient_18_1/xstreams.jar
24   creating: instantclient_18_1/network/admin/
25  inflating: instantclient_18_1/network/admin/README
26finishing deferred symbolic links:
27  instantclient_18_1/libclntsh.dylib -> libclntsh.dylib.18.1
28  instantclient_18_1/libocci.dylib -> libocci.dylib.18.1
29piclerick:oracle inter$ unzip ~/Downloads/instantclient-sqlplus-macos.x64-18.1.0.0.0.zip
30Archive:  /Users/inter/Downloads/instantclient-sqlplus-macos.x64-18.1.0.0.0.zip
31  inflating: instantclient_18_1/SQLPLUS_README
32  inflating: instantclient_18_1/glogin.sql
33  inflating: instantclient_18_1/libsqlplus.dylib
34  inflating: instantclient_18_1/libsqlplusic.dylib
35  inflating: instantclient_18_1/sqlplus
36piclerick:oracle inter$ unzip ~/Downloads/instantclient-tools-macos.x64-18.1.0.0.0.zip
37Archive:  /Users/inter/Downloads/instantclient-tools-macos.x64-18.1.0.0.0.zip
38  inflating: instantclient_18_1/TOOLS_README
39  inflating: instantclient_18_1/exp
40  inflating: instantclient_18_1/expdp
41  inflating: instantclient_18_1/imp
42  inflating: instantclient_18_1/impdp
43  inflating: instantclient_18_1/libnfsodm18.dylib
44  inflating: instantclient_18_1/sqlldr
45  inflating: instantclient_18_1/wrc
46piclerick:oracle inter$ unzip ~/Downloads/instantclient-sdk-macos.x64-18.1.0.0.0-2.zip
47Archive:  /Users/inter/Downloads/instantclient-sdk-macos.x64-18.1.0.0.0-2.zip
48   creating: instantclient_18_1/sdk/
49   creating: instantclient_18_1/sdk/admin/
50  inflating: instantclient_18_1/sdk/admin/oraaccess.xsd
51   creating: instantclient_18_1/sdk/demo/
52  inflating: instantclient_18_1/sdk/demo/cdemo81.c
53  inflating: instantclient_18_1/sdk/demo/demo.mk
54  inflating: instantclient_18_1/sdk/demo/occidemo.sql
55  inflating: instantclient_18_1/sdk/demo/occidemod.sql
56  inflating: instantclient_18_1/sdk/demo/occidml.cpp
57  inflating: instantclient_18_1/sdk/demo/occiobj.cpp
58  inflating: instantclient_18_1/sdk/demo/occiobj.typ
59  inflating: instantclient_18_1/sdk/demo/oraaccess.xml
60  inflating: instantclient_18_1/sdk/demo/setuporamysql.sh
61   creating: instantclient_18_1/sdk/include/
62  inflating: instantclient_18_1/sdk/include/ldap.h
63  inflating: instantclient_18_1/sdk/include/nzerror.h
64  inflating: instantclient_18_1/sdk/include/nzt.h
65  inflating: instantclient_18_1/sdk/include/occi.h
66  inflating: instantclient_18_1/sdk/include/occiAQ.h
67  inflating: instantclient_18_1/sdk/include/occiCommon.h
68  inflating: instantclient_18_1/sdk/include/occiControl.h
69  inflating: instantclient_18_1/sdk/include/occiData.h
70  inflating: instantclient_18_1/sdk/include/occiObjects.h
71  inflating: instantclient_18_1/sdk/include/oci.h
72  inflating: instantclient_18_1/sdk/include/oci1.h
73  inflating: instantclient_18_1/sdk/include/oci8dp.h
74  inflating: instantclient_18_1/sdk/include/ociap.h
75  inflating: instantclient_18_1/sdk/include/ociapr.h
76  inflating: instantclient_18_1/sdk/include/ocidef.h
77  inflating: instantclient_18_1/sdk/include/ocidem.h
78  inflating: instantclient_18_1/sdk/include/ocidfn.h
79  inflating: instantclient_18_1/sdk/include/ociextp.h
80  inflating: instantclient_18_1/sdk/include/ocikpr.h
81  inflating: instantclient_18_1/sdk/include/ociver.h
82  inflating: instantclient_18_1/sdk/include/ocixmldb.h
83  inflating: instantclient_18_1/sdk/include/ocixstream.h
84  inflating: instantclient_18_1/sdk/include/odci.h
85  inflating: instantclient_18_1/sdk/include/orastruc.h
86  inflating: instantclient_18_1/sdk/include/oratypes.h
87  inflating: instantclient_18_1/sdk/include/oraxml.h
88  inflating: instantclient_18_1/sdk/include/oraxml.hpp
89  inflating: instantclient_18_1/sdk/include/oraxmlcg.h
90  inflating: instantclient_18_1/sdk/include/oraxsd.h
91  inflating: instantclient_18_1/sdk/include/oraxsd.hpp
92  inflating: instantclient_18_1/sdk/include/ori.h
93  inflating: instantclient_18_1/sdk/include/orid.h
94  inflating: instantclient_18_1/sdk/include/orl.h
95  inflating: instantclient_18_1/sdk/include/oro.h
96  inflating: instantclient_18_1/sdk/include/ort.h
97  inflating: instantclient_18_1/sdk/include/xa.h
98  inflating: instantclient_18_1/sdk/include/xml.h
99  inflating: instantclient_18_1/sdk/include/xml.hpp
100  inflating: instantclient_18_1/sdk/include/xmlctx.hpp
101  inflating: instantclient_18_1/sdk/include/xmldav.h
102  inflating: instantclient_18_1/sdk/include/xmldf.h
103  inflating: instantclient_18_1/sdk/include/xmlerr.h
104  inflating: instantclient_18_1/sdk/include/xmlev.h
105  inflating: instantclient_18_1/sdk/include/xmlotn.h
106  inflating: instantclient_18_1/sdk/include/xmlotn.hpp
107  inflating: instantclient_18_1/sdk/include/xmlproc.h
108  inflating: instantclient_18_1/sdk/include/xmlsch.h
109  inflating: instantclient_18_1/sdk/include/xmlsoap.h
110  inflating: instantclient_18_1/sdk/include/xmlsoap.hpp
111  inflating: instantclient_18_1/sdk/include/xmlsoapc.hpp
112  inflating: instantclient_18_1/sdk/include/xmlurl.h
113  inflating: instantclient_18_1/sdk/include/xmlxptr.h
114  inflating: instantclient_18_1/sdk/include/xmlxsl.h
115  inflating: instantclient_18_1/sdk/include/xmlxvm.h
116  inflating: instantclient_18_1/sdk/ott
117 extracting: instantclient_18_1/sdk/ottclasses.zip
118  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:

1piclerick:~ inter$ go get gopkg.in/goracle.v2

Now we can set the environment variables and create our source code:

1piclerick:~ inter$ export  ORACLE_HOME=/Users/inter/oracle/instantclient_18_1
2piclerick:~ inter$ export DYLD_LIBRARY_PATH=/Users/inter/oracle/instantclient_18_1
3piclerick:~ inter$ vim chatt_with_oracle.go

The base for our script will look like this:

1package main
2 
3import (
4        "fmt"
5        "database/sql"
6        "log"
7        _ "gopkg.in/goracle.v2"
8)
9 
10func main() {
11 
12}

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

1package main
2 
3import (
4        "fmt"
5        "database/sql"
6        "log"
7        _ "gopkg.in/goracle.v2"
8)
9 
10func main() {
11  //connect to database
12  db, err := sql.Open("goracle", "hr/hr@192.168.56.18:1521/rokoko")
13 
14  //close database connection at the end of program execution
15  defer db.Close()
16 
17  //check if there were some errors during connection
18  if err != nil {
19        log.Panic(err)
20  }
21 
22  //execute a query and get pointer to the rows
23  rows, err := db.Query("select count(1) from employees")
24 
25  //check if there were some errors during execution
26  if err != nil {
27        log.Panic(err)
28  }
29 
30  //define variable for the output
31  var emps_cnt int
32 
33  //if there are some rows - put the output to the variable address by reference
34  if rows.Next() {
35        rows.Scan(&emps_cnt)
36  }
37 
38  fmt.Println(emps_cnt)
39 
40}

Now we can compile our program and execute it:

1piclerick:~ inter$ go build chatt_with_oracle.go
2piclerick:~ inter$ ./chatt_with_oracle
3107

Great! Let’s add more rows to the output and additional bind variable:

1package main
2 
3import (
4    "fmt"
5    "database/sql"
6    "log"
7    _ "gopkg.in/goracle.v2"
8)
9 
10func main() {
11  //connect to database
12  db, err := sql.Open("goracle", "hr/hr@192.168.56.18:1521/rokoko")
13 
14  //close database connection at the end of program execution
15  defer db.Close()
16 
17  //check if there were some errors during connection
18  if err != nil {
19    log.Panic(err)
20  }
21 
22  //execute a query and get pointer to the rows
23  deptId := 20
24  rows, err := db.Query("select first_name, last_name, salary from employees where department_id=:1", deptId)
25 
26  //check if there were some errors during execution
27  if err != nil {
28    log.Panic(err)
29  }
30 
31  //define variable for the output
32  var salary int
33  var fname, lname string
34 
35  //if there are some rows - put the output to the variable address by reference
36  for rows.Next() {
37    rows.Scan(&fname, &lname, &salary)
38        fmt.Println(fname, lname, salary)
39  }
40 
41}

And here’s the result:

1piclerick:~ inter$ ./chatt_with_oracle
2Michael Hartstein 26000
3Pat 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:

1SQL> ed
2Zapisano file afiedt.buf
3 
4  create or replace function f_get_dept_avg_sal(p_dept_id number) return number is
5  2    v_avg_sal number;
6  begin
7  4    select avg(salary) into v_avg_sal
8  5    from employees e
9  6    where e.department_id=p_dept_id;
10  7    return v_avg_sal;
11  8* end;
12  9  /
13 
14Utworzono funkcje.

Now we can add a simple PL/SQL block to our GO function and use Exec method and sql.Out interface:

1package main
2 
3import (
4    "fmt"
5    "database/sql"
6    "log"
7    _ "gopkg.in/goracle.v2"
8)
9 
10func main() {
11  //connect to database
12  db, err := sql.Open("goracle", "hr/hr@192.168.56.18:1521/rokoko")
13 
14  //close database connection at the end of program execution
15  defer db.Close()
16 
17  //check if there were some errors during connection
18  if err != nil {
19    log.Panic(err)
20  }
21 
22  //execute a query and get pointer to the rows
23  deptId := 20
24  rows, err := db.Query("select first_name, last_name, salary from employees where department_id=:1", deptId)
25 
26  //check if there were some errors during execution
27  if err != nil {
28    log.Panic(err)
29  }
30 
31  //define variable for the output
32  var salary int
33  var fname, lname string
34 
35  //if there are some rows - put the output to the variable address by reference
36  for rows.Next() {
37    rows.Scan(&fname, &lname, &salary)
38        fmt.Println(fname, lname, salary)
39  }
40 
41  //define variable for average salary
42  var avgSal float64
43 
44  //execute anonymous block and use sql.Out interface for extracting bind variable value
45  db.Exec("begin :1 := f_get_dept_avg_sal(:2); end;", sql.Out{Dest: &avgSal}, deptId)
46 
47  fmt.Printf("AVG salary in department %d is %f\n", deptId, avgSal)
48 
49}

And here’s the result:

1piclerick:~ inter$ go build chatt_with_oracle.go
2piclerick:~ inter$ ./chatt_with_oracle
3Michael Hartstein 26000
4Pat Fay 12000
5AVG salary in department 20 is 19000.000000

And it’s simple as that 🙂


Contact us

Database Whisperers sp. z o. o. sp. k.
al. Jerozolimskie 200, 3rd floor, room 342
02-486 Warszawa
NIP: 5272744987
REGON:362524978
+48 508 943 051
+48 661 966 009
info@ora-600.pl

Newsletter Sign up to be updated