git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.jvcl@19 7f62d464-2af8-f54e-996c-e91b33f51cbe
1372 lines
38 KiB
ObjectPascal
1372 lines
38 KiB
ObjectPascal
|
|
|
|
{-----------------------------------------------------------------------------
|
|
The contents of this file are subject to the Mozilla Public License
|
|
Version 1.1 (the "License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
http://www.mozilla.org/MPL/MPL-1.1.html
|
|
|
|
Software distributed under the License is distributed on an "AS IS" basis,
|
|
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
|
|
the specific language governing rights and limitations under the License.
|
|
|
|
The Original Code is: JvXMLDatabase.PAS, released on 2003-06-22.
|
|
|
|
The Initial Developer of the Original Code is S?stien Buysse [sbuysse att buypin dott com]
|
|
Portions created by S?stien Buysse are Copyright (C) 2003 S?stien Buysse.
|
|
All Rights Reserved.
|
|
|
|
Contributor(s):
|
|
|
|
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
|
|
located at http://jvcl.delphi-jedi.org
|
|
|
|
Known Issues:
|
|
-You have to place the columns you test in the where clause in the select clause too
|
|
-Where conditions *MUST* be enclosed between parenthesis as ... WHERE (Col = 5) AND (Col2 < Col3) ...
|
|
-Update statements are limited to simple operations like ... SET Col1 = Col1 + 1, Col2 = 4 ...
|
|
-----------------------------------------------------------------------------}
|
|
// $Id: JvXmlDatabase.pas 12461 2009-08-14 17:21:33Z obones $
|
|
|
|
unit JvXmlDatabase;
|
|
|
|
{$I jvcl.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFDEF UNITVERSIONING}
|
|
JclUnitVersioning,
|
|
{$ENDIF UNITVERSIONING}
|
|
SysUtils, Classes, Contnrs, Math, DateUtils,
|
|
JvTypes, JvComponentBase, JvSimpleXml;
|
|
|
|
type
|
|
TJvXMLDatabase = class;
|
|
TJvXMLQuery = class;
|
|
TJvXMLQueryParser = class;
|
|
TJvXMLDatabaseException = class(EJVCLException);
|
|
|
|
TJvXMLTable = class(TObject)
|
|
public
|
|
XML: TJvSimpleXML;
|
|
Locked: Boolean;
|
|
FileName: string;
|
|
end;
|
|
|
|
TJvXMLQueryTable = class(TObject)
|
|
public
|
|
Name: string;
|
|
Alias: string;
|
|
constructor Create(const AValue: string);
|
|
end;
|
|
|
|
TJvXMLQueryColumn = class(TObject)
|
|
public
|
|
Name: string;
|
|
Table: string;
|
|
constructor Create(const AValue: string);
|
|
end;
|
|
|
|
TJvXMLOrderConvertion = (ocNone, ocDate, ocInteger, ocFloat);
|
|
|
|
TJvXMLQueryOrder = class(TObject)
|
|
public
|
|
Column: string;
|
|
Ascending: Boolean;
|
|
Convertion: TJvXMLOrderConvertion;
|
|
constructor Create(const AValue: string);
|
|
end;
|
|
|
|
TJvXMLSQLOperator = (opEquals, opGreater, opSmaller, opGreaterEquals,
|
|
opSmallerEquals, opLike, opNot, opOr, opAnd, opXor, opLeftParenthesis,
|
|
opRightParenthesis, opConstant, opColumn, opNull, opNone);
|
|
|
|
TJvXMLQueryCondition = class(TObject)
|
|
public
|
|
Condition: string;
|
|
SQLOperator: TJvXMLSQLOperator;
|
|
constructor Create(AOperator: TJvXMLSQLOperator; const ACondition: string = '');
|
|
end;
|
|
|
|
TJvXMLSetKind = (skConstant, skColumn);
|
|
TJvXMLSetOperator = (soNone, soAdd, soMultiply, soDivide, soSubstract);
|
|
|
|
TJvXMLQueryAssignement = class(TObject)
|
|
public
|
|
Column: string;
|
|
ValueKind: TJvXMLSetKind;
|
|
SecondKind: TJvXMLSetKind;
|
|
SetOperator: TJvXMLSetOperator;
|
|
Value: string;
|
|
SecondValue: string;
|
|
constructor Create(AValue: string);
|
|
procedure UpdateElem(AElement: TJvSimpleXMLElem);
|
|
end;
|
|
|
|
TJvXMLInstruction = (xiSelect, xiUpdate, xiInsert, xiDelete);
|
|
TJvXMLQueryParser = class(TObject)
|
|
private
|
|
FQuery: string;
|
|
FTables: TObjectList;
|
|
FColumns: TObjectList;
|
|
FConditions: TObjectList;
|
|
FOrders: TObjectList;
|
|
FInstruction: TJvXMLInstruction;
|
|
FInstructionStr: string;
|
|
FTablesStr: string;
|
|
FWhereStr: string;
|
|
FColumnsStr: string;
|
|
FLimitStr: string;
|
|
FLimitBegin: Integer;
|
|
FLimitCount: Integer;
|
|
FOrderStr: string;
|
|
FSetStr: string;
|
|
FOrderTable: TJvSimpleXMLElem;
|
|
FUpdates: TObjectList;
|
|
FValuesStr: string;
|
|
FValues: TStringList;
|
|
function GetColumn(const AIndex: Integer): TJvXMLQueryColumn;
|
|
function GetTable(const AIndex: Integer): TJvXMLQueryTable;
|
|
function GetColumnsCount: Integer;
|
|
function GetTablesCount: Integer;
|
|
function GetCondition(const AIndex: Integer): TJvXMLQueryCondition;
|
|
function GetConditionsCount: Integer;
|
|
function OrderCallBack(Elems: TJvSimpleXMLElems; Index1, Index2: Integer): Integer;
|
|
function GetValue(const AIndex: Integer): string;
|
|
function GetValuesCount: Integer;
|
|
protected
|
|
function ReadToken: string;
|
|
function ReadColumns(const AEndStatement: array of string; ACanTerminate: Boolean): string;
|
|
function ReadTables(const AEndStatement: array of string): string;
|
|
function ReadWhere(const AEndStatement: array of string): string;
|
|
function ReadLimit(const AEndStatement: array of string): string;
|
|
function ReadOrderBy(const AEndStatement: array of string): string;
|
|
function ReadSet(const AEndStatement: array of string): string;
|
|
function ReadValues(const AEndStatement: array of string): string;
|
|
function ReadStatement(const AEndStatement: array of string;
|
|
ACanTerminate: Boolean; var AValue: string): string;
|
|
procedure DoValidateInstruction;
|
|
procedure DoValidateColumns;
|
|
procedure DoValidateTables;
|
|
procedure DoValidateWhere;
|
|
procedure DoValidateOrderBy;
|
|
procedure DoValidateSet;
|
|
procedure DoValidateValues;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Parse(const AQuery: string);
|
|
function CheckConditions(AXMLElem: TJvSimpleXMLElem): Boolean;
|
|
procedure LimitTable(var ATable: TJvSimpleXMLElem);
|
|
procedure OrderTable(var ATable: TJvSimpleXMLElem);
|
|
procedure UpdateRow(ARow: TJvSimpleXMLElem);
|
|
property Instruction: TJvXMLInstruction read FInstruction write FInstruction;
|
|
property Tables[const AIndex: Integer]: TJvXMLQueryTable read GetTable;
|
|
property TablesCount: Integer read GetTablesCount;
|
|
property Columns[const AIndex: Integer]: TJvXMLQueryColumn read GetColumn;
|
|
property ColumnsCount: Integer read GetColumnsCount;
|
|
property Condition[const AIndex: Integer]: TJvXMLQueryCondition read GetCondition;
|
|
property ConditionsCount: Integer read GetConditionsCount;
|
|
property Value[const AIndex: Integer]: string read GetValue;
|
|
property ValuesCount: Integer read GetValuesCount;
|
|
end;
|
|
|
|
TJvXMLQuery = class(TObject)
|
|
private
|
|
FParser: TJvXMLQueryParser;
|
|
FDatabase: TJvXMLDatabase;
|
|
FResults: TJvSimpleXMLElem;
|
|
FTables: TList;
|
|
FLastId: Integer;
|
|
protected
|
|
procedure Query(const AQuery: string);
|
|
public
|
|
constructor Create(AOwner: TJvXMLDatabase);
|
|
destructor Destroy; override;
|
|
|
|
property Results: TJvSimpleXMLElem read FResults;
|
|
property LastId: Integer read FLastId;
|
|
end;
|
|
|
|
TJvXMLDatabase = class(TJvComponent)
|
|
private
|
|
FTablesPath: string;
|
|
FTables: TObjectList;
|
|
protected
|
|
function GetTable(const AName: string): TJvSimpleXML;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
published
|
|
procedure SaveTables;
|
|
function Query(const AQuery: string): TJvXMLQuery;
|
|
property TablesPath: string read FTablesPath write FTablesPath;
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
const
|
|
UnitVersioning: TUnitVersionInfo = (
|
|
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_39/run/JvXmlDatabase.pas $';
|
|
Revision: '$Revision: 12461 $';
|
|
Date: '$Date: 2009-08-14 19:21:33 +0200 (ven., 14 août 2009) $';
|
|
LogPath: 'JVCL\run'
|
|
);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFDEF SUPPORTS_INLINE}
|
|
Windows,
|
|
{$ENDIF SUPPORTS_INLINE}
|
|
JvJCLUtils, JvResources;
|
|
|
|
//=== { TJvXMLDatabase } =====================================================
|
|
|
|
constructor TJvXMLDatabase.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FTables := TObjectList.Create;
|
|
end;
|
|
|
|
destructor TJvXMLDatabase.Destroy;
|
|
begin
|
|
FTables.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvXMLDatabase.GetTable(const AName: string): TJvSimpleXML;
|
|
var
|
|
I: Integer;
|
|
St: string;
|
|
LTable: TJvXMLTable;
|
|
begin
|
|
St := TablesPath + AName;
|
|
for I := 0 to FTables.Count-1 do
|
|
if TJvXMLTable(FTables[I]).FileName = St then
|
|
begin
|
|
Result := TJvXMLTable(FTables[I]).XML;
|
|
Exit;
|
|
end;
|
|
|
|
LTable := TJvXMLTable.Create;
|
|
LTable.XML := TJvSimpleXML.Create(nil);
|
|
LTable.XML.LoadFromFile(St);
|
|
LTable.Locked := False;
|
|
LTable.FileName := St;
|
|
FTables.Add(LTable);
|
|
Result := LTable.XML;
|
|
end;
|
|
|
|
function TJvXMLDatabase.Query(const AQuery: string): TJvXMLQuery;
|
|
begin
|
|
Result := TJvXMLQuery.Create(Self);
|
|
Result.Query(AQuery);
|
|
end;
|
|
|
|
procedure TJvXMLDatabase.SaveTables;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to FTables.Count-1 do
|
|
TJvXMLTable(FTables[I]).XML.SaveToFile(TJvXMLTable(FTables[I]).FileName);
|
|
end;
|
|
|
|
//=== { TJvXMLQuery } ========================================================
|
|
|
|
constructor TJvXMLQuery.Create(AOwner: TJvXMLDatabase);
|
|
begin
|
|
inherited Create;
|
|
FDatabase := AOwner;
|
|
FParser := TJvXMLQueryParser.Create;
|
|
FResults := TJvSimpleXMLElemClassic.Create(nil);
|
|
FTables := TList.Create;
|
|
end;
|
|
|
|
destructor TJvXMLQuery.Destroy;
|
|
begin
|
|
FParser.Free;
|
|
FResults.Free;
|
|
FTables.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TJvXMLQuery.Query(const AQuery: string);
|
|
var
|
|
I, J, lMax: Integer;
|
|
LElem: TJvSimpleXMLElemClassic;
|
|
LValue: string;
|
|
|
|
function IsColumnSelected(const ATable, AColumn: string): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
for I := 0 to FParser.ColumnsCount-1 do
|
|
if (FParser.Columns[I].Name = '*') or ((FParser.Columns[I].Name = AColumn) and
|
|
((FParser.Columns[I].Table = '') or (FParser.Columns[I].Table = ATable))) then
|
|
begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
procedure ConstructTable(AIndex: Integer; var AElem: TJvSimpleXMLElemClassic);
|
|
var
|
|
I, J: Integer;
|
|
LElem: TJvSimpleXMLElemClassic;
|
|
begin
|
|
if AIndex >= FTables.Count then
|
|
begin
|
|
if FParser.CheckConditions(AElem) then
|
|
FResults.Items.Add(AElem)
|
|
else
|
|
AElem.Free;
|
|
end
|
|
else
|
|
with TJvSimpleXML(FTables[AIndex]) do
|
|
for I := 0 to Root.Items.Count-1 do
|
|
begin
|
|
LElem := TJvSimpleXMLElemClassic.Create(nil);
|
|
LElem.Assign(AElem);
|
|
|
|
//Select columns to add
|
|
for J := 0 to Root.Items[I].Properties.Count-1 do
|
|
if IsColumnSelected(FParser.Tables[AIndex].Alias, Root.Items[I].Properties[J].Name) then
|
|
LElem.Properties.Add(Root.Items[I].Properties[J].Name, Root.Items[I].Properties[J].Value);
|
|
|
|
ConstructTable(AIndex + 1, LElem);
|
|
end;
|
|
end;
|
|
|
|
procedure DeleteRows;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
for I := 0 to FTables.Count-1 do
|
|
for J := TJvSimpleXML(FTables[I]).Root.Items.Count-1 downto 0 do
|
|
if FParser.CheckConditions(TJvSimpleXML(FTables[I]).Root.Items[J]) then
|
|
TJvSimpleXML(FTables[I]).Root.Items.Delete(J);
|
|
end;
|
|
|
|
procedure UpdateRows;
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
for I := 0 to FTables.Count-1 do
|
|
for J := TJvSimpleXML(FTables[I]).Root.Items.Count - 1 downto 0 do
|
|
if FParser.CheckConditions(TJvSimpleXML(FTables[I]).Root.Items[J]) then
|
|
FParser.UpdateRow(TJvSimpleXML(FTables[I]).Root.Items[J]);
|
|
end;
|
|
|
|
begin
|
|
//Parse
|
|
FParser.Parse(AQuery);
|
|
|
|
//Get all tables
|
|
for I := 0 to FParser.TablesCount-1 do
|
|
FTables.Add(FDatabase.GetTable(FParser.Tables[I].Name));
|
|
|
|
//Execute
|
|
case FParser.Instruction of
|
|
xiSelect:
|
|
begin
|
|
LElem := TJvSimpleXMLElemClassic.Create(nil);
|
|
LElem.Name := 'Item';
|
|
FResults.Name := 'Results';
|
|
ConstructTable(0, LElem);
|
|
end;
|
|
xiDelete:
|
|
begin
|
|
DeleteRows;
|
|
FDatabase.SaveTables;
|
|
end;
|
|
xiUpdate:
|
|
begin
|
|
UpdateRows;
|
|
FDatabase.SaveTables;
|
|
end;
|
|
xiInsert:
|
|
begin
|
|
if FTables.Count = 1 then
|
|
with TJvSimpleXML(FTables[0]).Root.Items.Add('item') do
|
|
for I := 0 to FParser.ColumnsCount-1 do
|
|
if I < FParser.ValuesCount then
|
|
begin
|
|
LValue := FParser.Value[I];
|
|
if LValue = 'NULL' then
|
|
begin
|
|
lMax := 0;
|
|
for J := 0 to TJvSimpleXML(FTables[0]).Root.Items.Count-1 do
|
|
lMax := Max(lMax, TJvSimpleXML(FTables[0]).Root.Items[J].Properties.IntValue(FParser.Columns[I].Name, 0));
|
|
Inc(lMax);
|
|
LValue := IntToStr(lMax);
|
|
FLastId := lMax;
|
|
end
|
|
else
|
|
if LValue = 'NOW' then
|
|
LValue := DateTimeToStr(Now)
|
|
else
|
|
if LValue = 'DATE' then
|
|
LValue := DateToStr(Now)
|
|
else
|
|
if LValue = 'TIME' then
|
|
LValue := TimeToStr(Now);
|
|
Properties.Add(FParser.Columns[I].Name, LValue);
|
|
end;
|
|
FDatabase.SaveTables;
|
|
end;
|
|
end;
|
|
|
|
FParser.OrderTable(FResults);
|
|
FParser.LimitTable(FResults);
|
|
end;
|
|
|
|
//=== { TJvXMLQueryParser } ==================================================
|
|
|
|
constructor TJvXMLQueryParser.Create;
|
|
begin
|
|
inherited Create;
|
|
FTables := TObjectList.Create;
|
|
FColumns := TObjectList.Create;
|
|
FConditions := TObjectList.Create;
|
|
FOrders := TObjectList.Create;
|
|
FUpdates := TObjectList.Create;
|
|
FValues := TStringList.Create;
|
|
FLimitBegin := 0;
|
|
FLimitCount := MaxInt;
|
|
end;
|
|
|
|
destructor TJvXMLQueryParser.Destroy;
|
|
begin
|
|
FTables.Free;
|
|
FColumns.Free;
|
|
FConditions.Free;
|
|
FOrders.Free;
|
|
FUpdates.Free;
|
|
FValues.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.CheckConditions(AXMLElem: TJvSimpleXMLElem): Boolean;
|
|
var
|
|
I: Integer;
|
|
|
|
function CheckCondition(var AIndex: Integer): Boolean;
|
|
var
|
|
LComp: TJvXMLSQLOperator;
|
|
LValue, LValue2: string;
|
|
LDate: TDateTime;
|
|
begin
|
|
Result := True;
|
|
while AIndex < FConditions.Count do
|
|
begin
|
|
with TJvXMLQueryCondition(FConditions[AIndex]) do
|
|
case SQLOperator of
|
|
opLeftParenthesis:
|
|
begin
|
|
Inc(AIndex);
|
|
Result := Result and (CheckCondition(AIndex));
|
|
end;
|
|
opRightParenthesis:
|
|
Exit;
|
|
opNot:
|
|
begin
|
|
Inc(AIndex);
|
|
Result := Result and (not CheckCondition(AIndex));
|
|
end;
|
|
opColumn, opConstant:
|
|
begin
|
|
if SQLOperator = opConstant then
|
|
LValue := Condition
|
|
else
|
|
begin
|
|
if Condition = 'daysbetweennow' then
|
|
begin
|
|
Inc(AIndex, 2);
|
|
LValue := AXMLElem.Properties.Value(TJvXMLQueryCondition(FConditions[AIndex]).Condition);
|
|
Inc(AIndex);
|
|
LDate := StrToDateTimeDef(LValue, 0);
|
|
LValue := IntToStr(DaysBetween(Now, LDate));
|
|
if LDate < Now then
|
|
LValue := '-' + LValue;
|
|
end
|
|
else
|
|
LValue := AXMLElem.Properties.Value(Condition);
|
|
end;
|
|
Inc(AIndex, 2);
|
|
if AIndex >= FConditions.Count then
|
|
begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
LComp := TJvXMLQueryCondition(FConditions[AIndex-1]).SQLOperator;
|
|
|
|
if TJvXMLQueryCondition(FConditions[AIndex]).SQLOperator = opConstant then
|
|
LValue2 := TJvXMLQueryCondition(FConditions[AIndex]).Condition
|
|
else
|
|
if TJvXMLQueryCondition(FConditions[AIndex]).SQLOperator = opColumn then
|
|
begin
|
|
LValue2 := TJvXMLQueryCondition(FConditions[AIndex]).Condition;
|
|
if AXMLElem.Properties.ItemNamed[LValue2] <> nil then
|
|
LValue2 := AXMLElem.Properties.Value(LValue2);
|
|
end
|
|
else
|
|
if (TJvXMLQueryCondition(FConditions[AIndex]).SQLOperator = opNull) and (LComp = opEquals) then
|
|
begin
|
|
Result := Result and (LValue = '');
|
|
LComp := opNone;
|
|
end
|
|
else
|
|
begin
|
|
Result := False;
|
|
LComp := opNone;
|
|
end;
|
|
|
|
try
|
|
case LComp of
|
|
opEquals:
|
|
Result := Result and (LValue = LValue2);
|
|
opGreater:
|
|
Result := Result and (StrToFloat(LValue) > StrToFloat(LValue2));
|
|
opSmaller:
|
|
Result := Result and (StrToFloat(LValue) < StrToFloat(LValue2));
|
|
opGreaterEquals:
|
|
Result := Result and (StrToFloat(LValue) >= StrToFloat(LValue2));
|
|
opSmallerEquals:
|
|
Result := Result and (StrToFloat(LValue) <= StrToFloat(LValue2));
|
|
opLike:
|
|
begin
|
|
//Not implemented yet
|
|
end;
|
|
end;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
opOr:
|
|
begin
|
|
Inc(AIndex);
|
|
Result := Result or CheckCondition(AIndex);
|
|
end;
|
|
opAnd:
|
|
begin
|
|
Inc(AIndex);
|
|
Result := Result and CheckCondition(AIndex);
|
|
end;
|
|
opXor:
|
|
begin
|
|
Inc(AIndex);
|
|
Result := Result xor CheckCondition(AIndex);
|
|
end;
|
|
end;
|
|
Inc(AIndex);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
I := 0;
|
|
Result := CheckCondition(I);
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateColumns;
|
|
var
|
|
I: Integer;
|
|
LColumn: TJvXMLQueryColumn;
|
|
begin
|
|
I := Pos(',', FColumnsStr);
|
|
repeat
|
|
if I <> 0 then
|
|
begin
|
|
LColumn := TJvXMLQueryColumn.Create(Trim(Copy(FColumnsStr, 1, I - 1)));
|
|
FColumns.Add(LColumn);
|
|
FColumnsStr := Trim(Copy(FColumnsStr, I + 1, MaxInt));
|
|
I := Pos(',', FColumnsStr);
|
|
end
|
|
else
|
|
if FColumnsStr <> '' then
|
|
begin
|
|
LColumn := TJvXMLQueryColumn.Create(Trim(FColumnsStr));
|
|
FColumns.Add(LColumn);
|
|
FColumnsStr := '';
|
|
end;
|
|
until FColumnsStr = '';
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateInstruction;
|
|
begin
|
|
FInstructionStr := UpperCase(FInstructionStr);
|
|
|
|
if FInstructionStr = 'SELECT' then
|
|
FInstruction := xiSelect
|
|
else
|
|
if FInstructionStr = 'UPDATE' then
|
|
FInstruction := xiUpdate
|
|
else
|
|
if FInstructionStr = 'INSERT' then
|
|
FInstruction := xiInsert
|
|
else
|
|
if FInstructionStr = 'DELETE' then
|
|
FInstruction := xiDelete
|
|
else
|
|
raise TJvXMLDatabaseException.CreateResFmt(@RsEUnknownInstruction, [FInstructionStr]);
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateOrderBy;
|
|
var
|
|
I: Integer;
|
|
LOrder: TJvXMLQueryOrder;
|
|
begin
|
|
FOrderStr := Trim(UpperCase(FOrderStr));
|
|
I := Pos(' ', FOrderStr);
|
|
if I <> 0 then
|
|
FOrderStr := Trim(Copy(FOrderStr, I + 1, MaxInt));
|
|
|
|
I := Pos(',', FOrderStr);
|
|
repeat
|
|
if I <> 0 then
|
|
begin
|
|
LOrder := TJvXMLQueryOrder.Create(Trim(Copy(FOrderStr, 1, I - 1)));
|
|
FOrders.Add(LOrder);
|
|
FOrderStr := Trim(Copy(FOrderStr, I + 1, MaxInt));
|
|
I := Pos(',', FOrderStr);
|
|
end
|
|
else
|
|
if FOrderStr <> '' then
|
|
begin
|
|
LOrder := TJvXMLQueryOrder.Create(Trim(FOrderStr));
|
|
FOrders.Add(LOrder);
|
|
FOrderStr := '';
|
|
end;
|
|
until FOrderStr = '';
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateSet;
|
|
var
|
|
I: Integer;
|
|
LSet: TJvXMLQueryAssignement;
|
|
begin
|
|
FSetStr := Trim(FSetStr);
|
|
I := Pos(',', FSetStr);
|
|
repeat
|
|
if I <> 0 then
|
|
begin
|
|
LSet := TJvXMLQueryAssignement.Create(Trim(Copy(FSetStr, 1, I - 1)));
|
|
FUpdates.Add(LSet);
|
|
FSetStr := Trim(Copy(FSetStr, I + 1, MaxInt));
|
|
I := Pos(',', FSetStr);
|
|
end
|
|
else
|
|
if FSetStr <> '' then
|
|
begin
|
|
LSet := TJvXMLQueryAssignement.Create(Trim(FSetStr));
|
|
FUpdates.Add(LSet);
|
|
FSetStr := '';
|
|
end;
|
|
until FSetStr = '';
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateTables;
|
|
var
|
|
I: Integer;
|
|
LTable: TJvXMLQueryTable;
|
|
begin
|
|
I := Pos(',', FTablesStr);
|
|
repeat
|
|
if I <> 0 then
|
|
begin
|
|
LTable := TJvXMLQueryTable.Create(Trim(Copy(FTablesStr, 1, I - 1)));
|
|
FTables.Add(LTable);
|
|
FTablesStr := Trim(Copy(FTablesStr, I + 1, MaxInt));
|
|
I := Pos(',', FTablesStr);
|
|
end
|
|
else
|
|
if FTablesStr <> '' then
|
|
begin
|
|
LTable := TJvXMLQueryTable.Create(Trim(FTablesStr));
|
|
FTables.Add(LTable);
|
|
FTablesStr := '';
|
|
end;
|
|
until FTablesStr = '';
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateValues;
|
|
var
|
|
I: Integer;
|
|
|
|
function ParseValue(const AValue: string): string;
|
|
begin
|
|
Result := Trim(AValue);
|
|
|
|
//Escape quotes
|
|
if (Result <> '') and ((Result[1] = '''') or (Result[1] = '"')) then // do not use 'in' because of D2009
|
|
Result := Copy(Result, 2, Length(Result) - 2);
|
|
|
|
if SameText(Result, 'now') then
|
|
Result := DateTimeToStr(Now);
|
|
end;
|
|
|
|
begin
|
|
I := Pos(',', FValuesStr);
|
|
repeat
|
|
if I <> 0 then
|
|
begin
|
|
FValues.Add(ParseValue(Trim(Copy(FValuesStr,1,I - 1))));
|
|
FValuesStr := Trim(Copy(FValuesStr, I + 1, MaxInt));
|
|
I := Pos(',', FValuesStr);
|
|
end
|
|
else
|
|
if FValuesStr<>'' then
|
|
begin
|
|
FValues.Add(ParseValue(Trim(FValuesStr)));
|
|
FValuesStr := '';
|
|
end;
|
|
until FValuesStr = '';
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.DoValidateWhere;
|
|
var
|
|
LToken: string;
|
|
I, WhereStrLen: Integer;
|
|
LChar: Char;
|
|
|
|
procedure AddToken(const AToken: string);
|
|
begin
|
|
LToken := LowerCase(LToken);
|
|
|
|
if LToken = 'and' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opAnd))
|
|
else
|
|
if LToken = 'or' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opOr))
|
|
else
|
|
if LToken = 'like' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opLike))
|
|
else
|
|
if LToken = 'xor' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opXor))
|
|
else
|
|
if LToken = 'is' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opEquals))
|
|
else
|
|
if LToken = 'null' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opNull))
|
|
else
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opColumn,LToken));
|
|
end;
|
|
|
|
begin
|
|
FWhereStr := FWhereStr + ' ';
|
|
WhereStrLen := Length(FWhereStr);
|
|
I := 1;
|
|
LToken := '';
|
|
while I < WhereStrLen do
|
|
begin
|
|
case FWhereStr[I] of
|
|
'(':
|
|
begin
|
|
if LToken<>'' then
|
|
begin
|
|
AddToken(LToken);
|
|
LToken := '';
|
|
end;
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opLeftParenthesis));
|
|
end;
|
|
')':
|
|
begin
|
|
if LToken<>'' then
|
|
begin
|
|
AddToken(LToken);
|
|
LToken := '';
|
|
end;
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opRightParenthesis));
|
|
end;
|
|
'a'..'z', 'A'..'Z', '0'..'9', '_':
|
|
LToken := LToken + FWhereStr[I];
|
|
' ':
|
|
if LToken <> '' then
|
|
begin
|
|
AddToken(LToken);
|
|
LToken := '';
|
|
end;
|
|
'=':
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opEquals));
|
|
'>':
|
|
begin
|
|
Inc(I);
|
|
if I < WhereStrLen then
|
|
begin
|
|
if FWhereStr[I] = '=' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opGreaterEquals))
|
|
else
|
|
begin
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opGreater));
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
end;
|
|
'<':
|
|
begin
|
|
Inc(I);
|
|
if I < WhereStrLen then
|
|
begin
|
|
if FWhereStr[I] = '=' then
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opSmallerEquals))
|
|
else
|
|
begin
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opSmaller));
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
end;
|
|
'''', '"':
|
|
begin
|
|
LChar := FWhereStr[I];
|
|
Inc(I);
|
|
LToken := '';
|
|
while (I < WhereStrLen) and (FWhereStr[I] <> LChar) do
|
|
begin
|
|
LToken := LToken + FWhereStr[I];
|
|
Inc(I);
|
|
end;
|
|
FConditions.Add(TJvXMLQueryCondition.Create(opConstant,LToken));
|
|
LToken := '';
|
|
end;
|
|
end;
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetColumn(const AIndex: Integer): TJvXMLQueryColumn;
|
|
begin
|
|
Result := TJvXMLQueryColumn(FColumns[AIndex]);
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetColumnsCount: Integer;
|
|
begin
|
|
Result := FColumns.Count;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetCondition(const AIndex: Integer): TJvXMLQueryCondition;
|
|
begin
|
|
Result := TJvXMLQueryCondition(FConditions[AIndex]);
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetConditionsCount: Integer;
|
|
begin
|
|
Result := FConditions.Count;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetTable(const AIndex: Integer): TJvXMLQueryTable;
|
|
begin
|
|
Result := TJvXMLQueryTable(FTables[AIndex]);
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetTablesCount: Integer;
|
|
begin
|
|
Result := FTables.Count;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetValue(const AIndex: Integer): string;
|
|
begin
|
|
Result := FValues[AIndex];
|
|
end;
|
|
|
|
function TJvXMLQueryParser.GetValuesCount: Integer;
|
|
begin
|
|
Result := FValues.Count;
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.LimitTable(var ATable: TJvSimpleXMLElem);
|
|
begin
|
|
while (FLimitBegin > 0) and (ATable.Items.Count > 0) do
|
|
begin
|
|
ATable.Items.Delete(0);
|
|
Dec(FLimitBegin);
|
|
end;
|
|
while (ATable.Items.Count > FLimitCount) do
|
|
ATable.Items.Delete(ATable.Items.Count - 1);
|
|
end;
|
|
|
|
function TJvXMLQueryParser.OrderCallBack(Elems: TJvSimpleXMLElems;
|
|
Index1, Index2: Integer): Integer;
|
|
var
|
|
I: Integer;
|
|
LStr1, LStr2: string;
|
|
LFloat1, LFloat2: Double;
|
|
begin
|
|
Result := 0;
|
|
|
|
for I := 0 to FOrders.Count-1 do
|
|
begin
|
|
LStr1 := FOrderTable.Items[Index1].Properties.Value(TJvXMLQueryOrder(FOrders[I]).Column);
|
|
LStr2 := FOrderTable.Items[Index2].Properties.Value(TJvXMLQueryOrder(FOrders[I]).Column);
|
|
if LStr1 <> LStr2 then
|
|
begin
|
|
//convert to date/int
|
|
case TJvXMLQueryOrder(FOrders[I]).Convertion of
|
|
ocNone:
|
|
Result := AnsiCompareStr(LStr1, LStr2);
|
|
ocDate:
|
|
Result := CompareDateTime(StrToDateTimeDef(LStr1, 0), StrToDateTimeDef(LStr2, 0));
|
|
ocInteger:
|
|
Result := StrToIntDef(LStr1, 0) - StrToIntDef(LStr2, 0);
|
|
ocFloat:
|
|
begin
|
|
// NOTE: StrToFloatDefIgnoreInvalidCharacters now called JvSafeStrToFloatDef:
|
|
LFloat1 := JvSafeStrToFloatDef(LStr1, 0);
|
|
LFloat2 := JvSafeStrToFloatDef(LStr2, 0);
|
|
if LFloat1 > LFloat2 then
|
|
Result := 1
|
|
else
|
|
if LFloat1 < LFloat2 then
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
if not TJvXMLQueryOrder(FOrders[I]).Ascending then
|
|
Result := - Result;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.OrderTable(var ATable: TJvSimpleXMLElem);
|
|
begin
|
|
FOrderTable := ATable;
|
|
ATable.Items.CustomSort(OrderCallBack);
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.Parse(const AQuery: string);
|
|
var
|
|
St: string;
|
|
LStatements: array of string;
|
|
I, J: Integer;
|
|
begin
|
|
FQuery := AQuery;
|
|
|
|
FInstructionStr := ReadToken;
|
|
DoValidateInstruction;
|
|
|
|
case Instruction of
|
|
xiSelect:
|
|
begin
|
|
St := ReadColumns(['FROM', 'WHERE', 'ORDER', 'LIMIT'], False);
|
|
SetLength(LStatements, 4);
|
|
LStatements[0] := 'FROM';
|
|
LStatements[1] := 'WHERE';
|
|
LStatements[2] := 'ORDER';
|
|
LStatements[3] := 'LIMIT';
|
|
end;
|
|
xiDelete:
|
|
begin
|
|
ReadToken; //pass the FROM keyword
|
|
St := 'FROM';
|
|
SetLength(LStatements, 2);
|
|
LStatements[0] := 'FROM';
|
|
LStatements[1] := 'WHERE';
|
|
end;
|
|
xiUpdate:
|
|
begin
|
|
St := 'FROM';
|
|
SetLength(LStatements, 3);
|
|
LStatements[0] := 'FROM';
|
|
LStatements[1] := 'SET';
|
|
LStatements[2] := 'WHERE';
|
|
end;
|
|
xiInsert:
|
|
begin
|
|
St := 'FROM';
|
|
SetLength(LStatements, 3);
|
|
LStatements[0] := 'FROM';
|
|
LStatements[1] := 'VALUES';
|
|
LStatements[2] := 'COLUMNS';
|
|
ReadToken; // Pass the into statement
|
|
|
|
//Modify query for lightness of parser
|
|
//INSERT INTO file.XML(Col1, Col2) VALUES(val1, val2)
|
|
// into
|
|
//INSERT INTO file.XML COLUMNS col1, col2 VALUES val1, val2
|
|
FQuery := StringReplace(FQuery, '()', '', [rfReplaceAll]);
|
|
FQuery := StringReplace(FQuery, ')', ' ', [rfReplaceAll]);
|
|
FQuery := StringReplace(FQuery, '(', ' COLUMNS ', []);
|
|
FQuery := StringReplace(FQuery, '(', ' ', []);
|
|
end;
|
|
end;
|
|
|
|
while St <> '' do
|
|
begin
|
|
J := -1;
|
|
for I := 0 to Length(LStatements) - 1 do
|
|
if LStatements[I] = St then
|
|
begin
|
|
LStatements[I] := ''; //Do not accept it anymore
|
|
J := I;
|
|
Break;
|
|
end;
|
|
if J = -1 then
|
|
raise TJvXMLDatabaseException.CreateResFmt(@RsEUnexpectedStatement, [St]);
|
|
|
|
if St = 'FROM' then
|
|
St := ReadTables(LStatements)
|
|
else
|
|
if St = 'WHERE' then
|
|
St := ReadWhere(LStatements)
|
|
else
|
|
if St = 'LIMIT' then
|
|
St := ReadLimit(LStatements)
|
|
else
|
|
if St = 'ORDER' then
|
|
St := ReadOrderBy(LStatements)
|
|
else
|
|
if St = 'SET' then
|
|
St := ReadSet(LStatements)
|
|
else
|
|
if St = 'VALUES' then
|
|
St := ReadValues(LStatements)
|
|
else
|
|
if St = 'COLUMNS' then
|
|
St := ReadColumns(LStatements, False);
|
|
end;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadColumns(const AEndStatement: array of string;
|
|
ACanTerminate: Boolean): string;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, ACanTerminate, FColumnsStr);
|
|
DoValidateColumns;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadLimit(const AEndStatement: array of string): string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, True, FLimitStr);
|
|
I := Pos(',', FLimitStr);
|
|
if I = 0 then
|
|
FLimitCount := StrToIntDef(FLimitStr, MaxInt)
|
|
else
|
|
begin
|
|
FLimitCount := StrToIntDef(Trim(Copy(FLimitStr, I + 1, MaxInt)), MaxInt);
|
|
FLimitBegin := StrToIntDef(Trim(Copy(FLimitStr, 1, I - 1)), 0);
|
|
end;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadOrderBy(const AEndStatement: array of string): string;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, True, FOrderStr);
|
|
DoValidateOrderBy;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadSet(const AEndStatement: array of string): string;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, True, FSetStr);
|
|
DoValidateSet;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadStatement(const AEndStatement: array of string;
|
|
ACanTerminate: Boolean; var AValue: string): string;
|
|
var
|
|
St, UpSt: string;
|
|
LFound: Boolean;
|
|
I: Integer;
|
|
begin
|
|
AValue := '';
|
|
LFound := False;
|
|
Result := '';
|
|
while not LFound do
|
|
if (FQuery = '') and ACanTerminate then
|
|
LFound := True
|
|
else
|
|
begin
|
|
St := ReadToken;
|
|
if St <> '' then
|
|
begin
|
|
UpSt := UpperCase(St);
|
|
for I := 0 to Length(AEndStatement) - 1 do
|
|
if UpSt = AEndStatement[I] then
|
|
begin
|
|
LFound := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
if not LFound then
|
|
AValue := AValue + ' ' + St
|
|
else
|
|
Result := St;
|
|
end;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadTables(const AEndStatement: array of string): string;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, True, FTablesStr);
|
|
DoValidateTables;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadToken: string;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if FQuery = '' then
|
|
raise TJvXMLDatabaseException.CreateRes(@RsEUnexpectedEndOfQuery);
|
|
|
|
FQuery := TrimLeft(FQuery);
|
|
I := 1;
|
|
while (I < Length(FQuery)) and (FQuery[I] <> ' ') do {,'(',')'}
|
|
Inc(I);
|
|
if I >= Length(FQuery) then
|
|
begin
|
|
Result := Trim(FQuery);
|
|
FQuery := '';
|
|
end
|
|
else
|
|
begin
|
|
Result := Copy(FQuery, 1, I - 1);
|
|
FQuery := Copy(FQuery, I + 1, MaxInt);
|
|
end;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadValues(const AEndStatement: array of string): string;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, True, FValuesStr);
|
|
DoValidateValues;
|
|
end;
|
|
|
|
function TJvXMLQueryParser.ReadWhere(const AEndStatement: array of string): string;
|
|
begin
|
|
Result := ReadStatement(AEndStatement, True, FWhereStr);
|
|
DoValidateWhere;
|
|
end;
|
|
|
|
procedure TJvXMLQueryParser.UpdateRow(ARow: TJvSimpleXMLElem);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to FUpdates.Count - 1 do
|
|
TJvXMLQueryAssignement(FUpdates[I]).UpdateElem(ARow);
|
|
end;
|
|
|
|
//=== { TJvXMLQueryColumn } ==================================================
|
|
|
|
constructor TJvXMLQueryColumn.Create(const AValue: string);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited Create;
|
|
I := Pos('.', AValue);
|
|
if I <> 0 then
|
|
begin
|
|
Name := Copy(AValue, I + 1, MaxInt);
|
|
Table := Copy(AValue, 1, I - 1);
|
|
end
|
|
else
|
|
Name := AValue;
|
|
end;
|
|
|
|
//=== { TJvXMLQueryTable } ===================================================
|
|
|
|
constructor TJvXMLQueryTable.Create(const AValue: string);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
inherited Create;
|
|
I := Pos(' ', AValue);
|
|
if I <> 0 then
|
|
begin
|
|
Name := Copy(AValue, 1, I - 1);
|
|
Alias := Trim(Copy(AValue, I + 1, MaxInt));
|
|
end
|
|
else
|
|
Name := AValue;
|
|
end;
|
|
|
|
//=== { TJvXMLQueryCondition } ===============================================
|
|
|
|
constructor TJvXMLQueryCondition.Create(AOperator: TJvXMLSQLOperator;
|
|
const ACondition: string);
|
|
begin
|
|
inherited Create;
|
|
Self.SQLOperator := AOperator;
|
|
Self.Condition := ACondition;
|
|
end;
|
|
|
|
//=== { TJvXMLQueryOrder } ===================================================
|
|
|
|
constructor TJvXMLQueryOrder.Create(const AValue: string);
|
|
var
|
|
I: Integer;
|
|
St: string;
|
|
begin
|
|
inherited Create;
|
|
Column := Trim(AValue);
|
|
Ascending := True;
|
|
Convertion := ocNone;
|
|
|
|
I := Pos(' ', Column);
|
|
if I <> 0 then
|
|
begin
|
|
SetLength(Column, I - 1);
|
|
Ascending := Pos('ASC', UpperCase(AValue)) <> 0;
|
|
end;
|
|
|
|
I := Pos('(', Column);
|
|
if I <> 0 then
|
|
begin
|
|
St := UpperCase(Copy(Column, 1, I - 1));
|
|
Column := Copy(Column, I + 1, MaxInt);
|
|
SetLength(Column, Length(Column) - 1);
|
|
|
|
if St = 'DATE' then
|
|
Convertion := ocDate
|
|
else
|
|
if (St = 'Integer') or (St = 'INT') then
|
|
Convertion := ocInteger
|
|
else
|
|
if St = 'FLOAT' then
|
|
Convertion := ocFloat;
|
|
end;
|
|
end;
|
|
|
|
//=== { TJvXMLQueryAssignement } =============================================
|
|
|
|
constructor TJvXMLQueryAssignement.Create(AValue: string);
|
|
var
|
|
I, J: Integer;
|
|
LDelimiter: Char;
|
|
begin
|
|
inherited Create;
|
|
I := Pos('=', AValue);
|
|
if I = 0 then
|
|
// (rom) this definitely neds to be improved
|
|
raise Exception.Create('')
|
|
else
|
|
begin
|
|
Column := Trim(Copy(AValue, 1, I - 1));
|
|
AValue := Trim(Copy(AValue, I + 1, MaxInt));
|
|
|
|
if AValue = '' then
|
|
raise Exception.Create('');
|
|
|
|
//Determine if column or constant
|
|
if (AValue[1] = '"') or (AValue[1] = '''') then
|
|
begin
|
|
LDelimiter := AValue[1];
|
|
ValueKind := skConstant;
|
|
AValue := Copy(AValue, 2, MaxInt);
|
|
I := 0;
|
|
for J := 1 to Length(AValue) do
|
|
if AValue[J] = LDelimiter then
|
|
if (J=1) or (AValue[J-1] <> '\') then
|
|
begin
|
|
I := J;
|
|
Break;
|
|
end;
|
|
if I <> 0 then
|
|
begin
|
|
Value := Copy(AValue, 1, I - 1);
|
|
Value := StringReplace(Value, '\' + LDelimiter, LDelimiter, [rfReplaceAll]);
|
|
AValue := Trim(Copy(AValue, I + 1, MaxInt));
|
|
end
|
|
else
|
|
raise Exception.Create('');
|
|
end
|
|
else
|
|
begin
|
|
ValueKind := skColumn;
|
|
I := Pos(' ', AValue);
|
|
if I = 0 then
|
|
begin
|
|
Value := AValue;
|
|
AValue := '';
|
|
end
|
|
else
|
|
begin
|
|
Value := Copy(AValue, 1, I - 1);
|
|
AValue := Trim(Copy(AValue, I + 1, MaxInt));
|
|
end;
|
|
end;
|
|
|
|
//Second kind and second value?
|
|
if AValue = '' then
|
|
SetOperator := soNone
|
|
else
|
|
begin
|
|
case AValue[1] of
|
|
'+':
|
|
SetOperator := soAdd;
|
|
'-':
|
|
SetOperator := soSubstract;
|
|
'*':
|
|
SetOperator := soMultiply;
|
|
'/':
|
|
SetOperator := soDivide;
|
|
else
|
|
raise Exception.Create('');
|
|
end;
|
|
|
|
SecondValue := Trim(Copy(AValue, 2, MaxInt));
|
|
if (SecondValue <> '') and ((SecondValue[1] = '''') or (SecondValue[1] = '"')) then // do not use 'in' to make D2009 happy
|
|
begin
|
|
SecondValue := Copy(SecondValue, 2, Length(SecondValue) - 2);
|
|
SecondKind := skConstant;
|
|
end
|
|
else
|
|
SecondKind := skColumn;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TJvXMLQueryAssignement.UpdateElem(AElement: TJvSimpleXMLElem);
|
|
var
|
|
LValue, LValue2: string;
|
|
|
|
function ParseValue(const AValue: string): string;
|
|
begin
|
|
if SameText(AValue, 'now()') then
|
|
Result := DateTimeToStr(Now)
|
|
else
|
|
Result := AValue;
|
|
end;
|
|
|
|
begin
|
|
if ValueKind = skConstant then
|
|
LValue := Value
|
|
else
|
|
LValue := AElement.Properties.Value(Value, ParseValue(Value));
|
|
|
|
if SetOperator <> soNone then
|
|
begin
|
|
if SecondKind = skConstant then
|
|
LValue2 := SecondValue
|
|
else
|
|
LValue2 := AElement.Properties.Value(SecondValue, ParseValue(SecondValue));
|
|
|
|
// NOTE: StrToFloatDefIgnoreInvalidCharacters now called JvSafeStrToFloatDef:
|
|
case SetOperator of
|
|
soAdd:
|
|
LValue := FloatToStr(JvSafeStrToFloatDef(LValue,0) + JvSafeStrToFloatDef(LValue2,0));
|
|
soMultiply:
|
|
LValue := FloatToStr(JvSafeStrToFloatDef(LValue,0) * JvSafeStrToFloatDef(LValue2,0));
|
|
soDivide:
|
|
LValue := FloatToStr(JvSafeStrToFloatDef(LValue,0) / JvSafeStrToFloatDef(LValue2,0));
|
|
soSubstract:
|
|
LValue := FloatToStr(JvSafeStrToFloatDef(LValue,0) - JvSafeStrToFloatDef(LValue2,0));
|
|
end;
|
|
end;
|
|
|
|
AElement.Properties.Delete(Column);
|
|
AElement.Properties.Add(Column, LValue);
|
|
end;
|
|
|
|
{$IFDEF UNITVERSIONING}
|
|
initialization
|
|
RegisterUnitVersion(HInstance, UnitVersioning);
|
|
|
|
finalization
|
|
UnregisterUnitVersion(HInstance);
|
|
{$ENDIF UNITVERSIONING}
|
|
|
|
end.
|