Loading...
Searching...
No Matches
connection.f90
1
5#include <c_interop.inc>
6!! @endcond
7module odbc_connection
8 use, intrinsic :: iso_c_binding
9 use, intrinsic :: iso_fortran_env, only: stderr => error_unit
10 use sql
11 use sqlext
12 use odbc_constants
13 use odbc_resultset, only: resultset, new
14
15 implicit none; private
16
17 public :: resultset
18
23 type, public :: connection
24 private
25 type(SQLHENV) :: env
26 type(SQLHDBC) :: dbc
27 type(SQLHSTMT) :: stmt
28 logical :: opened
29 integer :: timeout
30 integer(SQLSMALLINT) :: rec
31 character(kind=SQLTCHAR, len=6) :: state
32 character(kind=SQLTCHAR, len=SQL_MAX_MESSAGE_LENGTH) :: msg
33 integer(SQLINTEGER) :: ierr
34 integer(SQLSMALLINT) :: imsg
35 character(1024) :: connstring
36 contains
37 private
38 procedure, pass(this), public :: set_timeout => connection_set_timeout
39 procedure, pass(this), public :: get_timeout => connection_get_timeout
40 procedure, pass(this), public :: is_open => connection_isopened
41 procedure, pass(this), private :: connection_open
42 generic, public :: open => connection_open
43 procedure, pass(this), public :: execute => connection_execute
44 procedure, pass(this), private :: connection_execute_query
45 procedure, pass(this), private :: connection_execute_query_with_cursor
46 generic, public :: execute_query => connection_execute_query, &
47 connection_execute_query_with_cursor
48 procedure, pass(this), public :: commit => connection_commit
49 procedure, pass(this), public :: rollback => connection_rollback
50 procedure, pass(this), public :: close => connection_close
51 final :: connection_finalize
52 end type
53
58 interface connection
59 module procedure :: connection_new
60 end interface
61
62 interface throw
63 module procedure :: throw_i2
64 module procedure :: throw_i4
65 end interface
66
67contains
68
75 function connection_new(connstring) result(that)
76 type(connection) :: that
77 character(*), intent(in) :: connstring
78
79 that%env = null
80 that%dbc = null
81 that%stmt = null
82 that%opened = .false.
83 that%timeout = 10
84 that%rec = _short(1)
85 that%connstring = _string(connstring)
86 end function
87
91 subroutine connection_open(this)
92 class(connection), intent(inout) :: this
93
94 this%ierr = sqlallochandle(sql_handle_env, sql_null_handle, this%env)
95 if (this%ierr /= 0) call handle_error(this, 'ENV')
96
97 this%ierr = sqlsetenvattr(this%env, sql_attr_odbc_version, _ptr(sql_ov_odbc3), 0)
98 if (this%ierr /= 0) call handle_error(this, 'ENV')
99
100 this%ierr = sqlallochandle(sql_handle_dbc, this%env, this%dbc)
101 if (this%ierr == sql_error) then
102 call handle_error(this, 'ENV')
103 else if (this%ierr == sql_invalid_handle .or. this%ierr < sql_success) then
104 call handle_error(this, 'ENV')
105 end if
106
107 this%ierr = sqldriverconnect(this%dbc, null, this%connstring &
108 , int(len_trim(this%connstring), c_short), str_null_ptr, _short(0), short_null_ptr, sql_driver_complete)
109 if (this%ierr /= sql_success) call handle_error(this, 'DBC')
110
111 this%ierr = sqlallocstmt(this%dbc, this%stmt)
112 if (this%ierr /= sql_success) call handle_error(this, 'DBC')
113
114 this%opened = .true.
115 end subroutine
116
120 function connection_get_timeout(this) result(res)
121 class(connection), intent(in) :: this
122 integer :: res
123
124 res = this%timeout
125 end function
126
131 subroutine connection_set_timeout(this, n)
132 class(connection), intent(inout) :: this
133 integer, intent(in) :: n
134
135 this%timeout = n
136 end subroutine
137
141 function connection_isopened(this) result(res)
142 class(connection), intent(in) :: this
143 logical :: res
144
145 res = this%opened
146 end function
147
155 function connection_execute(this, sql) result(count)
156 class(connection), intent(inout) :: this
157 character(*), intent(in) :: sql
158 integer(c_int) :: count
159 !private
160 integer(SQLLEN), allocatable :: countint
161 character(len(sql)) :: tmp
162
163 if (.not. this%opened) call handle_error(this, 'Call Open() before execute()')
164
165 this%ierr = sqlprepare(this%stmt, _string(sql), sql_nts)
166 if (this%ierr == sql_error) call handle_error(this, 'STMT')
167
168 this%ierr = sqlexecute(this%stmt)
169 if (this%ierr == sql_error .or. this%ierr < sql_success) call handle_error(this, 'STMT')
170
171 allocate(countint, source = _long(0))
172 tmp = to_lower(sql)
173 if (index(tmp, 'update') > 0 .or. &
174 index(tmp, 'insert') > 0 .or. &
175 index(tmp, 'delete') > 0) then
176 this%ierr = sqlrowcount(this%stmt, countint)
177 end if
178 count = merge(int(this%ierr, c_int), int(countint, c_int), this%ierr /= sql_success)
179 end function
180
187 subroutine connection_execute_query(this, sql, rslt)
188 class(connection), intent(inout) :: this
189 character(*), intent(in) :: sql
190 type(resultset), intent(inout) :: rslt
191 !private
192 integer(c_int), target :: cursor
193
194 cursor = sql_cursor_dynamic
195
196 if (.not. this%opened) call throw('Connection not opened', sql_error)
197
198 this%ierr = sqlfreestmt(this%stmt, sql_close)
199 this%ierr = sqlallocstmt(this%dbc, this%stmt)
200 if (this%ierr == sql_error .or. &
201 this%ierr == sql_invalid_handle .or. &
202 this%ierr < sql_success) call handle_error(this, 'DBC')
203
204 this%ierr = sqlsetstmtattr(this%stmt, sql_attr_cursor_type, c_loc(cursor), sql_is_integer)
205 if (this%ierr < sql_success) call handle_error(this, 'STMT')
206
207 this%ierr = sqlexecdirect(this%stmt, _string(sql), sql_nts)
208 if (this%ierr == -1) call handle_error(this, 'STMT')
209
210 call new(rslt, this%stmt)
211 end subroutine
212
223 subroutine connection_execute_query_with_cursor(this, sql, cursor_type, scrollable, rslt)
224 class(connection), intent(inout) :: this
225 character(*), intent(in) :: sql
226 integer(c_short), intent(in), target :: cursor_type
227 logical, intent(in) :: scrollable
228 type(resultset), intent(inout) :: rslt
229 !private
230 integer(c_short), target :: dummy
231
232 dummy = sql_scrollable
233
234 if (.not. this%opened) call throw('Connection not opened', sql_error)
235
236 if (cursor_type /= sql_cursor_dynamic .and. cursor_type /= sql_cursor_forward_only &
237 .and. cursor_type /= sql_cursor_keyset_driven &
238 .and. cursor_type /= sql_cursor_static) then
239 call throw('Invalid cursor type', sql_error)
240 end if
241
242 this%ierr = sqlfreestmt(this%stmt, sql_close)
243 this%ierr = sqlallocstmt(this%dbc, this%stmt)
244 if (this%ierr == sql_error .or. &
245 this%ierr == sql_invalid_handle .or. &
246 this%ierr < sql_success) call handle_error(this, 'DBC')
247
248 this%ierr = sqlsetstmtattr(this%stmt, sql_attr_cursor_type, c_loc(cursor_type), sql_is_integer)
249 if (this%ierr < sql_success) call handle_error(this, 'STMT')
250
251 if (scrollable) then
252 this%ierr = sqlsetstmtattr(this%stmt, sql_attr_cursor_scrollable, c_loc(dummy), sql_is_integer)
253 if (this%ierr < sql_success) call handle_error(this, 'STMT')
254 end if
255
256 this%ierr = sqlexecdirect(this%stmt, sql, len_trim(sql))
257 if (this%ierr == sql_error) call handle_error(this, 'STMT')
258
259 call new(rslt, this%stmt)
260 end subroutine
261
266 function connection_commit(this) result(success)
267 class(connection), intent(inout) :: this
268 logical :: success
269
270 this%ierr = sqlendtran(sql_handle_dbc, this%dbc, sql_commit)
271 if (this%ierr == sql_error .or. &
272 this%ierr == sql_invalid_handle) call throw('Commit failed', this%ierr)
273
274 success = .true.
275 end function
276
281 function connection_rollback(this) result(success)
282 class(connection), intent(inout) :: this
283 logical :: success
284
285 this%ierr = sqlendtran(sql_handle_dbc, this%dbc, sql_rollback)
286 if (this%ierr == sql_error .or.&
287 this%ierr == sql_invalid_handle) call throw('Rollback failed', this%ierr)
288
289 success = .true.
290 end function
291
295 subroutine connection_close(this)
296 class(connection), intent(inout) :: this
297
298 if (this%opened) then
299 this%ierr = sqlfreestmt(this%stmt, sql_close)
300 this%ierr = sqldisconnect(this%dbc)
301 this%ierr = sqlfreeconnect(this%dbc)
302 this%ierr = sqlfreeenv(this%env)
303 this%opened = .false.
304 end if
305 end subroutine
306
307 subroutine connection_finalize(this)
308 type(connection), intent(inout) :: this
309
310 call this%close()
311 end subroutine
312
313 subroutine handle_error(this, type)
314 class(connection), intent(inout), target :: this
315 character(*), intent(in) :: type
316 !private
317 integer(SQLRETURN) :: status
318
319 ! Error handling
320 if (trim(type) == 'STMT') then
321 status = sqlgetdiagrec(sql_handle_stmt, this%stmt, this%rec, &
322 this%state, this%ierr, this%msg, &
323 len(this%msg, sqlsmallint), this%imsg)
324 else if (trim(type) == 'ENV') then
325 status = sqlgetdiagrec(sql_handle_env, this%env, this%rec, &
326 this%state, this%ierr, this%msg, &
327 len(this%msg, sqlsmallint), this%imsg)
328 else if (trim(type) == 'DBC') then
329 status = sqlgetdiagrec(sql_handle_dbc, this%dbc, this%rec, &
330 this%state, this%ierr, this%msg, &
331 len(this%msg, sqlsmallint), this%imsg)
332 else
333 call throw(trim(this%msg), this%ierr)
334 end if
335
336 if (status /= sql_success) then
337 call throw(trim(this%msg), this%ierr)
338 end if
339 end subroutine
340
341 subroutine throw_i2(msg, ierr)
342 character(*), intent(in) :: msg
343 integer(SQLSMALLINT), intent(in) :: ierr
344
345 write(stderr, '("connection error: ", A, "Error code: ", i0)') msg, ierr
346 error stop ierr
347 end subroutine
348
349 subroutine throw_i4(msg, ierr)
350 character(*), intent(in) :: msg
351 integer(SQLINTEGER), intent(in) :: ierr
352
353 write(stderr, '("connection error: ", A, "Error code: ", i0)') msg, ierr
354 error stop ierr
355 end subroutine
356
357 pure function to_lower(str) result(res)
358 character(*), intent(in) :: str
359 character(len(str)) :: res
360 !private
361 integer :: i,j
362 integer, parameter :: a = iachar('A'), z = iachar('Z')
363
364 do i = 1, len(str)
365 j = iachar(str(i:i))
366 if (j >= a .and. j <= z) then
367 res(i:i) = achar(iachar(str(i:i)) + 32)
368 else
369 res(i:i) = str(i:i)
370 end if
371 end do
372 end function
373end module
Triming of c-string returning fortran allocatable characters.
Constructor interface for initializing a resultset object with an ODBC statement handle.
Definition resultset.f90:63
Represents a database connection with ODBC, managing environment, connection, and statement handles,...
Represents a set of query results from an ODBC query, providing methods to navigate rows and retrieve...
Definition resultset.f90:20