-- -- -- package GNU.DB.SQLCLI.API Copyright (c) Dmitry A. Kazakov -- -- Implementation Luebeck -- -- Winter, 2002 -- -- -- -- Last revision : 14:24 11 Feb 2012 -- -- -- -- This library is free software; you can redistribute it and/or -- -- modify it under the terms of the GNU General Public License as -- -- published by the Free Software Foundation; either version 2 of -- -- the License, or (at your option) any later version. This library -- -- is distributed in the hope that it will be useful, but WITHOUT -- -- ANY WARRANTY; without even the implied warranty of -- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- -- General Public License for more details. You should have -- -- received a copy of the GNU General Public License along with -- -- this library; if not, write to the Free Software Foundation, -- -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- -- -- -- As a special exception, if other files instantiate generics from -- -- this unit, or you link this unit with other files to produce an -- -- executable, this unit does not by itself cause the resulting -- -- executable to be covered by the GNU General Public License. This -- -- exception does not however invalidate any other reasons why the -- -- executable file might be covered by the GNU Public License. -- --____________________________________________________________________-- with Ada.Exceptions; use Ada.Exceptions; with Ada.IO_Exceptions; use Ada.IO_Exceptions; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Strings.Wide_Unbounded; use Ada.Strings.Wide_Unbounded; with GNU.DB.SQLCLI.Info; use GNU.DB.SQLCLI.Info; with Ada.Unchecked_Conversion; with GNU.DB.SQLCLI.Environment_Attribute; use GNU.DB.SQLCLI.Environment_Attribute; with GNU.DB.SQLCLI.Connection_Attribute; use GNU.DB.SQLCLI.Connection_Attribute; package body GNU.DB.SQLCLI.API is use Interfaces; Buffer_Size : constant := 1024; Bytes_In_Wide_Char : constant := 2; SQLGUID_Length : constant := 16; SQLINTEGER_Length : constant := SQLINTEGER'Size / SQLCHAR'Size; SQLDOUBLE_Length : constant := SQLDOUBLE'Size / SQLCHAR'Size; SQLSMALLINT_Length : constant := SQLSMALLINT'Size / SQLCHAR'Size; SQLBIGINT_Length : constant := SQLBIGINT'Size / SQLCHAR'Size; SQLTIMESTAMP_Length : SQLINTEGER := SQL_TIMESTAMP_STRUCT'Size / SQLCHAR'Size; type PTR_SQLGUID is access all SQLGUID; type PTR_SQLBIGINT is access all SQLBIGINT; type PTR_TIMESTAMP is access all SQL_TIMESTAMP_STRUCT; function "<" (Left, Right : SQLGUID) return Boolean is begin if Left.Data1 /= Right.Data1 then return Left.Data1 < Right.Data1; end if; if Left.Data2 /= Right.Data2 then return Left.Data2 < Right.Data2; end if; if Left.Data3 /= Right.Data3 then return Left.Data3 < Right.Data3; end if; for Index in SQLGUID_Octet'Range loop if Left.Data4 (Index) /= Right.Data4 (Index) then return Left.Data4 (Index) < Right.Data4 (Index); end if; end loop; return False; end "<"; function "<=" (Left, Right : SQLGUID) return Boolean is begin if Left.Data1 /= Right.Data1 then return Left.Data1 < Right.Data1; end if; if Left.Data2 /= Right.Data2 then return Left.Data2 < Right.Data2; end if; if Left.Data3 /= Right.Data3 then return Left.Data3 < Right.Data3; end if; for Index in SQLGUID_Octet'Range loop if Left.Data4 (Index) /= Right.Data4 (Index) then return Left.Data4 (Index) < Right.Data4 (Index); end if; end loop; return True; end "<="; function ">" (Left, Right : SQLGUID) return Boolean is begin return Right < Left; end ">"; function ">=" (Left, Right : SQLGUID) return Boolean is begin return Right <= Left; end ">="; function To_SQLPOINTER is new Ada.Unchecked_Conversion (PTR_SQLDOUBLE, SQLPOINTER); function To_SQLPOINTER is new Ada.Unchecked_Conversion (PTR_SQLBIGINT, SQLPOINTER); function To_SQLPOINTER is new Ada.Unchecked_Conversion (PTR_SQLGUID, SQLPOINTER); function To_SQLPOINTER is new Ada.Unchecked_Conversion (PTR_TIMESTAMP, SQLPOINTER); function SQLTables ( StatementHandle : SQLHSTMT; pCatalogName : PTR_SQLTCHAR; NameLength1 : SQLSMALLINT; pSchemaName : PTR_SQLTCHAR; NameLength2 : SQLSMALLINT; pTableName : PTR_SQLTCHAR; NameLength3 : SQLSMALLINT; pTableType : PTR_SQLTCHAR; NameLength4 : SQLSMALLINT ) return SQLRETURN; function SQLTables ( StatementHandle : SQLHSTMT; pCatalogName : PTR_SQLCHAR; NameLength1 : SQLSMALLINT; pSchemaName : PTR_SQLCHAR; NameLength2 : SQLSMALLINT; pTableName : PTR_SQLCHAR; NameLength3 : SQLSMALLINT; pTableType : PTR_SQLCHAR; NameLength4 : SQLSMALLINT ) return SQLRETURN; pragma Import (Stdcall, SQLTables, "SQLTables"); procedure Initialize (Environment : in out ODBC_Environment) is begin SQLAllocHandle ( SQL_HANDLE_ENV, SQL_NULL_HANDLE, Environment.Handle ); SQLSetEnvAttr ( Environment.Handle, Environment_Attribute_ODBC_Version' ( Attribute => SQL_ATTR_ODBC_VERSION, Value => SQL_OV_ODBC3 ) ); end Initialize; procedure Finalize (Environment : in out ODBC_Environment) is begin SQLFreeHandle (SQL_HANDLE_ENV, Environment.Handle); exception when others => null; end Finalize; procedure Initialize (Connection : in out ODBC_Connection) is begin SQLAllocHandle ( SQL_HANDLE_DBC, Connection.Environment.Handle, Connection.Handle ); end Initialize; procedure Connect ( Connection : in out ODBC_Connection; Server_Name : String; User_Name : String; Password : String; Auto_Commit : Boolean ) is begin SQLConnect ( ConnectionHandle => Connection.Handle, ServerName => Server_Name, UserName => User_Name, Authentication => Password ); begin if Auto_Commit then SQLSetConnectAttr ( Connection.Handle, Connection_Attribute_AutoCommit' ( Attribute => SQL_ATTR_AUTOCOMMIT, Value => SQL_AUTOCOMMIT_ON ) ); else SQLSetConnectAttr ( Connection.Handle, Connection_Attribute_AutoCommit' ( Attribute => SQL_ATTR_AUTOCOMMIT, Value => SQL_AUTOCOMMIT_OFF ) ); end if; exception when others => null; end; end Connect; procedure Connect ( Connection : in out ODBC_Connection; Server_Name : Wide_String; User_Name : Wide_String; Password : Wide_String; Auto_Commit : Boolean ) is begin SQLConnect ( ConnectionHandle => Connection.Handle, ServerName => Server_Name, UserName => User_Name, Authentication => Password ); if Auto_Commit then SQLSetConnectAttr ( Connection.Handle, Connection_Attribute_AutoCommit' ( Attribute => SQL_ATTR_AUTOCOMMIT, Value => SQL_AUTOCOMMIT_ON ) ); else SQLSetConnectAttr ( Connection.Handle, Connection_Attribute_AutoCommit' ( Attribute => SQL_ATTR_AUTOCOMMIT, Value => SQL_AUTOCOMMIT_OFF ) ); end if; end Connect; procedure Finalize (Connection : in out ODBC_Connection) is begin begin SQLDisconnect (Connection.Handle); exception when others => -- Ignore disconnection errors for null; -- we might be not connected at all end; SQLFreeHandle (SQL_HANDLE_DBC, Connection.Handle); exception when others => null; end Finalize; procedure Initialize (Command : in out ODBC_Command) is begin SQLAllocHandle ( SQL_HANDLE_STMT, Command.Connection.Handle, Command.Handle ); end Initialize; procedure Close_Cursor (Command : in out ODBC_Command) is begin SQLCloseCursor (Command.Handle); exception when others => -- Ignore any cursor errors null; end Close_Cursor; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access String; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_LONGVARCHAR ) is begin Length.all := Parameter'Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_CHAR, ParameterType => Data_Type, ColumnSize => SQLUINTEGER (Length.all), DecimalDigits => 0, Value => To_SQLPOINTER (Parameter), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access Wide_String; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_WLONGVARCHAR ) is begin Length.all := Parameter'Length * Bytes_In_Wide_Char; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_WCHAR, ParameterType => Data_Type, ColumnSize => SQLUINTEGER (Length.all), DecimalDigits => 0, Value => To_SQLPOINTER (Parameter), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access SQLSMALLINT; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_SMALLINT ) is begin Length.all := SQLSMALLINT_Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_SSHORT, ParameterType => Data_Type, ColumnSize => 0, DecimalDigits => 0, BufferLength => 0, StrLen_Or_IndPtr => Length, Value => To_SQLPOINTER (PTR_SQLSMALLINT (Parameter)) ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access SQLINTEGER; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_INTEGER ) is begin Length.all := SQLINTEGER_Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_SLONG, ParameterType => Data_Type, ColumnSize => 0, DecimalDigits => 0, Value => To_SQLPOINTER (PTR_SQLINTEGER (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access SQLBIGINT; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_BIGINT ) is begin Length.all := SQLBIGINT_Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_SBIGINT, ParameterType => Data_Type, ColumnSize => 0, DecimalDigits => 0, Value => To_SQLPOINTER (PTR_SQLBIGINT (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access SQLDOUBLE; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_DOUBLE ) is begin Length.all := SQLDOUBLE_Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_DOUBLE, ParameterType => Data_Type, ColumnSize => 0, DecimalDigits => 0, Value => To_SQLPOINTER (PTR_SQLDOUBLE (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access SQLGUID; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_GUID ) is begin Length.all := SQLGUID_Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_GUID, ParameterType => Data_Type, ColumnSize => 0, DecimalDigits => 0, Value => To_SQLPOINTER (PTR_SQLGUID (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Parameter ( Command : in out ODBC_Command; Index : Positive; Parameter : access SQL_TIMESTAMP_STRUCT; Length : access SQLINTEGER; Data_Type : SQL_DATA_TYPE := SQL_TIMESTAMP ) is begin Length.all := SQLTIMESTAMP_Length; SQLBindParameter ( StatementHandle => Command.Handle, ParameterNumber => SQL_Parameter_Number (Index), InputOutputType => SQL_PARAM_INPUT, ValueType => SQL_C_TYPE_TIMESTAMP, ParameterType => Data_Type, ColumnSize => 0, DecimalDigits => 0, Value => To_SQLPOINTER (PTR_TIMESTAMP (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Parameter; procedure Bind_Result ( Command : in out ODBC_Command; Column : Positive; Parameter : access SQLSMALLINT; Length : access SQLINTEGER ) is begin Length.all := SQLSMALLINT_Length; SQLBindCol ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_SSHORT, BufferLength => 0, StrLen_Or_IndPtr => Length, TargetValuePtr => To_SQLPOINTER (PTR_SQLSMALLINT (Parameter)) ); end Bind_Result; procedure Bind_Result ( Command : in out ODBC_Command; Column : Positive; Parameter : access SQLINTEGER; Length : access SQLINTEGER ) is begin Length.all := SQLINTEGER_Length; SQLBindCol ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_SLONG, TargetValuePtr => To_SQLPOINTER (PTR_SQLINTEGER (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Result; procedure Bind_Result ( Command : in out ODBC_Command; Column : Positive; Parameter : access SQLBIGINT; Length : access SQLINTEGER ) is begin Length.all := SQLBIGINT_Length; SQLBindCol ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_SBIGINT, TargetValuePtr => To_SQLPOINTER (PTR_SQLBIGINT (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Result; procedure Bind_Result ( Command : in out ODBC_Command; Column : Positive; Parameter : access SQLDOUBLE; Length : access SQLINTEGER ) is begin Length.all := SQLDOUBLE_Length; SQLBindCol ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_DOUBLE, TargetValuePtr => To_SQLPOINTER (PTR_SQLDOUBLE (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Result; procedure Bind_Result ( Command : in out ODBC_Command; Column : Positive; Parameter : access SQLGUID; Length : access SQLINTEGER ) is begin Length.all := SQLGUID_Length; SQLBindCol ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_GUID, TargetValuePtr => To_SQLPOINTER (PTR_SQLGUID (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Result; procedure Bind_Result ( Command : in out ODBC_Command; Column : Positive; Parameter : access SQL_TIMESTAMP_STRUCT; Length : access SQLINTEGER ) is begin Length.all := SQLTIMESTAMP_Length; SQLBindCol ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_TYPE_TIMESTAMP, TargetValuePtr => To_SQLPOINTER (PTR_TIMESTAMP (Parameter)), BufferLength => 0, StrLen_Or_IndPtr => Length ); end Bind_Result; procedure Drop ( Command : in out ODBC_Command'Class; Table_Name : String ) is begin if Table_Exists (Command.Connection, Table_Name) then Execute (Command, "DROP TABLE " & Table_Name); end if; exception when Error : others => Raise_Exception ( Data_Error'Identity, Exception_Message (Error) ); end Drop; procedure Drop ( Command : in out ODBC_Command'Class; Table_Name : Wide_String ) is begin if Table_Exists (Command.Connection, Table_Name) then Execute (Command, "DROP TABLE " & Table_Name); end if; exception when Error : others => Raise_Exception ( Data_Error'Identity, Exception_Message (Error) ); end Drop; procedure EndTran (Connection : in out ODBC_Connection) is begin SQLEndTran ( SQL_HANDLE_DBC, Connection.Handle, SQL_COMMIT ); end EndTran; procedure Execute (Command : in out ODBC_Command) is begin if Command.Connection.Mode = None then Raise_Exception ( Use_Error'Identity, "No access to execute" ); end if; SQLExecute (Command.Handle); end Execute; procedure Execute (Command : in out ODBC_Command; Text : String) is begin if Command.Connection.Mode = None then Raise_Exception ( Use_Error'Identity, "No access to execute" ); end if; SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLExecDirect (Command.Handle, Text); end Execute; procedure Execute ( Command : in out ODBC_Command; Text : Wide_String ) is begin if Command.Connection.Mode = None then Raise_Exception ( Use_Error'Identity, "The connection is in no access mode" ); end if; SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLExecDirect (Command.Handle, Text); end Execute; function Execute (Command : access ODBC_Command) return Natural is begin if Command.Connection.Mode = None then Raise_Exception ( Use_Error'Identity, "No access to execute" ); end if; SQLExecute (Command.Handle); return Natural (SQLRowCount (Command.Handle)); exception when No_Data => return 0; end Execute; function Execute (Command : access ODBC_Command; Text : String) return Natural is begin if Command.Connection.Mode = None then Raise_Exception ( Use_Error'Identity, "No access to execute" ); end if; SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLExecDirect (Command.Handle, Text); return Natural (SQLRowCount (Command.Handle)); exception when No_Data => return 0; end Execute; function Execute (Command : access ODBC_Command; Text : Wide_String) return Natural is begin if Command.Connection.Mode = None then Raise_Exception ( Use_Error'Identity, "No access to execute" ); end if; SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLExecDirect (Command.Handle, Text); return Natural (SQLRowCount (Command.Handle)); exception when No_Data => return 0; end Execute; procedure Fetch (Command : in out ODBC_Command) is begin SQLFetch (Command.Handle); end Fetch; function Fetch (Command : access ODBC_Command) return SQLRETURN is begin return SQLFetch (Command.Handle); end Fetch; function Get_Data ( Command : access ODBC_Command; Column : Positive; Finish : Cursor_Disposition ) return String is Length : aliased SQLINTEGER; Block : aliased String := (1..Buffer_Size => ' '); Buffer : Unbounded_String; begin loop case SQLGetData ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_CHAR, TargetValue => To_SQLPOINTER (Block'Access), BufferLength => Block'Length, StrLen_Or_Ind => Length'Access ) is when SQL_NO_DATA => if Length = SQL_NULL_DATA then Raise_Exception ( End_Error'Identity, "No data set" ); end if; exit; when SQL_SUCCESS => if Length = SQL_NULL_DATA then Raise_Exception ( End_Error'Identity, "No data set" ); end if; Append (Buffer, Block (1..Integer (Length))); exit; when SQL_SUCCESS_WITH_INFO => if Length < 0 then Append (Buffer, Block); else Append (Buffer, Block (1..Integer (Length))); end if; when others => Raise_Exception ( Data_Error'Identity, Get_Message (Command.all) ); end case; end loop; if Finish = Always then Close_Cursor (Command.all); end if; return To_String (Buffer); exception when End_Error => if Finish in On_No_Result..Always then Close_Cursor (Command.all); end if; raise; when others => if Finish in On_Error..Always then Close_Cursor (Command.all); end if; raise; end Get_Data; function Get_Data ( Command : access ODBC_Command; Column : Positive; Finish : Cursor_Disposition ) return Time is Length : aliased SQLINTEGER := SQLTIMESTAMP_Length; Result : aliased SQL_TIMESTAMP_STRUCT; begin case SQLGetData ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_TIMESTAMP, TargetValue => To_SQLPOINTER (Result'Address), BufferLength => SQLTIMESTAMP_Length, StrLen_Or_Ind => Length'Access ) is when SQL_NO_DATA => Raise_Exception ( End_Error'Identity, "No data set" ); when SQL_SUCCESS => if Finish = Always then Close_Cursor (Command.all); end if; return Time_Of ( Year => Year_Number (Result.Year), Month => Month_Number (Result.Month), Day => Day_Number (Result.Day), Seconds => ( Duration (Result.Minute * 60) + Duration (Result.Second) + Duration ( Long_Float (Result.Fraction) / ( Long_Float (SQL_TIME_FRACTION'Last) + 1.0 ) ) ) ); when SQL_SUCCESS_WITH_INFO => Raise_Exception ( Data_Error'Identity, "Data overrun" ); when others => Raise_Exception ( Data_Error'Identity, Get_Message (Command.all) ); end case; exception when End_Error => if Finish in On_No_Result..Always then Close_Cursor (Command.all); end if; raise; when others => if Finish in On_Error..Always then Close_Cursor (Command.all); end if; raise; end Get_Data; function Get_Data ( Command : access ODBC_Command; Column : Positive; Finish : Cursor_Disposition ) return Wide_String is Length : aliased SQLINTEGER; Block : aliased Wide_String := (1..Buffer_Size => ' '); Buffer : Unbounded_Wide_String; begin loop case SQLGetData ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_WCHAR, TargetValue => To_SQLPOINTER (Block'Access), BufferLength => Block'Length * Bytes_In_Wide_Char, StrLen_Or_Ind => Length'Access ) is when SQL_NO_DATA => if Length = SQL_NULL_DATA then Raise_Exception ( End_Error'Identity, "No data set" ); end if; exit; when SQL_SUCCESS => if Length = SQL_NULL_DATA then Raise_Exception ( End_Error'Identity, "No data set" ); end if; Append ( Buffer, Block (1..Integer (Length / Bytes_In_Wide_Char)) ); exit; when SQL_SUCCESS_WITH_INFO => if Length < 0 then Append (Buffer, Block); else Append ( Buffer, Block (1..Integer (Length / Bytes_In_Wide_Char)) ); end if; when others => Raise_Exception ( Data_Error'Identity, Get_Message (Command.all) ); end case; end loop; if Finish = Always then Close_Cursor (Command.all); end if; return To_Wide_String (Buffer); exception when End_Error => if Finish in On_No_Result..Always then Close_Cursor (Command.all); end if; raise; when others => if Finish in On_Error..Always then Close_Cursor (Command.all); end if; raise; end Get_Data; function Get_Data ( Command : access ODBC_Command; Column : Positive; Finish : Cursor_Disposition ) return SQLINTEGER is Length : aliased SQLINTEGER := SQLINTEGER_Length; Result : aliased SQLINTEGER; begin case SQLGetData ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_SLONG, TargetValue => To_SQLPOINTER (Result'Unchecked_Access), BufferLength => SQLINTEGER_Length, StrLen_Or_Ind => Length'Access ) is when SQL_NO_DATA => Raise_Exception ( End_Error'Identity, "No data set" ); when SQL_SUCCESS => if Finish = Always then Close_Cursor (Command.all); end if; return Result; when SQL_SUCCESS_WITH_INFO => Raise_Exception ( Data_Error'Identity, "Data overrun" ); when others => Raise_Exception ( Data_Error'Identity, Get_Message (Command.all) ); end case; exception when End_Error => if Finish in On_No_Result..Always then Close_Cursor (Command.all); end if; raise; when others => if Finish in On_Error..Always then Close_Cursor (Command.all); end if; raise; end Get_Data; function Get_Data ( Command : access ODBC_Command; Column : Positive; Finish : Cursor_Disposition ) return SQLBIGINT is Length : aliased SQLINTEGER := SQLBIGINT_Length; Result : aliased SQLBIGINT; begin case SQLGetData ( StatementHandle => Command.Handle, ColumnNumber => SQL_Column_Number (Column), TargetType => SQL_C_SBIGINT, TargetValue => To_SQLPOINTER (Result'Unchecked_Access), BufferLength => SQLBIGINT_Length, StrLen_Or_Ind => Length'Access ) is when SQL_NO_DATA => Raise_Exception ( End_Error'Identity, "No data set" ); when SQL_SUCCESS => if Finish = Always then Close_Cursor (Command.all); end if; return Result; when SQL_SUCCESS_WITH_INFO => Raise_Exception ( Data_Error'Identity, "Data overrun" ); when others => Raise_Exception ( Data_Error'Identity, Get_Message (Command.all) ); end case; exception when End_Error => if Finish in On_No_Result..Always then Close_Cursor (Command.all); end if; raise; when others => if Finish in On_Error..Always then Close_Cursor (Command.all); end if; raise; end Get_Data; function Get_Message (Command : ODBC_Command) return String is State : aliased SQLSTATE; begin return SQL_Error_Message ( HandleType => SQL_HANDLE_STMT, Handle => Command.Handle, State => State'Access ); end Get_Message; procedure Get_Tables (Command : in out ODBC_Command) is Code : SQLRETURN; begin SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); Code := SQLTables ( Command.Handle, PTR_SQLCHAR'(null), 0, null, 0, null, 0, null, 0 ); Check_SQL_Error ( RC => Code, ProcedureName => "SQLTables", HandleType => SQL_HANDLE_STMT, Handle => Command.Handle ); end Get_Tables; function Get_Type_Info ( Command : access ODBC_Command; Data_Type : SQL_DATA_TYPE ) return Type_Info is Result : Type_Info; function To_SQL_DATA_TYPE is new Ada.Unchecked_Conversion (SQLSMALLINT, SQL_DATA_TYPE); function To_NULLABLE_INFO is new Ada.Unchecked_Conversion (SQLSMALLINT, SQL_NULLABLE_INFO); -- function To_SEARCHABLE_ATTRIBUTE is new -- Ada.Unchecked_Conversion (SQLSMALLINT, SEARCHABLE_ATTRIBUTE); begin SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLGetTypeInfo (Command.Handle, Data_Type); Fetch (Command.all); -- Column 1 Append (Result.Type_Name, Get_Data (Command, 1, On_Error)); -- Column 2 Result.Data_Type := To_SQL_DATA_TYPE ( SQLSMALLINT ( SQLINTEGER' ( Get_Data (Command, 2, On_Error) ) ) ); -- Column 3 begin Result.Column_Size := Integer (SQLINTEGER'(Get_Data (Command, 3, On_Error))); exception when End_Error => Result.Column_Size := 0; end; -- Column 4 begin Append ( Result.Literal_Prefix, Get_Data (Command, 4, On_Error) ); exception when End_Error => null; end; -- Column 5 begin Append ( Result.Literal_Suffix, Get_Data (Command, 5, On_Error) ); exception when End_Error => null; end; -- Column 6 begin Append ( Result.Create_Parameters, Get_Data (Command, 6, On_Error) ); exception when End_Error => null; end; -- Column 7 begin Result.Nullable := ( SQL_NULLABLE = To_NULLABLE_INFO ( SQLSMALLINT ( SQLINTEGER' ( Get_Data (Command, 7, On_Error) ) ) ) ); exception when End_Error => Result.Nullable := False; end; -- Column 8 Result.Case_Sensitive := SQLINTEGER'(Get_Data (Command, 8, On_Error)) /= 0; -- Column 9 Result.Searchable := -- To_SEARCHABLE_ATTRIBUTE ( SQLSMALLINT ( SQLINTEGER' ( Get_Data (Command, 9, On_Error) ) ) ); -- Column 10 begin Result.Unsigned_Attribute := SQLINTEGER'(Get_Data (Command, 10, On_Error)) /= 0; exception when End_Error => Result.Unsigned_Attribute := True; end; -- Column 11 Result.Fixed_Prec_Scale := SQLINTEGER'(Get_Data (Command, 11, On_Error)) /= 0; -- Column 12 begin Result.Auto_Unique_Value := SQLINTEGER'(Get_Data (Command, 12, On_Error)) /= 0; exception when End_Error => Result.Auto_Unique_Value := False; end; -- Column 13 begin Append ( Result.Local_Name, Get_Data (Command, 13, On_Error) ); exception when End_Error => null; end; Close_Cursor (Command.all); return Result; exception when No_Data | Constraint_Error => Close_Cursor (Command.all); Raise_Exception ( Constraint_Error'Identity, "Not supported" ); when others => Close_Cursor (Command.all); Raise_Exception ( Data_Error'Identity, Get_Message (Command.all) ); end Get_Type_Info; procedure Prepare ( Command : in out ODBC_Command; Request : String ) is begin SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLPrepare (Command.Handle, Request); end Prepare; procedure Prepare ( Command : in out ODBC_Command; Request : Wide_String ) is begin SQLFreeStmt (Command.Handle, SQL_UNBIND); SQLFreeStmt (Command.Handle, SQL_RESET_PARAMS); SQLPrepare (Command.Handle, Request); end Prepare; procedure Finalize (Command : in out ODBC_Command) is begin SQLFreeHandle (SQL_HANDLE_STMT, Command.Handle); exception when others => null; end Finalize; procedure Release (Connection : ODBC_Connection) is begin SQLSetConnectAttr ( Connection.Handle, Connection_Attribute_TXN_Isolation' ( SQL_ATTR_TXN_ISOLATION, SQL_TRANSACTION_READ_UNCOMMITTED ) ); end Release; procedure RollBack (Connection : in out ODBC_Connection) is begin SQLEndTran ( SQL_HANDLE_DBC, Connection.Handle, SQL_ROLLBACK ); end RollBack; procedure Seize (Connection : ODBC_Connection) is begin SQLSetConnectAttr ( Connection.Handle, Connection_Attribute_TXN_Isolation' ( SQL_ATTR_TXN_ISOLATION, SQL_TRANSACTION_SERIALIZABLE ) ); end Seize; procedure Set_Access_Mode ( Connection : in out ODBC_Connection; Mode : Access_Mode ) is begin Connection.Mode := Mode; end Set_Access_Mode; function Serializable (Connection : ODBC_Connection) return Boolean is begin return TXN_Isolation_Info ( SQLGetInfo ( Connection.Handle, SQL_TXN_ISOLATION_OPTION ) ) .Value (SQL_TXN_SERIALIZABLE); end Serializable; function Table_Exists ( Connection : access ODBC_Connection; Name : String ) return Boolean is Code : SQLRETURN; Command : ODBC_Command (Connection); begin Code := SQLTables ( Command.Handle, null, 0, null, 0, To_PTR_SQLCHAR (Name'Address), SQLSMALLINT (Name'Length), null, 0 ); Check_SQL_Error ( RC => Code, ProcedureName => "SQLTables", HandleType => SQL_HANDLE_STMT, Handle => Command.Handle ); SQLFetch (Command.Handle); return True; exception when No_Data | Table_Not_Found => return False; end Table_Exists; function Table_Exists ( Connection : access ODBC_Connection; Name : Wide_String ) return Boolean is Code : SQLRETURN; Command : ODBC_Command (Connection); begin Code := SQLTables ( Command.Handle, null, 0, null, 0, To_PTR_SQLTCHAR (Name'Address), SQLSMALLINT (Name'Length), null, 0 ); Check_SQL_Error ( RC => Code, ProcedureName => "SQLTables", HandleType => SQL_HANDLE_STMT, Handle => Command.Handle ); SQLFetch (Command.Handle); return True; exception when No_Data | Table_Not_Found => return False; end Table_Exists; begin Generate_Detailed_Exceptions := True; end GNU.DB.SQLCLI.API;