Topic: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Здравствуйте.
После начала использования Вашего конструктора, решил научиться Паскалю с нуля (использую Lazarus).
Пишу программу - копию той, что сделал с помощью конструктора. Всё хорошо, но столкнулся с проблемой блокировки базы данных.
Можете подсказать, как мне можно побороть эту самую блокировку?

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Приветствую,


скорей всего забываете освобождать датасет, после того как выполнили запрос, сложно подсказать не видя код работы с базой.

Dmitry.

3 (edited by Serhij 2014-12-15 09:54:37)

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Для подключения используются компоненты: SQLite3Connection1, SQLTransaction1, DataSource1, SQLQuery1 (для получения данных из БД), SQLQuery2 (для записи данных в БД).

Главная форма
Подключение к базе:

procedure TForm1.FormCreate(Sender: TObject);
begin
   sqlite3dyn.SQLiteDefaultLibrary := 'sqlite3.dll';
   SQLite3Connection1.DatabaseName := 'sqlite.db';
   SQLite3Connection1.Connected := True;
   SQLQuery1.Active := True;
end;

вычисляемое поле:

procedure TForm1.SQLQuery1CalcFields(DataSet: TDataSet);
begin
   SQLQuery1.FieldByName('zmist').AsString := ('Кошти боргу з ' + SQLQuery1.FieldByName('borzhnyk').AsString);
end;

кнопка поиска на главной форме:

procedure TForm1.Button5Click(Sender: TObject);
   var
   sqlwhere: string;
begin
   sqlwhere := '';
   if Edit1.Text <> '' then
   sqlwhere := Format('%s and %s', [sqlwhere, 'record_count = "'+Edit1.text+'"']);
   if ZVDateTimePicker1.Checked=True then
   sqlwhere := Format('%s and %s', [sqlwhere, 'strftime("%Y-%m-%d", data_vymoghy) >= "'+FormatDateTime('YYYY-MM-DD', ZVDateTimePicker1.DateTime)+'"']);
   if ZVDateTimePicker2.Checked=True then
   sqlwhere := Format('%s and %s', [sqlwhere, 'strftime("%Y-%m-%d", data_vymoghy) <= "'+FormatDateTime('YYYY-MM-DD', ZVDateTimePicker2.DateTime)+'"']);
   if Edit2.Text <> '' then
   sqlwhere := Format('%s and %s', [sqlwhere, 'borzhnyk LIKE "%'+Edit2.text+'%"']);
   if Edit3.Text <> '' then
   sqlwhere := Format('%s and %s', [sqlwhere, 'kod_borzhnyka = "'+Edit3.text+'"']);

   delete(sqlwhere,2,3);

   if sqlwhere='' then
   begin
   SQLQuery1.Active:=False;
   SQLQuery1.SQL.Text:= 'select * from vymoghy';
   SQLQuery1.Active:=True;
   end else
   begin
   SQLQuery1.Active:=False;
   SQLQuery1.SQL.Text:= 'select * from vymoghy where' + sqlwhere;
   SQLQuery1.Active:=True;
   end;
end;

Форма добавления новой записи
Запись нового значения счётчика в текстовое поле

procedure TForm2.FormShow(Sender: TObject);
   var
   last_record_count: integer;
   record_count: string;
begin
   SQLQuery1.Active:=False;
   SQLQuery1.SQL.Text:= 'SELECT MAX(record_count) FROM vymoghy';
   SQLQuery1.Open;
   Form1.SQLTransaction1.CommitRetaining;
   SQLQuery1.Active:=True;

   last_record_count := SQLQuery1.Fields[0].AsInteger;
   if last_record_count <> 0 then record_count := IntToStr(last_record_count+1) else record_count := IntToStr(1);

   Edit1.text := record_count;
end;

кнопка сохранения в БД:

procedure TForm2.Button1Click(Sender: TObject);
   var
   last_record_count: integer;
   record_count: string;
begin
   SQLQuery1.Active:=False;
   SQLQuery1.SQL.Text:= 'SELECT MAX(record_count) FROM vymoghy';
   SQLQuery1.Active:=True;

   last_record_count := SQLQuery1.Fields[0].AsInteger;
   if last_record_count <> 0 then record_count := IntToStr(last_record_count+1) else record_count := IntToStr(1);

   If Form1.SQLite3Connection1.Connected then
   begin
   SQLQuery1.SQL.Clear;
   SQLQuery1.SQL.Add(Format('insert into vymoghy (record_count, data_vymoghy, borzhnyk, kod_borzhnyka, bank_borzhnyka, mfo_banku, rakhunok, suma, pryznachennja_platezhu) values ("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s")',[record_count, FormatDateTime('YYYY-MM-DD 00:00:00.000', ZVDateTimePicker1.DateTime), Edit2.Text, Edit3.Text, Edit5.Text, Edit4.Text, Edit6.Text, Edit7.Text, Memo1.Text]));
   SQLQuery1.ExecSQL;
   Form1.SQLTransaction1.CommitRetaining;
   Form1.SQLQuery1.Refresh;
   end;

   Form2.Close;
end;

