TZRowAccessor.GetPAnsiChar error

In this forum you may discuss all issues concerning the Lazarus IDE and Freepascal (both running on Windows or Linux).

Moderators: gto, cipto_kh, EgonHugeist

lanmi70
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 10.06.2020, 15:38

TZRowAccessor.GetPAnsiChar error

Post by lanmi70 »

SQLite, Zeos 7.2.6 stable, Lazarus 2.0.8, everything installed two days ago over Lazarus Online package manager
I have used this database table configuration and function with 7.1.4 version and Lazarus 1.6.4 for years, do have some freezing issues from time to time, never detected reason.
With latest installation this exception almosst immidiately appear in debug mode, poiting on this line:

Code: Select all

function TZRowAccessor.GetPAnsiChar(ColumnIndex: Integer; out IsNull: Boolean;
  out Len: NativeUInt): PAnsiChar;
var Data: PPointer;
label Set_Results, SetEmpty;
begin
  {$R-}
  if FBuffer.Columns[FColumnOffsets[ColumnIndex{$IFNDEF GENERIC_INDEX} - 1{$ENDIF}]] = bIsNotNull then begin
    IsNull := False; 
SQLite table:
CREATE TABLE [Last_data] (
[datetime] REAL NOT NULL,
[username] VARCHAR(50) NOT NULL,
[device_id] INT NOT NULL,
[pin] TINYINT,
[byte2] TINYINT,
[byte3] TINYINT,
[byte4] TINYINT,
[byte5] TINYINT,
[val] FLOAT DEFAULT 0,
[is_received_from_serial] BOOLEAN DEFAULT 0,
[is_received_from_ip] BOOLEAN DEFAULT 0,
[IP] VARCHAR NOT NULL DEFAULT 'N/A',
[sec_between] INTEGER NOT NULL DEFAULT (-1),
[io] TINYINT DEFAULT 0,
[object_name] VARCHAR,
[alarm] BOOLEAN DEFAULT false);
code

Code: Select all

  function Insert_Update_Last_data(conn: TZConnection; ArduinoObjectName: string; datetime: extended; username: string; device_id: integer; VAR_OUT: TIOType;
  pin, byte2, byte3, byte4, byte5: byte; val: double; IP: string; sec_between: integer): string;
var
  query: TZQuery;
  io: byte;
  recordexists: boolean;
begin
   query := TZQuery.Create(nil);
  query.connection := Conn;
  conn.AutoCommit := True;
  recordexists := False;
  query.SQL.Add('Select * from Last_data WHERE object_name=' + QuotedStr(ArduinoObjectName));

  try
    query.Open;
    if query.RecordCount > 0 then
      recordexists := True;
  except
    on e: Exception do
    begin
      Result := 'Insert_Update_Last_data - ' + e.Message;
      FreeAndNil(query);
      exit;
    end;
  end;

  if query.Active then
    query.Close;
  query.SQL.Clear;

  if not recordexists then
  begin//insert if not exists
    query.SQL.Add('INSERT INTO Last_data (datetime,username,device_id,pin,byte2,byte3,byte4,byte5,val,io,IP,sec_between,object_name)');
    query.SQL.Add('values(:datetime,:username,:device_id,:pin,:byte2,:byte3,:byte4,:byte5,:val,:io,:IP,:sec_between,:object_name)');
    query.Params.ParamByName('datetime').AsFloat := datetime;
    query.Params.ParamByName('username').AsString := username;
    query.Params.ParamByName('device_id').AsInteger := device_id;
    query.Params.ParamByName('pin').AsInteger := pin;
    query.Params.ParamByName('byte2').AsInteger := byte2;
    query.Params.ParamByName('byte3').AsInteger := byte3;
    query.Params.ParamByName('byte4').AsInteger := byte4;
    query.Params.ParamByName('byte5').AsInteger := byte5;
    query.Params.ParamByName('val').AsFloat := val;
    query.Params.ParamByName('io').AsInteger := io;
    query.Params.ParamByName('IP').AsString := IP;
    query.Params.ParamByName('sec_between').AsInteger := sec_between;
    query.Params.ParamByName('object_name').AsString := ArduinoObjectName;
  end
  else
  begin
  
      query.SQL.Add('Update Last_data SET datetime=:datetime,username=:username,device_id=:device_id,pin=:pin,byte2=:byte2,byte3=:byte3,byte4=:byte4,byte5=:byte5,val=:val,IP=:IP,sec_between=:sec_between,io=:io,object_name=:object_name ');
      query.SQL.Add('WHERE device_id=' + IntToStr(device_id) + ' and username=' + quotedstr(username) + ' and pin=' + IntToStr(pin) + ' and byte4=' +
       IntToStr(byte4) + ' and object_name=' + QuotedStr(ArduinoObjectName));
   
    query.Params.ParamByName('datetime').AsFloat := datetime;
    query.Params.ParamByName('username').AsString := username;
    query.Params.ParamByName('device_id').AsInteger := device_id;
    query.Params.ParamByName('pin').AsInteger := pin;
    query.Params.ParamByName('byte2').AsInteger := byte2;
    query.Params.ParamByName('byte3').AsInteger := byte3;
    query.Params.ParamByName('byte4').AsInteger := byte4;
    query.Params.ParamByName('byte5').AsInteger := byte5;
    query.Params.ParamByName('val').AsFloat := val;
     query.Params.ParamByName('io').AsInteger := io;
    query.Params.ParamByName('IP').AsString := IP;
    query.Params.ParamByName('sec_between').AsInteger := sec_between;
    query.Params.ParamByName('object_name').AsString := ArduinoObjectName;
  end;

  try
    try
       query.ExecSQL;
      Result := 'ok';
   
    except
      on e: Exception do
      begin
        Result := 'Insert_Update_Last_data_2 - ' + e.Message;
     
      end;
    end;
  finally
    FreeAndNil(query);
  end;
