5 use,
intrinsic :: iso_c_binding
6 use,
intrinsic :: iso_fortran_env
11 implicit none;
private
22 type(SQLHSTMT) :: stmt
23 integer(SQLUINTEGER) :: rows
24 integer(SQLUSMALLINT) :: status(10)
25 integer(SQLSMALLINT) :: rec
26 character(kind=SQLTCHAR, len=6) :: state
27 character(kind=SQLTCHAR, len=SQL_MAX_MESSAGE_LENGTH) :: msg
28 integer(SQLINTEGER) :: ierr
29 integer(SQLSMALLINT) :: imsg
30 type(columnset),
public :: columns
33 procedure, pass(this) :: get_metadata => resultset_get_metadata
34 procedure, pass(this),
public :: next => resultset_movenext
35 procedure, pass(this),
public :: previous => resultset_moveprevious
36 procedure, pass(this),
public :: first => resultset_movefirst
37 procedure, pass(this),
public :: last => resultset_movelast
38 procedure, pass(this),
public :: nrows => resultset_get_nrows
39 procedure, pass(this),
public :: ncolumns => resultset_get_ncolumns
40 procedure, pass(this),
private :: resultset_get_integer_from_index
41 procedure, pass(this),
private :: resultset_get_real_from_index
42 procedure, pass(this),
private :: resultset_get_double_from_index
43 procedure, pass(this),
private :: resultset_get_string_from_index
44 procedure, pass(this),
private :: resultset_get_integer_from_name
45 procedure, pass(this),
private :: resultset_get_real_from_name
46 procedure, pass(this),
private :: resultset_get_double_from_name
47 procedure, pass(this),
private :: resultset_get_string_from_name
48 generic,
public :: get_integer => resultset_get_integer_from_index, &
49 resultset_get_integer_from_name
50 generic,
public :: get_real => resultset_get_real_from_index, &
51 resultset_get_real_from_name
52 generic,
public :: get_double => resultset_get_double_from_index, &
53 resultset_get_double_from_name
54 generic,
public :: get_string => resultset_get_string_from_index, &
55 resultset_get_string_from_name
56 procedure, pass(this) :: handle_errors
64 module procedure :: resultset_new
74 subroutine resultset_new(that, stmt)
75 type(sqlhstmt),
intent(in) :: stmt
78 integer(SQLRETURN) :: rc
82 rc =
sqlsetstmtattr(that%stmt, sql_attr_row_status_ptr, c_loc(that%status), 0)
83 rc =
sqlsetstmtattr(that%stmt, sql_attr_rows_fetched_ptr, c_loc(that%rows), 0)
84 that%rec = 0_sqlsmallint
85 call that%get_metadata(that%columns)
88 subroutine resultset_get_metadata(this, columns)
93 integer(SQLSMALLINT) :: name_length
94 integer(SQLSMALLINT),
allocatable :: column_count
95 type(
column),
allocatable,
target :: cols(:)
96 integer :: nErr, nMsg, iRec, cState
98 allocate(column_count, source = 0_sqlsmallint)
100 if (this%ierr == sql_error .or. this%ierr == sql_invalid_handle)
then
101 call this%handle_errors()
104 allocate(cols(column_count))
105 do i = 1, column_count
106 allocate(
character(51) :: cols(i)%name)
107 cols(i)%decim_size = 0
111 this%ierr =
sqldescribecol(this%stmt, int(i, c_short), cols(i)%name, len(cols(i)%name, kind=c_short), &
112 name_length, cols(i)%type, cols(i)%size, &
113 cols(i)%decim_size, cols(i)%nullable)
114 if (this%ierr == sql_error .or. this%ierr == sql_invalid_handle)
then
115 call this%handle_errors()
117 cols(i)%name = clean_string(cols(i)%name)
118 if (
allocated(cols(i)%content))
deallocate(cols(i)%content)
119 allocate(
character(merge(4096, min(4096, cols(i)%size), cols(i)%size <= 0)) :: cols(i)%content)
121 call columns%addrange(cols)
123 do i = 1, column_count
124 this%ierr = this%columns%bind(this%stmt, i)
125 if (this%ierr == sql_error)
call this%handle_errors()
132 logical function resultset_movenext(this)
result(res)
134 integer(SQLRETURN) :: rc
136 integer(c_long) :: offset
141 if (rc == sql_no_data) res = .false.
142 if (rc == sql_error)
call this%handle_errors()
150 logical function resultset_moveprevious(this)
result(res)
152 integer(SQLRETURN) :: rc
154 integer(c_long) :: offset
159 if (rc == sql_no_data .or. rc < sql_error) res = .false.
160 if (rc == sql_error)
call this%handle_errors()
168 logical function resultset_movefirst(this)
result(res)
170 integer(SQLRETURN) :: rc
172 integer(c_long) :: offset
177 if (rc == sql_no_data) res = .false.
178 if (rc < sql_error) res = .false.
179 if (rc == sql_error)
call this%handle_errors()
187 logical function resultset_movelast(this)
result(res)
189 integer(SQLRETURN) :: rc
191 integer(c_long) :: offset
196 if (rc == sql_no_data .or. rc < sql_error) res = .false.
197 if (rc == sql_error)
call this%handle_errors()
204 function resultset_get_nrows(this)
result(res)
215 function resultset_get_ncolumns(this)
result(res)
219 res = this%columns%count()
228 function resultset_get_integer_from_index(this, col)
result(res)
230 integer,
intent(in) :: col
233 type(
column),
pointer :: c => null()
234 character(:),
allocatable :: str
236 if (col <= 0 .or. col > this%columns%count())
then
241 c => this%columns%get(col)
242 if (
associated(c))
then
243 str = clean_string(c%content)
257 function resultset_get_integer_from_name(this, name)
result(res)
259 character(*),
intent(in) :: name
262 type(
column),
pointer :: c => null()
263 character(:),
allocatable :: str
265 c => this%columns%get(name)
266 if (
associated(c))
then
267 str = clean_string(c%content)
281 function resultset_get_real_from_index(this, col)
result(res)
283 integer,
intent(in) :: col
285 type(
column),
pointer :: c => null()
286 character(:),
allocatable :: str
288 if (col <= 0 .or. col > this%columns%count())
then
293 c => this%columns%get(col)
294 if (
associated(c))
then
295 str = clean_string(c%content)
309 function resultset_get_real_from_name(this, name)
result(res)
311 character(*),
intent(in) :: name
313 type(
column),
pointer :: c => null()
314 character(:),
allocatable :: str
316 c => this%columns%get(name)
317 if (
associated(c))
then
318 str = clean_string(c%content)
332 function resultset_get_double_from_index(this, col)
result(res)
334 integer,
intent(in) :: col
337 type(
column),
pointer :: c => null()
338 character(:),
allocatable :: str
340 if (col <= 0 .or. col > this%columns%count())
then
345 c => this%columns%get(col)
346 if (
associated(c))
then
347 str = clean_string(c%content)
361 function resultset_get_double_from_name(this, name)
result(res)
363 character(*),
intent(in) :: name
366 type(
column),
pointer :: c => null()
367 character(:),
allocatable :: str
369 c => this%columns%get(name)
370 if (
associated(c))
then
371 str = clean_string(c%content)
385 function resultset_get_string_from_index(this, col)
result(res)
387 integer,
intent(in) :: col
388 character(:),
allocatable :: res
390 type(
column),
pointer :: c => null()
392 if (col <= 0 .or. col > this%columns%count())
then
397 c => this%columns%get(col)
398 if (
associated(c))
then
399 res = clean_string(c%content)
412 function resultset_get_string_from_name(this, name)
result(res)
414 character(*),
intent(in) :: name
415 character(:),
allocatable :: res
417 type(
column),
pointer :: c => null()
419 c => this%columns%get(name)
420 if (
associated(c))
then
421 res = clean_string(c%content)
428 subroutine handle_errors(this)
429 class(
resultset),
intent(inout),
target :: this
431 integer(SQLRETURN) :: status
433 status =
sqlgetdiagrec(sql_handle_stmt, this%stmt, this%rec, &
434 this%state, this%ierr, this%msg, &
435 len(this%msg, kind=sqlsmallint), this%imsg)
437 print *, this%msg,
' Error code: ', this%ierr
441 pure function clean_string(value)
result(str)
442 character(*),
intent(in) :: value
443 character(:),
allocatable :: str
449 if (str(i:i) == c_null_char)
exit
451 str =
trim(adjustl(str(:i-1)))
Triming of c-string returning fortran allocatable characters.
Constructor interface for initializing a resultset object with an ODBC statement handle.
Represents metadata and data for a single column in a query result, storing name, type,...
Manages a collection of odbc_columnset::column objects in a query result set, providing methods to ad...
Represents a set of query results from an ODBC query, providing methods to navigate rows and retrieve...