Несколко экземпляров программы открываются, но ни один не может записать в БД. Если оставить один экземпляр, то он уже сможет записать данные.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Похожая проблема в этой теме
http://www.freepascal.ru/forum/viewtopi … amp;t=9798


возможно необходимо добавить SQLQuery.Close;

If Form1.SQLite3Connection1.Connected then
   begin
   SQLQuery.Close; // !!
   SQLQuery1.SQL.Clear;
   SQLQuery1.SQL.Add(Format('insert into vymoghy (record_count, data_vymoghy, borzhnyk, kod_borzhnyka, bank_borzhnyka, mfo_banku, rakhunok, suma, pryznachennja_platezhu) values ("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s")',[record_count, FormatDateTime('YYYY-MM-DD 00:00:00.000', ZVDateTimePicker1.DateTime), Edit2.Text, Edit3.Text, Edit5.Text, Edit4.Text, Edit6.Text, Edit7.Text, Memo1.Text]));
   SQLQuery1.ExecSQL;
   Form1.SQLTransaction1.CommitRetaining;
   Form1.SQLQuery1.Refresh;
   end;
Dmitry.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Код сохранения записи написал так

procedure TForm2.Button1Click(Sender: TObject);
   var
   last_record_count: integer;
   record_count: string;
begin
   SQLQuery1.Close;
   SQLQuery1.SQL.Text:= 'SELECT MAX(record_count) FROM vymoghy';
   SQLQuery1.Open;

   last_record_count := SQLQuery1.Fields[0].AsInteger;
   if last_record_count <> 0 then record_count := IntToStr(last_record_count+1) else record_count := IntToStr(1);

   If Form1.SQLite3Connection1.Connected then
   begin
   SQLQuery1.Close;
   SQLQuery1.SQL.Clear;
   SQLQuery1.SQL.Add(Format('insert into vymoghy (record_count, data_vymoghy, borzhnyk, kod_borzhnyka, bank_borzhnyka, mfo_banku, rakhunok, suma, pryznachennja_platezhu) values ("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s")',[record_count, FormatDateTime('YYYY-MM-DD 00:00:00.000', ZVDateTimePicker1.DateTime), Edit2.Text, Edit3.Text, Edit5.Text, Edit4.Text, Edit6.Text, Edit7.Text, Memo1.Text]));
   Form1.SQLTransaction1.Active := False;
   Form1.SQLTransaction1.Active := True;
   SQLQuery1.ExecSQL;
   Form1.SQLTransaction1.Commit;
   end;

   Form2.Close;
end;

и удалил Form1.SQLQuery1.Refresh; (обновление грида).
Но как теперь обновить грид?
Если добавить Form1.SQLQuery1.Refresh; в событие закрытия формы, то ругается на неактивный датасет, а если активировать Form1.SQLQuery1, то опять ругается на заблокированную базу.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