end;  
I do have tried with VARCHAR(100) in the SQLite table, query.Params.ParamByName('byte5').AsSmallInt for Byte, but this is original code working for years.

What in this configuration triggers the error and what params type will be adequate for Sqlite table configuration?

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

Re: TZRowAccessor.GetPAnsiChar error

Post by marsupilami »

Hello,

I created a ticket for this issue in the bug tracker:
https://sourceforge.net/p/zeoslib/tickets/431/

Which params are used when you call Insert_Update_Last_data to generate the error?

Best regards,

Jan
lanmi70
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 10.06.2020, 15:38

Re: TZRowAccessor.GetPAnsiChar error

Post by lanmi70 »

Hi Jan,
I wrote in topic complete code with included parameters.
The interesting thing that sometimes is triggered error for boolean and integer, but error, as described, is almost a rule.

Thanks for reply
lanmi70
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 10.06.2020, 15:38

Re: TZRowAccessor.GetPAnsiChar error

Post by lanmi70 »

I forgot to add that that function Insert_Update_Last_data has intense write - read, especially on application startup, depends on connected Arduino hardware devices and data values sent to the application.

Regards,
Milan
miab3
Zeos Test Team
Zeos Test Team
Posts: 1310
Joined: 11.05.2012, 12:32
Location: Poland

Re: TZRowAccessor.GetPAnsiChar error

Post by miab3 »

@lanami70, @marsupilami

In Delphi 10.3.3(Zeos 7.3 r6633) this table throws an error when attempting to open:
"Range check error"

SQLite Expert Personal opens it without any problems.

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

Re: TZRowAccessor.GetPAnsiChar error

Post by marsupilami »

Hello Milan, hello Michal,

I tried to test this. For me everything works.

@Michal: do you have an insert statement to use with this table? My data doesn't work.

@Milan: which values are given to Insert_Update_Last_data when it fails? Can you test if that happens on the current SVN version of Zeos 7.2 too?

Best regards,

Jan
miab3
Zeos Test Team
Zeos Test Team
Posts: 1310
Joined: 11.05.2012, 12:32
Location: Poland

Re: TZRowAccessor.GetPAnsiChar error

Post by miab3 »

Hi marsupilami;

Enter data through SQLite Expert.
The table with data gives an error when trying to open in z ZEOS 7.3r6633 D10.3.3.

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

Re: TZRowAccessor.GetPAnsiChar error

Post by marsupilami »

Hello Michal,

getting data into the database isn't my problem. Currently I use the following SQL statement:

Code: Select all

insert into [TestSF431] values (0.5, 'testuser', 5, 34, 2, 3, 4, 5, 0, false, true, 'N/A', 10, 0, 'objectname', false);
My test case looks like this:

Code: Select all

procedure ZTestCompSQLiteBugReport.TestSF431;
var
  Query: TZQuery;
begin
  Query := CreateQuery;
  try
    Query.SQL.Text := 'select * from TestSF431';
    Query.Open;
    Query.FetchAll;
    Query.Close;
  finally
    FreeAndNil(Query);
  end;
end;
This test doesn't raise any errors. So I don't know where to look for the problem.
miab3
Zeos Test Team
Zeos Test Team
Posts: 1310
Joined: 11.05.2012, 12:32
Location: Poland

Re: TZRowAccessor.GetPAnsiChar error

Post by miab3 »

Hi Jan,

Try to open this table, with data.

Michal
You do not have the required permissions to view the files attached to this post.
miab3
Zeos Test Team
Zeos Test Team
Posts: 1310
Joined: 11.05.2012, 12:32
Location: Poland

Re: TZRowAccessor.GetPAnsiChar error

Post by miab3 »

Hi Jan,

Something cleared up.
SQLite allows 8-bytes integer in all fields of type *INT* but ZEOS 7.3 for TINYINT limits to one byte.

