use "debug"
use "ffi"
use "collections"
struct \nodoc\ ODBCHandleEnv
class ODBCEnv is SqlState
"""
# ODBCEnv
The class that wraps our ODBC Environment handle.
## Usage
It is currently very simple as very few of the attributes are implemented
as of yet.
Currently, we only support ODBC version 3.0.
```pony
var oenv: ODBCEnv = ODBCEnv
```
## Error Handling
ODBCEnv maintains an error chain that can aggregate errors from all
child connections and their statements via `all_errors()`:
```pony
// Get all errors across entire handle hierarchy, sorted by sequence
for err in env.all_errors().values() do
env.err.print(err.string())
end
```
"""
let odbcenv: ODBCHandleEnv tag
var strict: Bool = true
var _err: SQLReturn val
var _call_location: SourceLoc val = __loc
var _error_chain: ODBCErrorChain = ODBCErrorChain
var _connections: Array[ODBCDbc] = Array[ODBCDbc]
new create() => None
"""
Creates a new ODBC Environment
"""
var envwrapper: EnvWrapper = EnvWrapper
_err = ODBCFFI.resolve(ODBCFFI.pSQLAllocHandle_env(envwrapper))
odbcenv = envwrapper.value
ODBCFFI.pSQLSetEnvAttr(odbcenv, _SqlAttrODBCVersion(), _SqlODBC3(), _SQLIsInteger())
fun ref dbc(sl: SourceLoc val = __loc): ODBCDbc ? =>
"""
Used to create an ODBCDbc object from this ODBCEnv.
"""
_call_location = sl
var dbh: ODBCDbc = ODBCDbc(this)
_err = dbh.alloc()
_check_valid("dbc")?
_connections.push(dbh)
dbh
fun sqlstates(): Array[(String val, String val)] val =>
"""
Returns an array of SQL States
"""
_from_env(odbcenv)
fun ref _capture_error(operation: String val) =>
"""
Capture the current error state into the error chain.
Called before throwing an error.
"""
let diags = sqlstates()
let frame = ODBCErrorFrame(
operation,
_call_location,
_err,
diags,
_error_chain.next_sequence(),
None,
"env",
false
)
_error_chain.push(frame)
fun ref _record_success(operation: String val) =>
"""
Record a successful operation if log_success is enabled.
"""
if _error_chain.log_success() then
let frame = ODBCErrorFrame(
operation,
_call_location,
_err,
recover val Array[(String val, String val)] end,
_error_chain.next_sequence(),
None,
"env",
true
)
_error_chain.push(frame)
end
fun \nodoc\ ref _check_valid(operation: String val = ""): Bool ? =>
if strict then
match _err
| let x: SQLSuccess val =>
_record_success(operation)
return true
else
_capture_error(operation)
error
end
else
match _err
| let x: SQLSuccess val =>
_record_success(operation)
return true
| let x: SQLSuccessWithInfo val =>
_record_success(operation)
return true
else
_capture_error(operation)
error
end
end
fun error_chain(): ODBCErrorChain box =>
"""
Access the error chain for inspection.
"""
_error_chain
fun last_error(): (ODBCErrorFrame val | None) =>
"""
Convenience method: get the most recent error frame.
"""
_error_chain.last()
fun ref configure_error_chain(
max_frames: USize = 100,
auto_clear: Bool = true,
log_success: Bool = false
) =>
"""
Configure error chain behavior.
- max_frames: Maximum number of frames to store (default: 100)
- auto_clear: Clear chain on prepare()/finish() (default: true)
- log_success: Also record successful operations (default: false)
"""
_error_chain.set_max_frames(max_frames)
_error_chain.set_auto_clear(auto_clear)
_error_chain.set_log_success(log_success)
fun all_errors(): Array[ODBCErrorFrame val] val =>
"""
Returns all errors across the entire handle hierarchy, sorted by sequence.
"""
let result: Array[ODBCErrorFrame val] ref = Array[ODBCErrorFrame val]
// Add own errors
for e in _error_chain.values() do
result.push(e)
end
// Add connection errors (which includes statement errors)
for conn in _connections.values() do
for e in conn.all_errors().values() do
result.push(e)
end
end
// Sort by sequence number
let sorted = Sort[Array[ODBCErrorFrame val], ODBCErrorFrame val](result)
// Convert to val
let out: Array[ODBCErrorFrame val] trn = recover trn Array[ODBCErrorFrame val] end
for e in sorted.values() do
out.push(e)
end
consume out
fun \nodoc\ ref get_attr_i32(a: _SqlEnvAttr, v: CBoxedI32, sl: SourceLoc val = __loc): Bool ? =>
"""
This is a primitive getter function used to retrieve i32 attributes
from the ODBC Handle.
"""
_call_location = sl
_err = ODBCFFI.resolve(ODBCFFI.pSQLGetEnvAttr_i32(odbcenv, a(), v, 0, CBoxedI32))
_check_valid("get_attr_i32")?
fun _final() =>
ODBCFFI.pSQLFreeHandle_env(odbcenv)