Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
slowly getting closer to properly working
- Loading branch information
1 parent
6c58220
commit 2c47f41
Showing
7 changed files
with
263 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
module ODBC | ||
class Field | ||
@name : String | ||
@col_type : SqlDataType | ||
@col_size : LibODBC::SqlULen | ||
@nullable : Bool | ||
|
||
getter name, col_type, col_size, nullable | ||
|
||
def initialize(stmt : Void*, col_num : Int32) | ||
LibODBC.describe_col(stmt, col_num, out name, | ||
256, out name_len, out col_type, | ||
out col_size, out digits, out nullable) | ||
|
||
@name = String.new(Pointer(UInt8).new(name)) | ||
@col_type = SqlDataType.new(col_type.to_i32) | ||
@col_size = col_size | ||
@nullable = case nullable | ||
when LibODBC::Nullable::SqlNullable | ||
true | ||
else | ||
false | ||
end | ||
end | ||
end | ||
|
||
class ResultSet < DB::ResultSet | ||
@num_cols : Int32 | ||
@rows_affected : Int64 | ||
@buffer : Array(UInt8*) | ||
@strlen : Array(Int64) | ||
|
||
getter rows_affected | ||
|
||
def initialize(statement) | ||
super(statement) | ||
@col_index = 0 | ||
@row_index = 0_i64 | ||
|
||
LibODBC.row_count(statement.raw_stmt, out rows_affected) | ||
@rows_affected = rows_affected | ||
|
||
LibODBC.num_result_cols(statement.raw_stmt, out num_cols) | ||
@num_cols = num_cols.to_i32 | ||
|
||
@fields = Array(ODBC::Field).new | ||
i = 0 | ||
while i < @num_cols | ||
@fields.push(ODBC::Field.new(statement.raw_stmt, i)) | ||
i += 1 | ||
end | ||
|
||
@buffer = Array(UInt8*).new(@num_cols, Pointer(UInt8).null) | ||
@strlen = Array(Int64).new(@num_cols, 0) | ||
i = 0 | ||
while i < @num_cols | ||
# kind of awkward workaround for dealing with an array of pointers and the fact that | ||
# arrays are themselves built of pointers and somake accessing the contained pointers | ||
# a bit clumsy | ||
# | ||
# TODO: a better way to handle this probably? | ||
tmp_buf = Pointer(UInt8).malloc | ||
|
||
# and here, since we're calling C functions here we have to specify the length of the buffer into which | ||
# we're reading the SqlCChars. does bind_col realloc strictly based on that? need to find some way to | ||
# get around this since we'd rather dynamically rellocate memory to accommodate a large field than | ||
# unnecessarily snip the end off | ||
LibODBC.bind_col(statement.raw_stmt, i + 1, SqlCDataType::SqlCChar.value, tmp_buf.as(Void*), 256, out ind) | ||
@buffer[i] = tmp_buf | ||
@strlen[i] = ind | ||
i += 1 | ||
end | ||
end | ||
|
||
protected def conn | ||
statement.as(Statement).conn | ||
end | ||
|
||
def move_next | ||
if @row_index < @rows_affected - 1 | ||
@row_index += 1 | ||
true | ||
else | ||
false | ||
end | ||
end | ||
|
||
def column_count : Int32 | ||
@num_cols | ||
end | ||
|
||
def column_name(index : Int32) : String | ||
@fields[index].name | ||
end | ||
|
||
def column_type(index : Int32) : SqlDataType | ||
@fields[index].col_type | ||
end | ||
|
||
def read | ||
case @col_index | ||
when 0 | ||
result = LibODBC.fetch_scroll(statement.raw_stmt, LibODBC::FetchOrientation::SqlFetchAbsolute, @row_index + 1) | ||
if result != LibODBC::SqlReturn::SqlSuccess && result != LibODBC::SqlReturn::SqlSuccessWithInfo | ||
err = ODBC.get_detail("SQLFetchScroll", statement.raw_stmt, 1) | ||
raise "Error fetching row #{@row_index + 1}: #{err}" | ||
end | ||
|
||
@col_index += 1 | ||
return @buffer[0] | ||
when .<(@num_cols) | ||
value = buffer[@col_index] | ||
@col_index += 1 | ||
return value | ||
else | ||
@col_index = 0 | ||
@row_index += 1 | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,42 @@ | ||
module ODBC | ||
class Statement < DB::Statement | ||
def initialize(connection, @query_sql : String) | ||
@raw_stmt : Void* | ||
@encoded_query : Bytes | ||
getter raw_stmt | ||
|
||
def initialize(connection, query : String) | ||
super(connection) | ||
@raw_stmt = Pointer(Void).null | ||
|
||
@encoded_query = ODBC.encode_nts(query) | ||
end | ||
|
||
protected def conn | ||
connection.as(Connection).connection | ||
end | ||
|
||
protected def perform_query(args : Enumerable) : ODBC::ResultSet | ||
body = ODBC.alloc_statement(@connection) | ||
LibODBC.tables(body, nil, 0, nil, 0, nil, 0, "TABLE", 6) | ||
@raw_stmt = ODBC.alloc_stmt(@connection.raw_conn) | ||
|
||
prep_result = LibODBC.prepare(raw_stmt, @encoded_query.to_unsafe, @encoded_query.size) | ||
if prep_result != LibODBC::SqlReturn::SqlSuccess && prep_result != LibODBC::SqlReturn::SqlSuccessWithInfo | ||
err = ODBC.get_detail("SQLPrepare", @raw_stmt, 1) | ||
raise "Error preparing SQL statement: #{err}" | ||
end | ||
|
||
exec_result = LibODBC.execute(raw_stmt) | ||
if exec_result != LibODBC::SqlReturn::SqlSuccess && exec_result != LibODBC::SqlReturn::SqlSuccessWithInfo | ||
err = ODBC.get_detail("SQLExecute", @raw_stmt, 1) | ||
raise "Error executing SQL statement: #{err}" | ||
end | ||
|
||
ODBC::ResultSet.new(self) | ||
end | ||
|
||
protected def perform_exec(args : Enumerable) : ::DB::ExecResult | ||
protected def perform_exec(args : Enumerable) : DB::ExecResult | ||
result = perform_query(args) | ||
result.each { } | ||
DB::ExecResult.new(rows_affected: result.rows_affected, last_insert_id: 0_i64) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,80 @@ | ||
alias SqlChar = UInt8 | ||
|
||
alias SqlWChar = UInt32 | ||
|
||
alias SqlSmallInt = Int16 | ||
|
||
alias SqlUSmallInt = UInt16 | ||
|
||
alias SqlInteger = Int32 | ||
|
||
alias SqlUInteger = UInt32 | ||
|
||
alias SqlReal = Float32 | ||
|
||
alias SqlDouble = Float64 | ||
|
||
alias SqlFloat = Float64 | ||
|
||
alias SqlBigInt = Int64 | ||
|
||
alias SqlUBigInt = UInt64 | ||
|
||
alias Bookmark = Array(UInt32) | ||
enum SqlDataType | ||
SqlUnknownType = 0 | ||
SqlChar = 1 | ||
SqlNumeric = 2 | ||
SqlDecimal = 3 | ||
SqlInteger = 4 | ||
SqlSmallInt = 5 | ||
SqlFloat = 6 | ||
SqlReal = 7 | ||
SqlDouble = 8 | ||
SqlDatetime = 9 | ||
SqlVarchar = 12 | ||
SqlUdt = 17 | ||
SqlRow = 19 | ||
SqlArray = 50 | ||
SqlMultiset = 55 | ||
SqlDate = 91 | ||
SqlTime = 92 | ||
SqlTimestamp = 93 | ||
SqlTimeWithTimezone = 94 | ||
SqlTimestampWithTimezone = 95 | ||
SqlExtlongVarchar = -1 | ||
SqlExtBinary = -2 | ||
SqlExtVarbinary = -3 | ||
SqlExtlongvarbinary = -4 | ||
SqlExtBigInt = -5 | ||
SqlExtTinyInt = -6 | ||
SqlExtBit = -7 | ||
SqlExtWChar = -8 | ||
SqlExtWVarchar = -9 | ||
SqlExtwLongVarchar = -10 | ||
SqlExtGuid = -11 | ||
SqlSsVariant = -150 | ||
SqlSsUdt = -151 | ||
SqlSsXml = -152 | ||
SqlSsTable = -153 | ||
SqlSsTime2 = -154 | ||
SqlSsTimestampOffset = -155 | ||
end | ||
|
||
enum SqlResult | ||
SqlSuccess | ||
SqlSuccessWithInfo | ||
SqlInvalidHandle | ||
SqlError | ||
enum SqlCDataType | ||
SqlCUTinyInt = -28 | ||
SqlCUBigInt = -27 | ||
SqlCSTinyInt = -26 | ||
SqlCSBigInt = -25 | ||
SqlCULong = -18 | ||
SqlCUShort = -17 | ||
SqlCSLong = -16 | ||
SqlCSShort = -15 | ||
SqlCGuid = -11 | ||
SqlCWChar = -8 | ||
SqlCBit = -7 | ||
SqlCBinary = -2 | ||
SqlCChar = 1 | ||
SqlCNumeric = 2 | ||
SqlCFloat = 7 | ||
SqlCDouble = 8 | ||
SqlCDate = 9 | ||
SqlCTime = 10 | ||
SqlCTimestamp = 11 | ||
SqlCTypeDate = 91 | ||
SqlCTypeTime = 92 | ||
SqlCTypeTimestamp = 93 | ||
SqlCTypeTimeWithTimezone = 94 | ||
SqlCTypeTimestampWithTimezone = 95 | ||
SqlCDefault = 99 | ||
SqlCIntervalYear = 101 | ||
SqlCIntervalMonth = 102 | ||
SqlCIntervalDay = 103 | ||
SqlCIntervalHour = 104 | ||
SqlCIntervalMinute = 105 | ||
SqlCIntervalSecond = 106 | ||
SqlCIntervalYearToMonth = 107 | ||
SqlCIntervalDayToHour = 108 | ||
SqlCIntervalDayToMinute = 109 | ||
SqlCIntervalDayToSecond = 110 | ||
SqlCIntervalHourToMinute = 111 | ||
SqlCIntervalHourToSecond = 112 | ||
SqlCIntervalMinuteToSecond = 113 | ||
end |