And a little bit changed in SQLite types since ZEOS 7.1.4.
Maybe this is the cause of the error that lanmi70 gets?

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

Re: TZRowAccessor.GetPAnsiChar error

Post by marsupilami »

miab3 wrote: 15.06.2020, 06:32 SQLite allows 8-bytes integer in all fields of type *INT* but ZEOS 7.3 for TINYINT limits to one byte.
*sigh* - if we supported everything that sqlite allows, we had to make all columns variant columns. sqlite also allows storing strings in columns with interger affinity.
miab3 wrote: 15.06.2020, 06:32And a little bit changed in SQLite types since ZEOS 7.1.4.
Maybe this is the cause of the error that lanmi70 gets?
Yes - it very much can be the source of the problem. I didn't look into determining data types for sqlite columns yet, so I don't know what we do there.
User avatar
aehimself
Zeos Dev Team
Zeos Dev Team
Posts: 796
Joined: 18.11.2018, 17:37
Location: Hungary

Re: TZRowAccessor.GetPAnsiChar error

Post by aehimself »

Sorry for offtopic, but...
marsupilami wrote: 15.06.2020, 09:21sqlite also allows storing strings in columns with interger affinity.
Wtf... really? Good thing they have different data types, if it doesn't matter at the end. It's like JavaScript, define a variable and it can be anything what you want :)
Delphi 12.2, Zeos 8 from latest GIT snapshot
Using:
- MySQL server 8.0.18; libmysql.dll 8.0.40 x64 5.7.19 x68, libmariadb.dll 3.3.11
- Oracle server 11.2.0, 12.1.0, 19.0.0; oci.dll 21.15
- MSSQL 2012, 2019; sybdb.dll FreeTDS_3102
- SQLite 3.47
marsupilami
Platinum Boarder
Platinum Boarder
Posts: 1956
Joined: 17.01.2011, 14:17

Re: TZRowAccessor.GetPAnsiChar error

Post by marsupilami »

aehimself wrote: 15.06.2020, 12:30
marsupilami wrote: 15.06.2020, 09:21sqlite also allows storing strings in columns with interger affinity.
Wtf... really? Good thing they have different data types, if it doesn't matter at the end. It's like JavaScript, define a variable and it can be anything what you want :)
Yes - see here as "Manifest typing" and here, where they describe their type system. Alternatives for embeddable databases that have a strict typing system are Firebird and Interbase. If portability is not an issue, one might also use the Jet Engine supported databases (Access, DBase, Paradox, ...). But the SQL syntax of the jet engine is a horrible thing in itself.

Anyway - back to the topic - if we follow through with this, and want to fully support what sqlite can throw at us, we need to extend Zeos to support Variant columns and the sqlite driver would be using them.
Any other decisions will always leave us in a place where we will raise errors on a perfectly valid sqlite use case.
User avatar
EgonHugeist
Zeos Project Manager
Zeos Project Manager
Posts: 1936
Joined: 31.03.2011, 22:38

Re: TZRowAccessor.GetPAnsiChar error

Post by EgonHugeist »

Hi,
miab3 wrote: 15.06.2020, 06:32 SQLite allows 8-bytes integer in all fields of type *INT* but ZEOS 7.3 for TINYINT limits to one byte.
That's not correct. "Tinyint" as a ShortInt range for all servers except Sybase/MSSQL, "Tinyint unsigned" as a byte-range. That's propably the issue lanmi70 is running in, since i added the Fields with exact ranges for FPC too. Note FPC currently misses the ftByte, ftShortInt, ftSingle types so we'd improved the Fields by our self.

@lanmi70

alter the tinyint columns using the "unsigned" keyword, update to latest zeos and report your findings.
Best regards, Michael

You want to help? http://zeoslib.sourceforge.net/viewtopic.php?f=4&t=3671
You found a (possible) bug? Use the new bugtracker dude! http://sourceforge.net/p/zeoslib/tickets/

Image
lanmi70
Fresh Boarder
Fresh Boarder
Posts: 5
Joined: 10.06.2020, 15:38

Re: TZRowAccessor.GetPAnsiChar error

Post by lanmi70 »

Hi,
Sorry for this late reply, I never received notification on this topic.
Today I've updated Zeos lib over Lazarus official repo to 7.2.8 version.

Now I get "range check error" for TINYINT fields with values greater than 127, since I store byte values in this field (0..255)

A field defined as UNSIGNED TINYINT in SQLite DB seems to solve this issue, but I have to change all fields in DB defined as tinyint.
[byte3] TINYINT,
[byte4] TINYINT,
[byte5] [UNSIGNED TINYINT],
[val] FLOAT DEFAULT 0,

Is that the only way to solve this(redesign DB)?

Thank you very much to all ZEOS team

Edit:

It seems that byte5 := Byte(query.FieldByName('byte5').AsInteger); solve the range error issue without changing DB table fields definition to UNSIGNED TINYINT. Will do more testing in debug mode
Post Reply