5#include <c_interop.inc>
8 use,
intrinsic :: iso_c_binding
9 use,
intrinsic :: iso_fortran_env, only: stderr => error_unit
15 implicit none;
private
27 type(SQLHSTMT) :: stmt
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
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
59 module procedure :: connection_new
63 module procedure :: throw_i2
64 module procedure :: throw_i4
75 function connection_new(connstring)
result(that)
77 character(*),
intent(in) :: connstring
85 that%connstring = _string(connstring)
91 subroutine connection_open(this)
94 this%ierr =
sqlallochandle(sql_handle_env, sql_null_handle, this%env)
95 if (this%ierr /= 0)
call handle_error(this,
'ENV')
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')
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')
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')
112 if (this%ierr /= sql_success)
call handle_error(this,
'DBC')
120 function connection_get_timeout(this)
result(res)
131 subroutine connection_set_timeout(this, n)
133 integer,
intent(in) :: n
141 function connection_isopened(this)
result(res)
155 function connection_execute(this, sql)
result(count)
157 character(*),
intent(in) :: sql
158 integer(c_int) :: count
160 integer(SQLLEN),
allocatable :: countint
161 character(len(sql)) :: tmp
163 if (.not. this%opened)
call handle_error(this,
'Call Open() before execute()')
165 this%ierr =
sqlprepare(this%stmt, _string(sql), sql_nts)
166 if (this%ierr == sql_error)
call handle_error(this,
'STMT')
169 if (this%ierr == sql_error .or. this%ierr < sql_success)
call handle_error(this,
'STMT')
171 allocate(countint, source = _long(0))
173 if (index(tmp,
'update') > 0 .or. &
174 index(tmp,
'insert') > 0 .or. &
175 index(tmp,
'delete') > 0)
then
178 count = merge(int(this%ierr, c_int), int(countint, c_int), this%ierr /= sql_success)
187 subroutine connection_execute_query(this, sql, rslt)
189 character(*),
intent(in) :: sql
192 integer(c_int),
target :: cursor
194 cursor = sql_cursor_dynamic
196 if (.not. this%opened)
call throw(
'Connection not opened', sql_error)
200 if (this%ierr == sql_error .or. &
201 this%ierr == sql_invalid_handle .or. &
202 this%ierr < sql_success)
call handle_error(this,
'DBC')
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')
208 if (this%ierr == -1)
call handle_error(this,
'STMT')
210 call new(rslt, this%stmt)
223 subroutine connection_execute_query_with_cursor(this, sql, cursor_type, scrollable, rslt)
225 character(*),
intent(in) :: sql
226 integer(c_short),
intent(in),
target :: cursor_type
227 logical,
intent(in) :: scrollable
230 integer(c_short),
target :: dummy
232 dummy = sql_scrollable
234 if (.not. this%opened)
call throw(
'Connection not opened', sql_error)
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)
244 if (this%ierr == sql_error .or. &
245 this%ierr == sql_invalid_handle .or. &
246 this%ierr < sql_success)
call handle_error(this,
'DBC')
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')
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')
257 if (this%ierr == sql_error)
call handle_error(this,
'STMT')
259 call new(rslt, this%stmt)
266 function connection_commit(this)
result(success)
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)
281 function connection_rollback(this)
result(success)
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)
295 subroutine connection_close(this)
298 if (this%opened)
then
303 this%opened = .false.
307 subroutine connection_finalize(this)
313 subroutine handle_error(this, type)
314 class(
connection),
intent(inout),
target :: this
315 character(*),
intent(in) :: type
317 integer(SQLRETURN) :: status
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
326 this%state, this%ierr, this%msg, &
327 len(this%msg, sqlsmallint), this%imsg)
328 else if (
trim(type) ==
'DBC')
then
330 this%state, this%ierr, this%msg, &
331 len(this%msg, sqlsmallint), this%imsg)
333 call throw(
trim(this%msg), this%ierr)
336 if (status /= sql_success)
then
337 call throw(
trim(this%msg), this%ierr)
341 subroutine throw_i2(msg, ierr)
342 character(*),
intent(in) :: msg
343 integer(SQLSMALLINT),
intent(in) :: ierr
345 write(stderr,
'("connection error: ", A, "Error code: ", i0)') msg, ierr
349 subroutine throw_i4(msg, ierr)
350 character(*),
intent(in) :: msg
351 integer(SQLINTEGER),
intent(in) :: ierr
353 write(stderr,
'("connection error: ", A, "Error code: ", i0)') msg, ierr
357 pure function to_lower(str)
result(res)
358 character(*),
intent(in) :: str
359 character(len(str)) :: res
362 integer,
parameter :: a = iachar(
'A'), z = iachar(
'Z')
366 if (j >= a .and. j <= z)
then
367 res(i:i) = achar(iachar(str(i:i)) + 32)
Triming of c-string returning fortran allocatable characters.
Constructor interface for initializing a resultset object with an ODBC statement handle.
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...