Loading...
Searching...
No Matches
resultset.f90
1
4module odbc_resultset
5 use, intrinsic :: iso_c_binding
6 use, intrinsic :: iso_fortran_env
7 use sql
8 use odbc_columnset
9 use odbc_constants
10
11 implicit none; private
12
13 public :: columnset, &
14 new
15
20 type, public :: resultset
21 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
31 contains
32 private
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
57 end type
58
63 interface new
64 module procedure :: resultset_new
65 end interface
66
67contains
68
74 subroutine resultset_new(that, stmt)
75 type(sqlhstmt), intent(in) :: stmt
76 type(resultset), target :: that
77 !private
78 integer(SQLRETURN) :: rc
79
80 that%stmt = stmt
81 that%rows = 0
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)
86 end subroutine
87
88 subroutine resultset_get_metadata(this, columns)
89 class(resultset), intent(inout) :: this
90 type(columnset), intent(inout) :: columns
91 !private
92 integer :: i
93 integer(SQLSMALLINT) :: name_length
94 integer(SQLSMALLINT), allocatable :: column_count
95 type(column), allocatable, target :: cols(:)
96 integer :: nErr, nMsg, iRec, cState
97
98 allocate(column_count, source = 0_sqlsmallint)
99 this%ierr = sqlnumresultcols(this%stmt, column_count)
100 if (this%ierr == sql_error .or. this%ierr == sql_invalid_handle) then
101 call this%handle_errors()
102 end if
103
104 allocate(cols(column_count))
105 do i = 1, column_count
106 allocate(character(51) :: cols(i)%name)
107 cols(i)%decim_size = 0
108 cols(i)%nullable = 0
109 cols(i)%size = 0
110 cols(i)%type = 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()
116 end if
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)
120 end do
121 call columns%addrange(cols)
122
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()
126 end do
127 end subroutine
128
132 logical function resultset_movenext(this) result(res)
133 class(resultset), intent(inout) :: this
134 integer(SQLRETURN) :: rc
135 !private
136 integer(c_long) :: offset
137 offset = 0_c_long
138
139 res = .true.
140 rc = sqlfetchscroll(this%stmt, sql_fetch_next, offset)
141 if (rc == sql_no_data) res = .false.
142 if (rc == sql_error) call this%handle_errors()
143 end function
144
150 logical function resultset_moveprevious(this) result(res)
151 class(resultset), intent(inout) :: this
152 integer(SQLRETURN) :: rc
153 !private
154 integer(c_long) :: offset
155 offset = 0_c_long
156
157 res = .true.
158 rc = sqlfetchscroll(this%stmt, sql_fetch_prior, offset)
159 if (rc == sql_no_data .or. rc < sql_error) res = .false.
160 if (rc == sql_error) call this%handle_errors()
161 end function
162
168 logical function resultset_movefirst(this) result(res)
169 class(resultset), intent(inout) :: this
170 integer(SQLRETURN) :: rc
171 !private
172 integer(c_long) :: offset
173 offset = 0_c_long
174
175 res = .true.
176 rc = sqlfetchscroll(this%stmt, sql_fetch_first, 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()
180 end function
181
187 logical function resultset_movelast(this) result(res)
188 class(resultset), intent(inout) :: this
189 integer(SQLRETURN) :: rc
190 !private
191 integer(c_long) :: offset
192 offset = 0_c_long
193
194 res = .true.
195 rc = sqlfetchscroll(this%stmt, sql_fetch_last, offset)
196 if (rc == sql_no_data .or. rc < sql_error) res = .false.
197 if (rc == sql_error) call this%handle_errors()
198 end function
199
204 function resultset_get_nrows(this) result(res)
205 class(resultset), intent(in) :: this
206 integer :: res
207
208 res = this%rows
209 end function
210
215 function resultset_get_ncolumns(this) result(res)
216 class(resultset), intent(in) :: this
217 integer :: res
218
219 res = this%columns%count()
220 end function
221
228 function resultset_get_integer_from_index(this, col) result(res)
229 class(resultset), intent(inout) :: this
230 integer, intent(in) :: col
231 integer :: res
232 !private
233 type(column), pointer :: c => null()
234 character(:), allocatable :: str
235
236 if (col <= 0 .or. col > this%columns%count()) then
237 res = 0
238 return
239 end if
240
241 c => this%columns%get(col)
242 if (associated(c)) then
243 str = clean_string(c%content)
244 read(str, *) res
245 else
246 res = 0
247 end if
248 nullify(c)
249 end function
250
257 function resultset_get_integer_from_name(this, name) result(res)
258 class(resultset), intent(inout) :: this
259 character(*), intent(in) :: name
260 integer :: res
261 !private
262 type(column), pointer :: c => null()
263 character(:), allocatable :: str
264
265 c => this%columns%get(name)
266 if (associated(c)) then
267 str = clean_string(c%content)
268 read(str, *) res
269 else
270 res = 0
271 end if
272 nullify(c)
273 end function
274
281 function resultset_get_real_from_index(this, col) result(res)
282 class(resultset), intent(inout) :: this
283 integer, intent(in) :: col
284 real(real32) :: res
285 type(column), pointer :: c => null()
286 character(:), allocatable :: str
287
288 if (col <= 0 .or. col > this%columns%count()) then
289 res = 0_real32
290 return
291 end if
292
293 c => this%columns%get(col)
294 if (associated(c)) then
295 str = clean_string(c%content)
296 read(str, *) res
297 else
298 res = 0.0_real32
299 end if
300 nullify(c)
301 end function
302
309 function resultset_get_real_from_name(this, name) result(res)
310 class(resultset), intent(inout) :: this
311 character(*), intent(in) :: name
312 real(real32) :: res
313 type(column), pointer :: c => null()
314 character(:), allocatable :: str
315
316 c => this%columns%get(name)
317 if (associated(c)) then
318 str = clean_string(c%content)
319 read(str, *) res
320 else
321 res = 0.0_real32
322 end if
323 nullify(c)
324 end function
325
332 function resultset_get_double_from_index(this, col) result(res)
333 class(resultset), intent(inout) :: this
334 integer, intent(in) :: col
335 real(real64) :: res
336 !private
337 type(column), pointer :: c => null()
338 character(:), allocatable :: str
339
340 if (col <= 0 .or. col > this%columns%count()) then
341 res = 0.0_real64
342 return
343 end if
344
345 c => this%columns%get(col)
346 if (associated(c)) then
347 str = clean_string(c%content)
348 read(str, *) res
349 else
350 res = 0.0_real64
351 end if
352 nullify(c)
353 end function
354
361 function resultset_get_double_from_name(this, name) result(res)
362 class(resultset), intent(inout) :: this
363 character(*), intent(in) :: name
364 real(real64) :: res
365 !private
366 type(column), pointer :: c => null()
367 character(:), allocatable :: str
368
369 c => this%columns%get(name)
370 if (associated(c)) then
371 str = clean_string(c%content)
372 read(str, *) res
373 else
374 res = 0.0_real64
375 end if
376 nullify(c)
377 end function
378
385 function resultset_get_string_from_index(this, col) result(res)
386 class(resultset), intent(inout) :: this
387 integer, intent(in) :: col
388 character(:), allocatable :: res
389 !private
390 type(column), pointer :: c => null()
391
392 if (col <= 0 .or. col > this%columns%count()) then
393 res = ''
394 return
395 end if
396
397 c => this%columns%get(col)
398 if (associated(c)) then
399 res = clean_string(c%content)
400 else
401 res = ''
402 end if
403 nullify(c)
404 end function
405
412 function resultset_get_string_from_name(this, name) result(res)
413 class(resultset), intent(inout) :: this
414 character(*), intent(in) :: name
415 character(:), allocatable :: res
416 !private
417 type(column), pointer :: c => null()
418
419 c => this%columns%get(name)
420 if (associated(c)) then
421 res = clean_string(c%content)
422 else
423 res = ''
424 end if
425 nullify(c)
426 end function
427
428 subroutine handle_errors(this)
429 class(resultset), intent(inout), target :: this
430 !private
431 integer(SQLRETURN) :: status
432
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)
436
437 print *, this%msg, ' Error code: ', this%ierr
438 error stop this%ierr
439 end subroutine
440
441 pure function clean_string(value) result(str)
442 character(*), intent(in) :: value
443 character(:), allocatable :: str
444 !private
445 integer i
446
447 str = value
448 do i = 1, len(str)
449 if (str(i:i) == c_null_char) exit
450 end do
451 str = trim(adjustl(str(:i-1)))
452 end function
453
454end 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 metadata and data for a single column in a query result, storing name, type,...
Definition columnset.f90:14
Manages a collection of odbc_columnset::column objects in a query result set, providing methods to ad...
Definition columnset.f90:27
Represents a set of query results from an ODBC query, providing methods to navigate rows and retrieve...
Definition resultset.f90:20