Delphi android SQLite Транзакции

Обсуждение возникших проблем, предложений и ошибок UniDAC компонентов
Закрыто
pssolo
Сообщения: 10
Зарегистрирован: Ср 05 авг 2015 11:57

Delphi android SQLite Транзакции

Сообщение pssolo » Ср 05 авг 2015 12:14

Добрый день

Код: Выделить всё

dbConnect.StartTransaction;
try
          updateRows(); //процедура в которой происходит update таблицы
          Q1.SQL.text:='UPDATE table1 SET field1=1';
          Q1.ExecSQL;
          Q2.SQL.text:='INSERT INTO table2 (field1)VALUES(1)'
          Q2.ExecSQL;
          dbConnect.Commit;
except
          dbConnect.Rollback;
end;

end.
При попытке выполнить Q1 происходит исключительная ситуация unable to open database.
Список Компонентов
TUniqConnection - ничего не настраивал, просто прописал путь к БД
TUniQuery - Создается в runtime, указал только Connection

Причем если я закоменчу процедуру то все работает.

В самой процедуре происходит update в Цикле, Ошибка происходит когда цикл делает второй проход.

MaximG
Devart Team
Сообщения: 114
Зарегистрирован: Пн 06 июл 2015 12:51

Re: Delphi android SQLite Транзакции

Сообщение MaximG » Чт 06 авг 2015 13:14

К сожалению нам не удалось воспроизвести данную проблему по приведенному Вами фрагменту кода. Пожалуйста, составьте и вышлите нам полный пример, демонстрирующий проблему, включающий тестовую БД.

pssolo
Сообщения: 10
Зарегистрирован: Ср 05 авг 2015 11:57

Re: Delphi android SQLite Транзакции

Сообщение pssolo » Вс 09 авг 2015 06:50

Пример:

Код: Выделить всё

function TDM_WORK_BASE.saveTable(orderId: Integer;
  table: TVirtualTable): Boolean;
var
  q:TUniQuery;
  lastId:Integer;
  I: Integer;
begin
  try
    DM_BASE.set_locale;
    Result:=true;
    q:=TUniQuery.Create(nil);
    q.Connection:=DM_BASE.dbConnect;
    table.First;
    q.SQL.Clear;
    for I := 1 to table.RecordCount do
      begin
        if table.FieldByName('old').AsInteger=1 then
          begin
            table.Next;
            Continue;
          end
        else;
        q.SQL.Add('INSERT INTO t_order (dt,orderid,itemid,note,printerid,complex,price,sum,quantity,servicesum,discountsum,printed,measure_id,salesum)VALUES');
        if getBase='mysql' then
          q.SQL.Add('(NOW()')
        else
          begin
            q.SQL.Add('(&now');
            q.MacroByName('now').AsString:=FormatDateTime('yyyy-mm-dd hh:mm:ss', now);
          end;
        q.SQL.Add(',&orderid,&itemid,&note,&printerid,&complex,&price,&sum,&quantity,&servicesum,&discountsum,&printed,&measure_id,&salesum);');
        q.MacroByName('orderid').AsInteger:=orderId;
        q.MacroByName('itemid').AsString:=table.FieldByName('itemid').AsString;
        q.MacroByName('note').AsString:=table.FieldByName('note').AsString;
        q.MacroByName('printerid').AsString:=table.FieldByName('divid').AsString;
        q.MacroByName('complex').AsString:=table.FieldByName('complex').AsString;
        q.MacroByName('price').AsString:=table.FieldByName('price').AsString;
        q.MacroByName('sum').AsString:=table.FieldByName('summa').AsString;
        q.MacroByName('quantity').AsString:=table.FieldByName('quantity').AsString;
        q.MacroByName('servicesum').AsString:=table.FieldByName('service_sum').AsString;
        q.MacroByName('discountsum').AsString:=table.FieldByName('discount_sum').AsString;
        q.MacroByName('printed').AsInteger:=0;
        q.MacroByName('measure_id').AsInteger:=0;
        q.MacroByName('salesum').AsString:=table.FieldByName('sale_sum').AsString;
        table.Next;
      end;
      try
        if q.SQL.Text.Length>0 then
          q.ExecSQL
        else;
      Except on E:Exception do
        begin
          ShowMessage('INSIDE:Ошибка при сохранении таблицы чека. '+e.Message+' Строка '+i.ToString);
          result:=false;
          q.Free;
          exit;
        end;
      end;
  finally
    q.Free;
  end;