К сожалению не работал с этими компонентами (использую только TSQLConnection)
не смогу с этим подсказать (

Dmitry.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

А в какой среде Вы работаете,

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Delphi XE

Dmitry.

9 (edited by Serhij 2014-12-15 15:07:13)

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Можно уточнить? А какая версия Delphi XE (7 или более ранняя?) и какой язык (Object Pascal или C++?)?
Извиняюсь за расспросы, но хочу для себя определиться с чего же лучше начать изучение.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Delphi XE3 (Object Pascal)

Dmitry.

11 (edited by Serhij 2014-12-17 11:25:15)

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Здравствуйте.
Сменил компоненты подключения к базе на ZeosDBO, - вроде работает. Но уже другая проблема.
Форма добавления новой записи у меня вызывается как модальная форма

procedure TForm1.Button1Click(Sender: TObject);
begin
   Form2.ShowModal;
end;

Код формы:

unit Unit2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, sqldb, sqlite3conn, FileUtil, ZVDateTimePicker,
  DBZVDateTimePicker, curredit, dbdateedit, RTTICtrls, u_framework_components,
  u_extformatedits, U_ExtNumEdits, u_extsearchedit, U_ExtDBNavigator,
  JLabeledCurrencyEdit, JLabeledFloatEdit, JLabeledIntegerEdit, JDBLabeledEdit,
  jdblabeledintegeredit, ZDataset, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Spin, EditBtn, DbCtrls;

type

  { TForm2 }

  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    Edit7: TEdit;
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Memo1: TMemo;
    ZQuery2: TZQuery;
    ZVDateTimePicker1: TZVDateTimePicker;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Edit3KeyPress(Sender: TObject; var Key: char);
    procedure Edit4KeyPress(Sender: TObject; var Key: char);
    procedure Edit6KeyPress(Sender: TObject; var Key: char);
    procedure Edit7KeyPress(Sender: TObject; var Key: char);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormShow(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.lfm}

uses Unit1;

{ TForm2 }

procedure TForm2.Button1Click(Sender: TObject);
   var
   last_record_count: integer;
   record_count: string;
begin
   ZQuery2.Close;
   ZQuery2.SQL.Clear;
   ZQuery2.SQL.Text:= 'SELECT MAX(record_count) FROM vymoghy';
   ZQuery2.Open;

   last_record_count := ZQuery2.Fields[0].AsInteger;
   if last_record_count <> 0 then record_count := IntToStr(last_record_count+1) else record_count := IntToStr(1);

   If Form1.ZConnection1.Connected then
   begin
   ZQuery2.Close;
   ZQuery2.SQL.Clear;
   ZQuery2.SQL.Add(Format('insert into vymoghy (record_count, data_vymoghy, borzhnyk, kod_borzhnyka, bank_borzhnyka, mfo_banku, rakhunok, suma, pryznachennja_platezhu) values ("%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s", "%s")',[record_count, FormatDateTime('YYYY-MM-DD 00:00:00.000', ZVDateTimePicker1.DateTime), Edit2.Text, Edit3.Text, Edit5.Text, Edit4.Text, Edit6.Text, StringReplace(Edit7.Text, ',', '.', [rfReplaceAll, rfIgnoreCase]), Memo1.Text]));
   ZQuery2.ExecSQL;
   end;

   Form2.Close;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
   Form2.Close;
end;

procedure TForm2.Edit3KeyPress(Sender: TObject; var Key: char);
begin
   case Key of
   '0'..'9': ;
   #8: ;  // the backspace key
   else Key:= #0;
   end;
end;

procedure TForm2.Edit4KeyPress(Sender: TObject; var Key: char);
begin
   case Key of
   '0'..'9': ;
   #8, #9: ;  // the backspace & tab key
   else Key:= #0;
   end;
end;

procedure TForm2.Edit6KeyPress(Sender: TObject; var Key: char);
begin
   case Key of
   '0'..'9': ;
   #8, #9: ;  // the backspace & tab key
   else Key:= #0;
   end;
end;

procedure TForm2.Edit7KeyPress(Sender: TObject; var Key: char);
begin
   case Key of
   '0'..'9': ;
   ',', #8, #9: ;  // the backspace & tab key
   else Key:= #0;
   end;
end;

procedure TForm2.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
   Form1.ZQuery1.Close;
   Form1.ZQuery1.SQL.Clear;
   Form1.ZQuery1.SQL.Text:='select * from vymoghy';
   Form1.ZQuery1.Open;
end;

procedure TForm2.FormShow(Sender: TObject);
   var
   last_record_count: integer;
   record_count: string;
begin
   ZVDateTimePicker1.DateTime:=now;
   ZQuery2.Close;
   ZQuery2.SQL.Clear;
   ZQuery2.SQL.Text:= 'SELECT MAX(record_count) FROM vymoghy';
   ZQuery2.Open;

   last_record_count := ZQuery2.Fields[0].AsInteger;
   if last_record_count <> 0 then record_count := IntToStr(last_record_count+1) else record_count := IntToStr(1);

   Edit1.text := record_count;
end;

{ TForm2 }

end.

После закрытия этой формы, данные компонентов (Edit, Memo,...) не очищаются. Когда снова открываешь форму, для добавления новой аписи, в компонентах сидят предыдущие значения.
До смены компонентов подключения такого не наблюдал.
Подскажите, куда копать.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Они сами не будут очищаться, вам необходимо это сделать самостоятельно, например в событии формы Form2.FormShow

procedure TForm2.FormShow(Sender: TObject);
   var
   last_record_count: integer;
   record_count: string;
begin

   Edit1.Clear; //!! 
   Memo1.Clear;


   ZVDateTimePicker1.DateTime:=now;
   ZQuery2.Close;
   ZQuery2.SQL.Clear;
   ZQuery2.SQL.Text:= 'SELECT MAX(record_count) FROM vymoghy';
   ZQuery2.Open;

   last_record_count := ZQuery2.Fields[0].AsInteger;
   if last_record_count <> 0 then record_count := IntToStr(last_record_count+1) else record_count := IntToStr(1);

   Edit1.text := record_count;
end;
Dmitry.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Спасибо.
Ещё вопрос.
У SQLite есть параметр "busytimeout=<milliseconds>".
А как его использовать?

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Должно быть так, но не проверял

SQLConnection.ExecuteDirect('PRAGMA busy_timeout=1000;'); // 1000ms

SQLConnection это TSQLConnection;


ExecuteDirect
Executes an SQL command that does not include parameters.

Call ExecuteDirect to execute a single parameterless command on the server without the overhead of using an SQL dataset or preparing the statement before it executes. If the statement ordinarily returns a cursor, ExecuteDirect executes the statement, but does not return a cursor.

Dmitry.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Добрый вечер.
Столкнулся с проблемой регистронезависимого поиска по базе SQLite3.
Запрос вида: SELECT * FROM vymoghy WHERE borzhnyk LIKE "%'+Edit1.Text+'%" по тексту "ася" находит: "Вася", "Мася", но не "Ася".
Уже пробовал разные компоненты для подключения к БД, upper+AnsiUpperCase, lower+AnsiLowerCase, collate.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

SQLite не умеет производить регистронезависимый поиск не английского текста, возьмите файл sqlite.dll из дистрибутива My Visual Database, он модифицирован для решения этой проблемы.

Dmitry.

Re: Lazarus SQLite и одновременный доступ нескольких пользователей к базе

Спасибо. Работает.