Connecting and session reusing

Forum related to MS SQL Server

Moderators: gto, cipto_kh, EgonHugeist

Post Reply
kenny
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 07.09.2019, 11:24

Connecting and session reusing

Post by kenny »

I'm using Windows 10 1903 18362.295, Embarcadero Delphi 10.2 25.0.26309.314, Microsoft SQL Server 2016 13.0.1601.5, ZeosLib 7.3 5906, FreeTDS supplied with ZeosLib and Microsoft OLE DB Driver 18.2.2.0.

When I try to execute the following code using FreeTDS the program exits without any exception. When I use address and port instead of named pipe it hangs out indefinitely. When I add timeout with TZConnection.Properties it throws "Access violation at address 00000000 in module 'Test.exe'. Read of address 00000000":

Code: Select all

program Test;

{$apptype console}

uses
	SysUtils, ZDataset, ZAbstractConnection, ZConnection;

procedure FreeTdsTest();
var
	c: TZConnection;
begin
	c := nil;
	try
		c := TZConnection.Create(nil);//sybdb.dll
		c.Protocol := 'FreeTds_MsSQL>=2005';
		c.HostName := '.\sqlexpress';//or c.HostName with ip and c.Port with port and c.Properties.Add with timeout
		c.Database := 'master';
		c.User := 'sa';
		c.Password := '1';
		c.LoginPrompt := false;
		c.UseMetadata := false;
		c.Connect();//here
		c.Disconnect();
	finally
		c.Free();
	end;
end;