end;

function TDM_WORK_BASE.saveOrder(var header: THeader;
  table: TVirtualTable): Boolean;
var
  q:TUniQuery;
  lastId:Integer;
begin
  try
    DM_BASE.set_locale;
    Result:=false;
    q:=TUniQuery.Create(nil);
    q.Connection:=DM_BASE.dbConnect;
    q.SQL.Clear;
    q.SQL.Add('INSERT INTO d_order (idout,creationdt,t_certificate_id,parentid,changeid,employeeid,clientid,'+
    'objectid,discountid,servicepercent,servicesum,discountpercent,discountsum,printed,closed,guestcount,totalsum,idautomated_point,wpid,note,idlink_online) VALUES ');
    q.SQL.Add('(&idout,');
    if getBase='mysql' then
      q.SQL.Add('NOW()')
    else
      begin
        q.SQL.Add('&now');
        q.MacroByName('now').AsString:=FormatDateTime('yyyy-mm-dd hh:mm:ss', now);
      end;
    q.SQL.Add(',&t_certificate_id,&parentid,&changeid,&employeeid,&clientid,&objectid,&discountid,&servicepercent,&servicesum,');
    q.SQL.Add('&discountpercent,&discountsum,&printed,&closed,&guestcount,&totalsum,&idautomated_point,&wpid,&note,NULL)');
    q.MacroByName('idout').AsString:=DM_BASE.getOrderIDOUT;
    q.MacroByName('t_certificate_id').AsInteger:=0;
    q.MacroByName('parentid').AsInteger:=header.parent_id;
    q.MacroByName('changeid').AsInteger:=DM_BASE.get_change_id;
    q.MacroByName('employeeid').AsInteger:=S_EMPOYEE_ID;
    q.MacroByName('clientid').AsInteger:=header.client_id;
    q.MacroByName('objectid').AsInteger:=header.object_id;
    q.MacroByName('discountid').AsInteger:=header.discount_id;
    q.MacroByName('servicepercent').AsInteger:=header.service_percent;
    q.MacroByName('servicesum').AsFloat:=header.service_sum;
    q.MacroByName('discountpercent').AsInteger:=header.discount_percent;
    q.MacroByName('discountsum').AsFloat:=header.discount_sum;
    q.MacroByName('printed').AsInteger:=header.printed;
    q.MacroByName('closed').AsInteger:=header.closed;
    q.MacroByName('guestcount').AsInteger:=header.guest;
    q.MacroByName('totalsum').AsFloat:=header.total;
    q.MacroByName('idautomated_point').AsInteger:=G_IDAP;
    q.MacroByName('wpid').AsInteger:=G_WPID;
    q.MacroByName('note').AsString:=header.note;
    try
      q.ExecSQL;
      if getBase='mysql' then
        q.SQL.Text:='SELECT LAST_INSERT_ID() as last_id'
      else
        q.SQL.Text:='SELECT last_insert_rowid() as last_id';
      q.Open;
      lastId:=q.FieldByName('last_id').AsInteger;
      try
        if saveTable(lastId,table) then
          begin
            header.order_id:=lastId;
            result:=True;
          end
        else
          begin
            DM_BASE._ROLLBACK;
          end;
      except on E:Exception do
        begin
          DM_BASE._ROLLBACK;
          ShowMessage('OUTSIDE:Ошибка при сохранении таблицы чека. '+e.Message);
        end;
      end;
    except on E:Exception do
      begin
        DM_BASE._ROLLBACK;
        ShowMessage('Ошибка при сохранении шапки чека. '+e.Message);
      end;
    end;
  finally
    q.Free;
  end;
end;

function pay:Boolean;
begin
          DM_BASE._START_TRANSACTION; // Старт транзакции от TuniConnection.startTran...
          if DM_WORK_BASE.saveOrder(head,payVirtualTable) then //Сохранение данных в этой функции происходит только INSERT данных, тут все ок
            begin
              if not DM_WORK_BASE.payOrder(head) then  // вся загвоздка в этой функции
                begin
                  DM_BASE._ROLLBACK;
                  Layout1.Enabled:=true;
                  exit;
                end;
              DM_BASE._COMMIT;
              self.Close;
              FM_SALES.reInitSale;
              FM_SALES.openSale(0);
              Layout1.Enabled:=true;
            end
          else;