begin
	try
		FreeTdsTest();
	except on e: Exception do begin
		Write(e.Message, #10);
	end end;
end.
When I try to execute the following code using OleDB the program throws the exception "Access violation at address 00762A4A in module 'Test.exe'. Read of address 00000000". The exception comes from 3313 line in src\component\ZAbstractRODataset.pas (if GetColumnCount > 0 then in procedure TZAbstractRODataset.InternalInitFieldDefs):

Code: Select all

program Test;

{$apptype console}

uses
	SysUtils, ZDataset, ZAbstractConnection, ZConnection;

procedure OleDbTest();
var
	c: TZConnection;
	q: TZQuery;
begin
	c := nil;
	q := nil;
	try
		c := TZConnection.Create(nil);
		q := TZQuery.Create(nil);
		c.Protocol := 'OleDB';
		c.Database := 'Provider=MSOLEDBSQL.1;Data Source=.\sqlexpress;Database=master';
		c.User := 'sa';
		c.Password := '1';
		c.LoginPrompt := false;
		c.UseMetadata := false;
		q.Connection := c;
		q.ParamCheck := false;
		c.Connect();
		q.Sql.Add('create table #t (i int)');
		q.Sql.Add('insert into #t values (0)');
		q.Sql.Add('select * from #t');
		q.Sql.Add('drop table #t');
		q.Open();//here
		Write(q.RecordCount, #10);
		q.Close();
		c.Disconnect();
	finally
		q.Free();
		c.Free();
	end;
end;

begin
	try
		OleDbTest();
	except on e: Exception do begin
		Write(e.Message, #10);
	end end;
end.
What I'm doing wrong? Also is there a way to use the same sql session between TZConnection.Connect and TZConnection.Disconnect as reuse the session after TZQuery.ExecSql or TZQuery.Open and TzQuery.Close with any of the protocols?
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1956
Joined: 17.01.2011, 14:17

Re: Connecting and session reusing

Post by marsupilami »

Hello Kenny,

welcome to the forums :)

regarding freetds: I fixed a bug yesterday that might have lead to the access violation you have reported. Could you please test the current svn version of Zeos 7.3 (Rev. 5911 or later)?

Regarding your OLEDB problem - I will have to check what happens there. Maybe I will have time to do that today in the evening.

Regarding sessions in Zeos: As soon as you call ZConnection.Connect or open a ZQuery or ZTable, a connection to you server is established and you will get a session. This session will be used until you explicitly call ZConnection.Disconnect.

Best regards,

Jan
kenny
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 07.09.2019, 11:24

Re: Connecting and session reusing

Post by kenny »

Revision 5911 still crashes the same way. When HostName is named pipe it does not report any exception just exits at line 403 in src\dbc\ZDbcDbLib.pas (FHandle := FPlainDriver.dbOpen(LoginRec, Pointer(RawTemp)) in procedure TZDBLibConnection.InternalLogin). When HostName is ip and Port is set it hangs out at 1425 line in src\plain\ZPlainDbLibDriver.pas (then Result := FdbOpen(Login, server) in function TZDBLIBPLainDriver.dbOpen(Login: PLOGINREC; server: PAnsiChar): PDBPROCESS). When Properties.Add('timeout=30') is added it raises the exception "Access violation at address 00000000 in module 'Test.exe'. Read of address 00000000" at line 1599 in src\plain\ZPlainDbLibDriver.pas (else Result := FdbSetLoginTime_stdcall(Seconds) in function TZDBLIBPLainDriver.dbSetLoginTime(Seconds: Integer): RETCODE).

I've tried ado with 'Provider=MSOLEDBSQL.1;Data Source=.\sqlexpress;Database=master' and odbc_w with 'Driver={ODBC Driver 17 for SQL Server};Server=.\sqlexpress;Database=master' and they can connect, but when I use the following code they do not retain the session between TZQuery.ExecSql or TZQuery.Open and TzQuery.Close:

Code: Select all

c.Connect();
q.Sql.Add('create table #t (i int)');
q.ExecSql();
q.Sql.Clear();
q.Sql.Add('insert into #t values (0)');
q.ExecSql();
q.Sql.Clear();
q.Sql.Add('select * from #t');
q.Open();
Write(q.RecordCount, #10);
q.Close();
q.Sql.Clear();
q.Sql.Add('drop table #t');
q.ExecSql();
c.Disconnect();
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1956
Joined: 17.01.2011, 14:17

Re: Connecting and session reusing

Post by marsupilami »

Hello Kenny,
kenny wrote:When HostName is named pipe
I didn't see that earlier. FreeTDS doesn't support named pipes. FreeTDS only supports TCP/IP. What works for me is to enable the SQl Server Browser service and use the server instance name:

Code: Select all

ZConnection.HostName := 'computername\databaseinstance';
In a scenario like that the port becomes irrelevant because FreeTDS will check it for iself.

Regarding the rest of your problems - I will have to check them later on. I didn't have the time to do debugging for those things today.

Best regards,

Jan
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1956
Joined: 17.01.2011, 14:17

Re: Connecting and session reusing

Post by marsupilami »

kenny wrote:Revision 5911 still crashes the same way. When HostName is named pipe it does not report any exception just exits at line 403 in src\dbc\ZDbcDbLib.pas (FHandle := FPlainDriver.dbOpen(LoginRec, Pointer(RawTemp)) in procedure TZDBLibConnection.InternalLogin). When HostName is ip and Port is set it hangs out at 1425 line in src\plain\ZPlainDbLibDriver.pas (then Result := FdbOpen(Login, server) in function TZDBLIBPLainDriver.dbOpen(Login: PLOGINREC; server: PAnsiChar): PDBPROCESS). When Properties.Add('timeout=30') is added it raises the exception "Access violation at address 00000000 in module 'Test.exe'. Read of address 00000000" at line 1599 in src\plain\ZPlainDbLibDriver.pas (else Result := FdbSetLoginTime_stdcall(Seconds) in function TZDBLIBPLainDriver.dbSetLoginTime(Seconds: Integer): RETCODE).
Case 1 - named pipes:
On my system things work as expected. In the case of a named pipe I get the error message "The connection to the server failed, no proper handle was returned. Insufficient memory, unable to connect for any reason." This is because FreeTDS can't use named pipes and the given host name doesn't translate into a proper server name. If I use a correct server name, things work as expected.
Please make sure to use the current Zeos 7.3 from SVN. If in doubt, include the following subdirectoris of Zeos in your project search path:
  • src\core
  • src\parsesql
  • src\plain
  • src\dbc
  • src\component
Case 2 - servername and port:
I tried that and can connect correctly. See the hints above. By the way - why do you keep disabling UseMetadata?
kenny wrote:I've tried ado with 'Provider=MSOLEDBSQL.1;Data Source=.\sqlexpress;Database=master' and odbc_w with 'Driver={ODBC Driver 17 for SQL Server};Server=.\sqlexpress;Database=master' and they can connect, but when I use the following code they do not retain the session between TZQuery.ExecSql or TZQuery.Open and TzQuery.Close:

Code: Select all

c.Connect();
q.Sql.Add('create table #t (i int)');
q.ExecSql();
q.Sql.Clear();
q.Sql.Add('insert into #t values (0)');
q.ExecSql();
q.Sql.Clear();
q.Sql.Add('select * from #t');
q.Open();
Write(q.RecordCount, #10);
q.Close();
q.Sql.Clear();
q.Sql.Add('drop table #t');
q.ExecSql();
c.Disconnect();
Why do you think, the session is not kept?
kenny
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 07.09.2019, 11:24

Re: Connecting and session reusing

Post by kenny »

In the library path I have "\packages\DelphiXE10.2\Win32\Debug" and in the browsing path I have "\src\component", "\src\core", "\src\dbc", "\src\parsesql" and "\src\plain". I've tried revision 5431, 5877, 5890, 5904, 5906, 5911 and 5913. The SQl Server Browser service was running in every test. Using ".\sqlexpress" in revision 5913 throws "The connection to the server failed, no proper handle was returned. Insufficient memory, unable to connect for any reason.". Using "computername\sqlexpress" hangs out the same way the ip does. Adding timeout still throws "Access violation at address 00000000 in module 'Test.exe'. Read of address 00000000". OleDb still throws "Access violation at address 00762A4A in module 'Test.exe'. Read of address 00000000". Using the default true for UseMetadata does not change the outcome.

The temporary table does not exist after ExecSql or Open and Close which means that each query is using a new session or is resetting the existing session. I've tried Connection Reset and Pooling set to false for ado. I was hoping to try RetainSameConnection set to true for oledb or be able to retain the session using freetds.
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1956
Joined: 17.01.2011, 14:17

Re: Connecting and session reusing

Post by marsupilami »

Hello Kenny,
kenny wrote:In the library path I have "\packages\DelphiXE10.2\Win32\Debug" and in the browsing path I have "\src\component", "\src\core", "\src\dbc", "\src\parsesql" and "\src\plain". I've tried revision 5431, 5877, 5890, 5904, 5906, 5911 and 5913. The SQl Server Browser service was running in every test.
That is good to know.
kenny wrote:Using ".\sqlexpress" in revision 5913 throws "The connection to the server failed, no proper handle was returned. Insufficient memory, unable to connect for any reason.".
This is to be expected. ".\sqlexpress" is not a valid server name for TCP/IP connections.
kenny wrote:Using "computername\sqlexpress" hangs out the same way the ip does.
I finally was able to get the same result - by disablling TCP/IP in the SQL server configuration. Are you sure, your SQL server is configured to accept TCP/IP connections and not only shared memory and / or named pipes? TCP/IP is disabled by default after the installation, I think.
kenny wrote:Adding timeout still throws "Access violation at address 00000000 in module 'Test.exe'. Read of address 00000000".
I corrected this some minutes ago. Please update from SVN. You should now get the same message as you got with ".\sqlexpress".
kenny wrote:OleDb still throws "Access violation at address 00762A4A in module 'Test.exe'. Read of address 00000000". Using the default true for UseMetadata does not change the outcome.
No - it wouldn't. I assume there is a problem in the OleDB driver logic there. It seems that the OleDB driver doesn't return any rows for your SQL:

Code: Select all

create table #t (i int)
insert into #t values (0)
select * from #t
drop table #t
This will have to be reviewed by Egonhugeist as he ist the master of OleDB ;)
kenny wrote:The temporary table does not exist after ExecSql or Open and Close which means that each query is using a new session or is resetting the existing session. I've tried Connection Reset and Pooling set to false for ado. I was hoping to try RetainSameConnection set to true for oledb or be able to retain the session using freetds.
FreeTDS should keep the connection alive. Currently I don't know why this doesn't work with OLEDB. It might have to do with a specialty of the OleDB driver. It executes SQL statements from within an SP on the server. So the following might apply to your temporary table:
CREATE TABLE (Transact-SQL) wrote:A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished.
I found that note on https://docs.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql?view=sql-server-2017.
So you might want to use the SQL server profiler to see if your connection really gets closed or if this has to do with the way the statement is executed. I think about filing a bug report against the OleDB and ADO drivers. Both behave the same in this regard.

Best regards,

Jan
kenny
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 07.09.2019, 11:24

Re: Connecting and session reusing

Post by kenny »

TCP/IP protocol was enabled for all tests. Using "computername\sqlexpress" works for ado and odbc, but not for freetds. The timeout throws "The connection to the server failed, no proper handle was returned. Insufficient memory, unable to connect for any reason.". The tracing confirms that there is no reconnect or session reset.

Tracing result from ado:

Code: Select all

-- network protocol: LPC
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed
--
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,NULL,N'create table #t (i int)
'
select @p1
--
exec sp_unprepare 1
--
declare @p1 int
set @p1=NULL
exec sp_prepexec @p1 output,NULL,N'insert into #t values (0)
'
select @p1
Tracing result from odbc:

Code: Select all

-- network protocol: LPC
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed
--
SELECT CASE DATABASEPROPERTYEX( DB_NAME(), 'Updateability') WHEN 'READ_ONLY' THEN 'Y' ELSE 'N' END
--
SELECT CASE DATABASEPROPERTYEX( DB_NAME(), 'Updateability') WHEN 'READ_ONLY' THEN 'Y' ELSE 'N' END
--
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,NULL,N'create table #t (i int)
'
select @p1
--
exec sp_unprepare 1
--
declare @p1 int
set @p1=NULL
exec sp_prepexec @p1 output,NULL,N'insert into #t values (0)
'
select @p1
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1956
Joined: 17.01.2011, 14:17

Re: Connecting and session reusing

Post by marsupilami »

Hello Kenny,
kenny wrote:TCP/IP protocol was enabled for all tests. Using "computername\sqlexpress" works for ado and odbc, but not for freetds.
Hmm - maybe there is some problem with your network configuration? I can only assume that ADO and OLEDB also could use shared memory and named pipes to connect to a local server. Do ADO and OLEDB work if you disable Named PIpes and Shared Memory and restart your SQL server process?
kenny wrote:The timeout throws "The connection to the server failed, no proper handle was returned. Insufficient memory, unable to connect for any reason.".
This is to be expected. In a timeout case FreeTDS simply doesn't return a connection handle and this error is generated by Zeos.
kenny wrote: The tracing confirms that there is no reconnect or session reset.

Tracing result from ado:

Code: Select all

-- network protocol: LPC
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed
--
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,NULL,N'create table #t (i int)
'
select @p1
--
exec sp_unprepare 1
--
declare @p1 int
set @p1=NULL
exec sp_prepexec @p1 output,NULL,N'insert into #t values (0)
'
select @p1
Tracing result from odbc:

Code: Select all

-- network protocol: LPC
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed
--
SELECT CASE DATABASEPROPERTYEX( DB_NAME(), 'Updateability') WHEN 'READ_ONLY' THEN 'Y' ELSE 'N' END
--
SELECT CASE DATABASEPROPERTYEX( DB_NAME(), 'Updateability') WHEN 'READ_ONLY' THEN 'Y' ELSE 'N' END
--
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,NULL,N'create table #t (i int)
'
select @p1
--
exec sp_unprepare 1
--
declare @p1 int
set @p1=NULL
exec sp_prepexec @p1 output,NULL,N'insert into #t values (0)
'
select @p1
Ok. I will file a bug report for ADO and OLEDB later on.
kenny
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 07.09.2019, 11:24

Re: Connecting and session reusing

Post by kenny »

Ado can connect with disabled Shared Memory and Named Pipes and with stopped SQL Server Browser when "computername\sqlexpress" or "ip,port" is used. Odbc can connect only with disabled Shared Memory and Named Pipes when "computername\sqlexpress" is used and additionally without SQL Server Browser when "ip,port" is used.

After turning off and on the Shared Memory and Named Pipes or after the cumulative updates I had yesterday the issues with freetds is fixed and now I can connect with "computername\sqlexpress" and with ip and port. Oledb still throws the exception.

ExecuteDirect with odbc does not use sp_prepexec, but that is not the case for ado.

Code: Select all

c.Connect();
c.ExecuteDirect('create table #t (i int)');
q.Sql.Clear();
q.Sql.Add('insert into #t values (0)');
q.ExecSql();
q.Sql.Clear();
q.Sql.Add('select * from #t');
q.Open();
Write(q.RecordCount, #10);
q.Close();
q.Sql.Clear();
q.Sql.Add('drop table #t');
q.ExecSql();
c.Disconnect();
Post Reply