end;

function TDM_WORK_BASE.payOrder(Header: THeader): Boolean;
var
  q:TUniQuery;
begin
  try
    Result:=False;
    q:=TUniQuery.Create(nil);
    q.Connection:=DM_BASE.dbConnect;
    q.SQL.Clear;
    q.SQL.Add('UPDATE d_order SET');
    if getBase='mysql' then
      q.SQL.Add('dtclose=NOW(),')
    else
      begin
        q.SQL.Add('dtclose=&now,');
        q.MacroByName('now').AsString:=FormatDateTime('yyyy-mm-dd hh:mm:ss', now);
      end;
    q.SQL.Add('paymentid=&paymentid,');
    q.SQL.Add('sumfromclient=&sumfromclient,');
    q.SQL.Add('emp_pay=&emp_pay,');
    q.SQL.Add('closed=1');
    q.SQL.Add('Where id=&id');
    q.MacroByName('paymentid').AsInteger:=Header.payId;
    q.MacroByName('sumfromclient').AsFloat:=Header.clientSum;
    q.MacroByName('emp_pay').AsInteger:=S_EMPOYEE_ID;
    q.MacroByName('id').AsInteger:=Header.order_id;
    try
      q.Execute;
      Result:=true;
    except on E:Exception do
      begin
        Result:=false;
        q.Free;
        ShowMessage(e.Message);
        exit;
      end;
    end;
  finally
    q.Free;
  end;
end;
Когда компилю под Win32 все хорошо работает и нет ошибок.
Но когда компилю под андоид он выдает сообщение мол база не доступна,
методом тыка догадался закоментить транзации, и прога заработала.
Причем заметил что ломается только когда в теле транзакции выполняются update запросы, на insert он такую ошибку не дает.
Может это из за того что ReadCommited стоит и он не может проапдейтить запись которая еще по факту не в базе?

P.S. Вспомнил, еще была проблема в функции SaveTable. Там в цикле проиходит insert записей, так вот, был изначально так что execSQL был в теле функции и происходила такая же ошибка Enable to open database file, после того как начинала сохранятся запись под номером 2, т.е. если одна запись и цикл выполняется один раз, то мир-жвачка, а если больше одного то ошибка.
Потом пока лазил тут по ветками форума (англ версия), наткнулся на пример, вначале сформируй sql запрос а потом выполняй. Ну и заработало в итоге.

Тестовую базу не могу выслать так как просто не скомпилите, проект, заморочек дольше будет, вы лучше пример может организуете какой, чтобы я у себя скомпилить мог и глянуть как правильно делать?

MaximG
Devart Team
Сообщения: 114
Зарегистрирован: Пн 06 июл 2015 12:51

Re: Delphi android SQLite Транзакции

Сообщение MaximG » Ср 12 авг 2015 08:51

Для исследования проблемы мы создали небольшой тестовый проект, включающий в себя вставку нескольких записей в таблицу с последующим обновлением этих записей с использованием транзакции. Наш тестовый проект успешно работает на Android. Укажите адрес своей электронной почты, на который мы могли бы выслать Вам этот проект. После этого попробуйте изменить его так, чтобы он приводил к ошибке « unable to open database» и прислать нам.

pssolo
Сообщения: 10
Зарегистрирован: Ср 05 авг 2015 11:57

Re: Delphi android SQLite Транзакции

Сообщение pssolo » Ср 12 авг 2015 12:57

pssolo*bk*ru

pssolo
Сообщения: 10
Зарегистрирован: Ср 05 авг 2015 11:57

Re: Delphi android SQLite Транзакции

Сообщение pssolo » Ср 19 авг 2015 09:31

Во вложении проект с эмуляцией ошибки.
Компилировать под android.
Вложения
transExample2.rar
(75.98 КБ) 205 скачиваний

MaximG
Devart Team
Сообщения: 114
Зарегистрирован: Пн 06 июл 2015 12:51

Re: Delphi android SQLite Транзакции

Сообщение MaximG » Ср 26 авг 2015 10:44

Спасибо за Вашу информацию. Мы воспроизвели проблему и исправили данную ошибку. Исправление войдет в ближайшую сборку UniDAC.

Закрыто