
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5438 8e941d3f-bd1b-0410-a28a-d453659cc2b4
18200 lines
531 KiB
ObjectPascal
18200 lines
531 KiB
ObjectPascal
{*********************************************************}
|
|
{* FlashFiler: SQL Class Definitions *}
|
|
{*********************************************************}
|
|
|
|
(* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1
|
|
*
|
|
* 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/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is TurboPower FlashFiler
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* TurboPower Software
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END LICENSE BLOCK ***** *)
|
|
|
|
{2.11 - extensive changes throughout}
|
|
|
|
{$I ffdefine.inc}
|
|
|
|
{Enable the following to have index optimization analysis and usage
|
|
information logged to a file (used for debugging)}
|
|
{.$DEFINE LogIndexAnalysis}
|
|
|
|
{Enable the following to have transformation information
|
|
logged to a file (used for debugging)}
|
|
{$DEFINE LogTransformations}
|
|
|
|
{Enable the following to have writes counted}
|
|
{.$DEFINE CountWrites}
|
|
|
|
{Enable the following to have the root node made available through the global
|
|
LastStatement variable below (used for debugging only)}
|
|
{.$DEFINE ExposeLastStatement}
|
|
|
|
unit ffsqldef;
|
|
|
|
interface
|
|
uses
|
|
Windows,
|
|
SysUtils,
|
|
Classes,
|
|
DB,
|
|
{$IFDEF DCC6OrLater}
|
|
Variants,
|
|
{$ENDIF}
|
|
ffllbase,
|
|
ffsqldb,
|
|
ffhash;
|
|
|
|
const
|
|
fftInterval = fftReserved20;
|
|
{$IFDEF LogIndexAnalysis}
|
|
IALogFile = 'c:\ffialog.txt';
|
|
{$ENDIF}
|
|
{$IFDEF LogTransformations}
|
|
TRLogFile = 'c:\fftrlog.txt';
|
|
{$ENDIF}
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
var
|
|
IALog : System.Text;
|
|
{$ENDIF}
|
|
{$IFDEF LogTransformations}
|
|
var
|
|
TRLog : System.Text;
|
|
{$ENDIF}
|
|
|
|
type
|
|
TffSqlAggQueryMode = (aqmIdle, aqmGrouping, aqmHaving);
|
|
|
|
TffSqlNode = class;
|
|
TffSqlStatement = class;
|
|
TffSqlEnumMethod = procedure(Node: TffSqlNode) of object;
|
|
TffSqlAggregate = class;
|
|
TffSqlSELECT = class;
|
|
TffSqlColumnListOwner = class;
|
|
TffSqlRelOp = (roNone, roEQ, roLE, roL, roG, roGE, roNE);
|
|
TffSqlNode = class(TFFObject)
|
|
protected
|
|
FParent : TffSqlNode;
|
|
FOwner : TffSqlStatement;
|
|
FOwnerStmt: TffSqlColumnListOwner; {!!.11}
|
|
procedure WriteStr(Stream: TStream; const S: string);
|
|
procedure WriteEOF(Stream: TStream);
|
|
procedure AddTableReference(Select: TffSqlSELECT); virtual;
|
|
procedure AddColumnDef(Target: TffSqlColumnListOwner); virtual;
|
|
procedure AddAggregate(Target: TList); virtual;
|
|
procedure ClearBinding; virtual;
|
|
function IsAncestor(const Node : TffSqlNode) : Boolean;
|
|
{ Returns True if Node is an ancestor of this node. }
|
|
procedure ResetConstant; virtual;
|
|
procedure FlagAggregate(Select: TffSqlSELECT); virtual;
|
|
function GetType: TffFieldType; virtual;
|
|
function GetSize: Integer; virtual;
|
|
function GetDecimals: Integer; virtual;
|
|
function GetOwner: TffSqlStatement;
|
|
function GetOwnerSelect : TffSqlSelect;
|
|
function GetOwnerStmt: TFFSqlColumnListOwner; {!!.11}
|
|
procedure SQLError(const ErrorMsg: string);
|
|
procedure AssignError(Source: TffSqlNode);
|
|
procedure TypeMismatch;
|
|
function BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy; virtual;
|
|
function IsAggregate: Boolean; virtual;
|
|
public
|
|
constructor Create(AParent: TffSqlNode);
|
|
property Parent : TffSqlNode read FParent write FParent;
|
|
property Owner : TffSqlStatement read GetOwner;
|
|
property OwnerSelect : TffSqlSelect read GetOwnerSelect;
|
|
property OwnerStmt: TFFSqlColumnListOwner read GetOwnerStmt; {!!.11}
|
|
procedure EmitSQL(Stream : TStream); virtual;
|
|
function SQLText: string;
|
|
function Equals(Other: TffSqlNode): Boolean; virtual; abstract;
|
|
procedure Assign(const Source: TffSqlNode); virtual; abstract;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
virtual; abstract;
|
|
end;
|
|
|
|
TffSqlFieldRef = class(TffSqlNode)
|
|
protected
|
|
FFieldName: string;
|
|
FTableName: string;
|
|
TypeKnown : Boolean;
|
|
FType : TffFieldType;
|
|
FField : TFFSqlFieldProxy;
|
|
FGroupField : TffSqlFieldProxy;
|
|
WasWildcard: Boolean;
|
|
procedure ClearBinding; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
function GetType: TffFieldType; override;
|
|
procedure CheckType;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetField: TFFSqlFieldProxy;
|
|
function GetGroupField : TffSqlFieldProxy;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
property TableName : string read FTableName write FTableName;
|
|
property FieldName : string read FFieldName write FFieldName;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue : Variant;
|
|
property Field: TFFSqlFieldProxy read GetField;
|
|
property GroupField : TffSqlFieldProxy read GetGroupField;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function QualName : string;
|
|
function IsNull: Boolean;
|
|
end;
|
|
|
|
TffSqlSimpleExpression = class;
|
|
|
|
TAggCounter = class(TffObject)
|
|
protected
|
|
FMin, FMax : variant;
|
|
FSum, FCount : double;
|
|
function GetMax: Variant;
|
|
function GetMin: Variant;
|
|
function GetSum: Variant;
|
|
function GetAvg: Variant;
|
|
public
|
|
procedure Reset;
|
|
procedure Add(const Value: Variant);
|
|
property Min: Variant read GetMin;
|
|
property Max: Variant read GetMax;
|
|
property Count: double read FCount;
|
|
property Sum: Variant read GetSum;
|
|
property Avg: Variant read GetAvg;
|
|
end;
|
|
|
|
TffSQLAggFunction = (agCount, agMin, agMax, agSum, agAvg);
|
|
|
|
TffSqlAggregate = class(TffSqlNode)
|
|
protected
|
|
FAgFunction: TffSQLAggFunction;
|
|
FSimpleExpression : TffSqlSimpleExpression;
|
|
FDistinct : Boolean;
|
|
FCounter : TAggCounter;
|
|
FSourceField: TFFSqlFieldProxy;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetSize: Integer; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
procedure FlagAggregate(Select: TffSqlSELECT); override;
|
|
procedure AddAggregate(Target: TList); override;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
destructor Destroy; override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property AgFunction : TffSQLAggFunction read FAgFunction write FAgFunction;
|
|
property SimpleExpression : TffSqlSimpleExpression
|
|
read FSimpleExpression write FSimpleExpression;
|
|
property Distinct: Boolean read FDistinct write FDistinct;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetAggregateValue: Variant;
|
|
procedure CreateCounter(SourceField: TFFSqlFieldProxy);
|
|
procedure DeleteCounter;
|
|
procedure ResetCounters;
|
|
procedure Update;
|
|
function ValidType(aType : TffFieldType) : Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlColumn = class(TffSqlNode)
|
|
protected
|
|
FColumnName: string;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property ColumnName: string read FColumnName write FColumnName;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
end;
|
|
|
|
TffSqlBaseColumn = class(TffSqlNode)
|
|
protected
|
|
FFieldName: string;
|
|
FTableName: string;
|
|
public
|
|
property TableName: string read FTableName write FTableName;
|
|
property FieldName: string read FFieldName write FFieldName;
|
|
end;
|
|
|
|
TffSqlGroupColumn = class(TffSqlBaseColumn)
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function QualColumnName: string; virtual;
|
|
end;
|
|
|
|
TffSqlOrderColumn = class(TffSqlBaseColumn)
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function QualColumnName: string;
|
|
end;
|
|
|
|
TffSqlSelection = class;
|
|
|
|
TffSqlGroupColumnList = class(TffSqlNode)
|
|
protected
|
|
ColumnList : TList;
|
|
procedure Clear;
|
|
function GetColumn(Index: Integer): TffSqlGroupColumn;
|
|
procedure SetColumn(Index: Integer; const Value: TffSqlGroupColumn);
|
|
function GetColumnCount: Integer;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddColumn(Column: TffSqlGroupColumn): TffSqlGroupColumn;
|
|
property ColumnCount : Integer read GetColumnCount;
|
|
property Column[Index: Integer] : TffSqlGroupColumn read GetColumn write SetColumn;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function Contains(const aColName : string;
|
|
Se: TffSqlSelection): Boolean;
|
|
end;
|
|
|
|
TffSqlIsOp = (ioNull, ioTrue, ioFalse, ioUnknown);
|
|
TffSqlIsTest = class(TffSqlNode)
|
|
protected
|
|
FUnaryNot : Boolean;
|
|
FIsOp : TffSqlIsOp;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property UnaryNot: Boolean read FUnaryNot write FUnaryNot;
|
|
property IsOp : TffSqlIsOp read FIsOp write FIsOp;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean(const TestValue: Variant): Boolean;
|
|
function Evaluate(Expression: TffSqlSimpleExpression): Boolean;
|
|
end;
|
|
|
|
TffSqlBetweenClause = class(TffSqlNode)
|
|
protected
|
|
FSimpleHigh: TffSqlSimpleExpression;
|
|
FSimpleLow: TffSqlSimpleExpression;
|
|
FNegated : Boolean;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property Negated : Boolean read FNegated write FNegated;
|
|
property SimpleLow : TffSqlSimpleExpression read FSimpleLow write FSimpleLow;
|
|
property SimpleHigh : TffSqlSimpleExpression read FSimpleHigh write FSimpleHigh;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean(const TestValue: Variant): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlLikePattern = class(TffObject)
|
|
protected
|
|
LeadPattern,
|
|
TrailPattern : string;
|
|
LeadMask,
|
|
TrailMask: string;
|
|
FloatPatterns,
|
|
FloatMasks: TStringList;
|
|
public
|
|
constructor Create(SearchPattern: string; const Escape: string);
|
|
{S is the search pattern; Escape is an optional one-character escape
|
|
character}
|
|
{S contains the string to be searched for, and optionally one or more
|
|
occurrences of
|
|
'%' (match zero or more characters of any kind), and/or
|
|
'_' (match exactly one character of any kind)
|
|
If the Escape character is specified, it defines a character to prefix '%'
|
|
or '_' with
|
|
to indicate a literal '%' or '_', respectively, in the search phrase S.}
|
|
{the search must be case sensitive ('a' <> 'A') }
|
|
destructor Destroy; override;
|
|
function Find(const TextToSearch: Variant; IgnoreCase: Boolean): Boolean; {!!.13}
|
|
{examples:
|
|
S = '%Berkeley%' - Find returns true if the string 'Berkeley' exists
|
|
anywhere in TextToSearch
|
|
S = 'S__' - Find returns true if TextToSearch is exactly thee characters
|
|
long and starts with an upper-case 'S'
|
|
S = '%c___' - Find returns True if length(TextToSearch) >= 4 and the
|
|
last but three is 'c'
|
|
S = '=_%' and Escape = '=' - Find returns True if TextToSearch begins
|
|
with an underscore.
|
|
}
|
|
end;
|
|
|
|
TffSqlLikeClause = class(TffSqlNode)
|
|
protected
|
|
FSimpleExp: TffSqlSimpleExpression;
|
|
FEscapeExp: TffSqlSimpleExpression;
|
|
FNegated : Boolean;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
Limited: Boolean;
|
|
LikePattern: TffSqlLikePattern;
|
|
FBMCompat : Boolean; {!!.11}
|
|
BMCompatChecked : Boolean; {!!.11}
|
|
FBMTable: PBTable; {!!.11}
|
|
FBMPhrase: string; {!!.11}
|
|
FIgnoreCase: Boolean; {!!.13}
|
|
procedure CheckBMCompat; {!!.11}
|
|
function IsBMCompatible: Boolean; {!!.11}
|
|
function GetBmTable: PBTable; {!!.11}
|
|
function CanLimit: Boolean;
|
|
function CanReplaceWithCompare: Boolean;
|
|
procedure CheckIsConstant;
|
|
function GetLowLimit: string;
|
|
function GetHighLimit: string;
|
|
function IsConstant: Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property SimpleExp : TffSqlSimpleExpression read FSimpleExp write FSimpleExp;
|
|
property EscapeExp: TffSqlSimpleExpression read FEscapeExp write FEscapeExp;
|
|
property Negated : Boolean read FNegated write FNegated;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean(const TestValue: Variant): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
property BmTable: PBTable read GetBmTable; {!!.11}
|
|
property BmPhrase: string read FBmPhrase; {!!.11}
|
|
property IgnoreCase: Boolean read FIgnoreCase write FIgnoreCase; {!!.13}
|
|
end;
|
|
|
|
TffSqlSimpleExpressionList = class;
|
|
|
|
TffSqlInClause = class(TffSqlNode)
|
|
protected
|
|
FSimpleExp: TffSqlSimpleExpressionList;
|
|
FNegated : Boolean;
|
|
FSubQuery : TffSqlSELECT;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property SimpleExpList : TffSqlSimpleExpressionList
|
|
read FSimpleExp write FSimpleExp;
|
|
property SubQuery : TffSqlSELECT read FSubQuery write FSubQuery;
|
|
property Negated : Boolean read FNegated write FNegated;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean(const TestValue: Variant): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlTableExp = class;
|
|
|
|
TffSqlMatchOption = (moUnspec, moPartial, moFull);
|
|
TffSqlMatchClause = class(TffSqlNode)
|
|
protected
|
|
FSubQuery : TffSqlSELECT;
|
|
FOption: TffSqlMatchOption;
|
|
FUnique : Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property Unique: Boolean read FUnique write FUnique;
|
|
property Option: TffSqlMatchOption read FOption write FOption;
|
|
property SubQuery : TffSqlSELECT read FSubQuery write FSubQuery;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean(const TestValue: Variant): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlAllOrAnyClause = class(TffSqlNode)
|
|
protected
|
|
FSubQuery : TffSqlSELECT;
|
|
FAll : Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function Compare(RelOp: TffSqlRelOp; const Val: Variant): Boolean;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property All: Boolean read FAll write FAll;
|
|
property SubQuery : TffSqlSELECT read FSubQuery write FSubQuery;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlExistsClause = class(TffSqlNode)
|
|
protected
|
|
FSubQuery : TffSqlSELECT;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property SubQuery : TffSqlSELECT read FSubQuery write FSubQuery;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean: Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlUniqueClause = class(TffSqlNode)
|
|
protected
|
|
FSubQuery: TffSqlTableExp;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property SubQuery : TffSqlTableExp read FSubQuery write FSubQuery;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean: Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlCondPrimary = class(TffSqlNode)
|
|
protected
|
|
FSimpleExp1: TffSqlSimpleExpression;
|
|
FRelOp: TffSqlRelOp;
|
|
FSimpleExp2: TffSqlSimpleExpression;
|
|
FBetweenClause : TffSqlBetweenClause;
|
|
FLikeClause : TffSqlLikeClause;
|
|
FInClause : TffSqlInClause;
|
|
FIsTest : TffSqlIsTest;
|
|
FAllOrAnyClause : TffSqlAllOrAnyClause;
|
|
FExistsClause : TFfSqlExistsClause;
|
|
FUniqueClause : TFfSqlUniqueClause;
|
|
FMatchClause : TffSqlMatchClause;
|
|
TypeChecked : Boolean;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
procedure CheckType;
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
function JustSimpleExpression: Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType); {!!.11}
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
property SimpleExp1 : TffSqlSimpleExpression
|
|
read FSimpleExp1 write FSimpleExp1;
|
|
property RelOp : TffSqlRelOp read FRelOp write FRelOp;
|
|
property SimpleExp2 : TffSqlSimpleExpression
|
|
read FSimpleExp2 write FSimpleExp2;
|
|
property BetweenClause : TffSqlBetweenClause
|
|
read FBetweenClause write FBetweenClause;
|
|
property LikeClause : TffSqlLikeClause read FLikeClause write FLikeClause;
|
|
property InClause : TffSqlInClause read FInClause write FInClause;
|
|
property IsTest : TffSqlIsTest read FIsTest write FIsTest;
|
|
property AllOrAnyClause : TffSqlAllOrAnyClause
|
|
read FAllOrAnyClause write FAllOrAnyClause;
|
|
property ExistsClause : TffSqlExistsClause
|
|
read FExistsClause write FExistsClause;
|
|
property UniqueClause : TffSqlUniqueClause
|
|
read FUniqueClause write FUniqueClause;
|
|
property MatchClause : TffSqlMatchClause read FMatchClause write FMatchClause;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean: Boolean;
|
|
function GetValue: Variant;
|
|
procedure BindHaving;
|
|
function IsRelationTo(Table : TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy;
|
|
var Operator: TffSqlRelOp;
|
|
var ArgExpression: TffSqlSimpleExpression;
|
|
var SameCase: Boolean): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlCondFactor = class(TffSqlNode)
|
|
protected
|
|
FUnaryNot: Boolean;
|
|
FCondPrimary: TffSqlCondPrimary;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
TmpKnown: Boolean;
|
|
TmpValue: Boolean;
|
|
EvalLevel: Integer;
|
|
procedure CheckIsConstant;
|
|
procedure Clear;
|
|
function IsConstant: Boolean;
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
procedure MatchType(ExpectedType: TffFieldType); {!!.11}
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
destructor Destroy; override;
|
|
property UnaryNot : Boolean read FUnaryNot write FUnaryNot;
|
|
property CondPrimary : TffSqlCondPrimary read FCondPrimary write FCondPrimary;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean: Boolean;
|
|
function GetValue: Variant;
|
|
procedure BindHaving;
|
|
function IsRelationTo(Table : TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy;
|
|
var Operator: TffSqlRelOp;
|
|
var ArgExpression: TffSqlSimpleExpression;
|
|
var SameCase: Boolean): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
procedure MarkTrue;
|
|
procedure MarkUnknown;
|
|
end;
|
|
|
|
TffSqlCondExp = class;
|
|
TFFObjectProc = procedure of object;
|
|
|
|
TFFSqlKeyRelation = record
|
|
CondF : TFFSqlCondFactor;
|
|
RelationB: array[0..pred(ffcl_MaxIndexFlds)] of TffSqlCondFactor; {!!.11}
|
|
NativeKeyIndex: Integer;
|
|
RelationFieldCount,
|
|
RelationKeyFieldCount: Integer;
|
|
RelationOperators : array[0..pred(ffcl_MaxIndexFlds)] of TffSqlRelOp;
|
|
RelationOperatorB : array[0..pred(ffcl_MaxIndexFlds)] of TffSqlRelOp; {!!.11}
|
|
RelationKeyIsUnique: Boolean;
|
|
RelationKeyIsCaseInsensitive: Boolean;
|
|
RelationKeyIndexAsc : Boolean;
|
|
ArgExpressionB : array[0..pred(ffcl_MaxIndexFlds)] of TffSqlSimpleExpression; {!!.11}
|
|
ArgExpressions : array[0..pred(ffcl_MaxIndexFlds)] of TffSqlSimpleExpression;
|
|
{$IFDEF LogIndexAnalysis}
|
|
RelationFields : array[0..pred(ffcl_MaxIndexFlds)] of TFFSqlFieldProxy;
|
|
{$ENDIF}
|
|
SameCases : array[0..pred(ffcl_MaxIndexFlds)] of Boolean;
|
|
SameCaseB: array[0..pred(ffcl_MaxIndexFlds)] of Boolean; {!!.11}
|
|
DepIndex: Integer;
|
|
end;
|
|
|
|
TFFSqlTableProxySubset = class(TffObject)
|
|
protected
|
|
FTable : TFFSqlTableProxy;
|
|
FOpposite: TFFSqlTableProxy;
|
|
FOuter: Boolean;
|
|
public
|
|
Relations: Integer;
|
|
KeyRelation: TffSqlKeyRelation;
|
|
constructor Create(Table: TFFSqlTableProxy);
|
|
function EqualKeyDepth: Integer;
|
|
procedure Iterate(Iterator: TFFSqlTableIterator; Cookie: TffWord32);
|
|
property Table : TFFSqlTableProxy read FTable;
|
|
procedure Assign(const Source: TFFSqlTableProxySubset);
|
|
function UniqueValue: Boolean;
|
|
function ClosedSegment: Boolean;
|
|
function KeyDepth: Integer;
|
|
property Outer: Boolean read FOuter write FOuter;
|
|
property Opposite: TFFSqlTableProxy read FOpposite write FOpposite;
|
|
end;
|
|
|
|
TFFSqlTableProxySubsetList = class;
|
|
|
|
TffSqlCondTerm = class(TffSqlNode)
|
|
protected
|
|
CondFactorList : TList;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
OrderedSources : TFFSqlTableProxySubsetList;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
function GetCondFactor(Index: Integer): TffSqlCondFactor;
|
|
procedure SetCondFactor(Index: Integer; const Value: TffSqlCondFactor);
|
|
function GetCondFactorCount: Integer;
|
|
function GetSize: Integer; override;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
function AsBooleanLevel(Level: Integer): Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType); {!!.11}
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddCondFactor(Factor : TffSqlCondFactor): TffSqlCondFactor;
|
|
function InsertCondFactor(Index: Integer; Factor : TffSqlCondFactor):
|
|
TffSqlCondFactor;
|
|
property CondFactorCount : Integer read GetCondFactorCount;
|
|
property CondFactor[Index: Integer] : TffSqlCondFactor
|
|
read GetCondFactor write SetCondFactor;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean: Boolean;
|
|
function GetValue: Variant;
|
|
procedure BindHaving;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
procedure SetLevelDep(List: TFFSqlTableProxySubsetList);
|
|
end;
|
|
|
|
TFFSqlTableProxySubsetList = class(TffObject)
|
|
protected
|
|
FList : TList;
|
|
Level : Integer;
|
|
FCondTerm : TffSqlCondTerm;
|
|
FCreateResultRecord : TFFObjectProc;
|
|
FRecordsRead : Longint;
|
|
FOwner: TffSqlStatement;
|
|
WroteRow: Boolean;
|
|
FOuterJoin: Boolean;
|
|
FSkipInner: Boolean;
|
|
V : array[0..pred(ffcl_MaxIndexFlds)] of Variant;
|
|
VB : array[0..pred(ffcl_MaxIndexFlds)] of Variant; {!!.11}
|
|
procedure ReadSources;
|
|
function GetItem(Index: Integer): TFFSqlTableProxySubset;
|
|
function GetCount: Integer;
|
|
function ProcessLevel(Cookie1: TffWord32): Boolean;
|
|
procedure Clear;
|
|
function Insert(
|
|
TableProxySubset: TFFSqlTableProxySubset): TFFSqlTableProxySubset;
|
|
public
|
|
constructor Create(AOwner: TffSqlStatement);
|
|
destructor Destroy; override;
|
|
function Add(TableProxySubset: TFFSqlTableProxySubset): TFFSqlTableProxySubset;
|
|
procedure Delete(Index: Integer);
|
|
property Item[Index: Integer]: TFFSqlTableProxySubset read GetItem;
|
|
property Count: Integer read GetCount;
|
|
procedure Assign(const Source: TFFSqlTableProxySubsetList);
|
|
function RelationUsed(Relation: TffSqlCondFactor): Boolean;
|
|
function DependencyExists(Table : TFFSqlTableProxy): Boolean;
|
|
procedure Join(
|
|
CondTerm: TffSqlCondTerm;
|
|
CreateResultRecord: TFFObjectProc);
|
|
property RecordsRead : Longint read FRecordsRead;
|
|
property Owner: TffSqlStatement read FOwner;
|
|
property OuterJoin: Boolean read FOuterJoin write FOuterJoin;
|
|
property SkipInner: Boolean read FSkipInner write FSkipInner;
|
|
end;
|
|
|
|
TffSqlCondExp = class(TffSqlNode)
|
|
protected
|
|
CondTermList : TList;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
function GetCondTerm(Index: Integer): TffSqlCondTerm;
|
|
procedure SetCondTerm(Index: Integer; const Value: TffSqlCondTerm);
|
|
function GetCondTermCount: Integer;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
procedure ResetConstant; override;
|
|
function Reduce: Boolean;
|
|
function AsBooleanLevel(Level: Integer): Boolean;
|
|
procedure SetLevelDep(List: TFFSqlTableProxySubsetList);
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddCondTerm(Term : TffSqlCondTerm): TffSqlCondTerm;
|
|
property CondTermCount : Integer read GetCondTermCount;
|
|
property CondTerm[Index: Integer] : TffSqlCondTerm
|
|
read GetCondTerm write SetCondTerm;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function AsBoolean: Boolean;
|
|
function GetValue: Variant;
|
|
procedure BindHaving;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffComp = array[0..7] of Byte;
|
|
|
|
TffSqlFloatLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
SingleValue : single;
|
|
DoubleValue : double;
|
|
ExtendedValue : extended;
|
|
CompValue : TffComp;
|
|
CurrencyValue : currency;
|
|
Converted: Boolean;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : string read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlIntegerLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
Int32Value: Integer;
|
|
Converted: Boolean;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : string read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlStringLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
FType : TffFieldType;
|
|
Converted : Boolean;
|
|
CharValue : Char;
|
|
WideCharValue : WideChar;
|
|
ShortStringValue : ShortString;
|
|
ShortAnsiStringValue : ShortString;
|
|
NullStringValue : string;
|
|
NullAnsiStrValue : string;
|
|
WideStringValue : WideString;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
property Value : string read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlIntervalDef = (iUnspec, iYear, iMonth, iDay, iHour, iMinute, iSecond);
|
|
TffSqlIntervalLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
FStartDef : TffSqlIntervalDef;
|
|
FEndDef : TffSqlIntervalDef;
|
|
Y1, M1, D1, H1, S1 : Integer;
|
|
Converted: Boolean;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
function AddIntervalTo(Target: TDateTime): TDateTime;
|
|
function SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : string read FValue write FValue;
|
|
property StartDef : TffSqlIntervalDef read FStartDef write FStartDef;
|
|
property EndDef : TffSqlIntervalDef read FEndDef write FEndDef;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlTimestampLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
DateTimeValue: TDateTime;
|
|
Converted: Boolean;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : string read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlTimeLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
TimeValue : TDateTime;
|
|
Converted : Boolean;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : string read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlDateLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : string;
|
|
DateValue : TDateTime;
|
|
Converted : Boolean;
|
|
procedure ConvertToNative;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : string read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlBooleanLiteral = class(TffSqlNode)
|
|
protected
|
|
FValue : Boolean;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetType: TffFieldType; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Value : Boolean read FValue write FValue;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Boolean;
|
|
end;
|
|
|
|
TffSqlLiteral = class(TffSqlNode)
|
|
protected
|
|
FFloatLiteral: TffSqlFloatLiteral;
|
|
FIntegerLiteral: TffSqlIntegerLiteral;
|
|
FStringLiteral: TffSqlStringLiteral;
|
|
FDateLiteral : TffSqlDateLiteral;
|
|
FTimeLiteral : TffSqlTimeLiteral;
|
|
FTimeStampLiteral : TffSqlTimestampLiteral;
|
|
FIntervalLiteral : TffSqlIntervalLiteral;
|
|
FBooleanLiteral: TffSqlBooleanLiteral;
|
|
procedure Clear;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
function AddIntervalTo(Target: TDateTime): TDateTime;
|
|
function SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
destructor Destroy; override;
|
|
property BooleanLiteral : TffSqlBooleanLiteral
|
|
read FBooleanLiteral write FBooleanLiteral;
|
|
property FloatLiteral : TffSqlFloatLiteral
|
|
read FFloatLiteral write FFloatLiteral;
|
|
property IntegerLiteral : TffSqlIntegerLiteral
|
|
read FIntegerLiteral write FIntegerLiteral;
|
|
property StringLiteral : TffSqlStringLiteral
|
|
read FStringLiteral write FStringLiteral;
|
|
property DateLiteral : TffSqlDateLiteral
|
|
read FDateLiteral write FDateLiteral;
|
|
property TimeLiteral : TffSqlTimeLiteral
|
|
read FTimeLiteral write FTimeLiteral;
|
|
property TimeStampLiteral : TffSqlTimestampLiteral
|
|
read FTimestampLiteral write FTimestampLiteral;
|
|
property IntervalLiteral : TffSqlIntervalLiteral
|
|
read FIntervalLiteral write FIntervalLiteral;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue : Variant;
|
|
end;
|
|
|
|
TffSqlParam = class(TffSqlNode)
|
|
protected
|
|
FParmIndex: Integer;
|
|
function GetSize: Integer; override;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
function GetType: TffFieldType; override;
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetDecimals: Integer; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
property ParmIndex: Integer read FParmIndex;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
end;
|
|
|
|
TffSqlCoalesceExpression = class(TffSqlNode)
|
|
protected
|
|
ArgList : TList;
|
|
procedure Clear;
|
|
function GetArg(Index: Integer): TffSqlSimpleExpression;
|
|
function GetArgCount: Integer;
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
property ArgCount : Integer read GetArgCount;
|
|
property Arg[Index: Integer]: TffSqlSimpleExpression read GetArg;
|
|
function AddArg(Value: TffSqlSimpleExpression): TffSqlSimpleExpression;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlWhenClause = class(TffSqlNode)
|
|
protected
|
|
FWhenExp : TffSqlCondExp;
|
|
FThenExp : TffSqlSimpleExpression;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property WhenExp : TffSqlCondExp read FWhenExp write FWhenExp;
|
|
property ThenExp : TffSqlSimpleExpression read FThenExp write FThenExp;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlWhenClauseList = class(TffSqlNode)
|
|
protected
|
|
WhenClauseList : TList;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
function GetWhenClause(Index: Integer): TffSqlWhenClause;
|
|
function GetWhenClauseCount: Integer;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
property WhenClauseCount : Integer read GetWhenClauseCount;
|
|
property WhenClause[Index: Integer]: TffSqlWhenClause read GetWhenClause;
|
|
function AddWhenClause(Value: TffSqlWhenClause): TffSqlWhenClause;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlCaseExpression = class(TffSqlNode)
|
|
protected
|
|
FWhenClauseList : TffSqlWhenClauseList;
|
|
FElseExp : TffSqlSimpleExpression;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
procedure CheckIsConstant;
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
function IsConstant: Boolean;
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property WhenClauseList : TffSqlWhenClauseList
|
|
read FWhenClauseList write FWhenClauseList;
|
|
property ElseExp : TffSqlSimpleExpression read FElseExp write FElseExp;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlScalarFunction = (sfCase, sfCharlen, sfCoalesce, sfCurrentDate, sfCurrentTime,
|
|
sfCurrentTimestamp, sfCurrentUser, sfLower, sfUpper, sfPosition,
|
|
sfSessionUser, sfSubstring, sfSystemUser, sfTrim, sfExtract, sfNullIf,
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfPower, sfRand, sfRound); {!!.11}
|
|
TffSqlLTB = (ltbBoth, ltbLeading, ltbTrailing);
|
|
TffSqlScalarFunc = class(TffSqlNode)
|
|
protected
|
|
FSQLFunction : TffSqlScalarFunction;
|
|
FArg1 : TffSqlSimpleExpression;
|
|
FArg2 : TffSqlSimpleExpression;
|
|
FArg3 : TffSqlSimpleExpression;
|
|
FLTB : TffSqlLTB;
|
|
FXDef : TffSqlIntervalDef;
|
|
FCaseExp : TffSqlCaseExpression;
|
|
FCoalesceExp : TffSqlCoalesceExpression;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
FType : TffFieldType;
|
|
TypeKnown : Boolean;
|
|
ConstantValue: Variant;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
function IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
procedure CheckType;
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property SQLFunction : TffSqlScalarFunction
|
|
read FSQLFunction write FSQLFunction;
|
|
property Arg1 : TffSqlSimpleExpression read FArg1 write FArg1;
|
|
property Arg2 : TffSqlSimpleExpression read FArg2 write FArg2;
|
|
property Arg3 : TffSqlSimpleExpression read FArg3 write FArg3;
|
|
property LTB : TffSqlLTB read FLTB write FLTB;
|
|
property XDef : TffSqlIntervalDef read FXDef write FXDef;
|
|
property CaseExp : TffSqlCaseExpression read FCaseExp write FCaseExp;
|
|
property CoalesceExp : TFFSqlCoalesceExpression
|
|
read FCoalesceExp write FCoalesceExp;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlMulOp = (moMul, moDiv);
|
|
TffSqlFactor = class(TffSqlNode)
|
|
protected
|
|
TypeKnown : Boolean;
|
|
FType : TffFieldType;
|
|
FMulOp: TffSqlMulOp;
|
|
FUnaryMinus : Boolean;
|
|
FCondExp: TffSqlCondExp;
|
|
FFieldRef: TffSqlFieldRef;
|
|
FLiteral: TffSqlLiteral;
|
|
FParam: TffSqlParam;
|
|
FAggregate : TffSqlAggregate;
|
|
FSubQuery : TffSqlSELECT;
|
|
FScalarFunc : TffSqlScalarFunc;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
procedure CheckType;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function IsAggregate: Boolean; override;
|
|
function AddIntervalTo(Target: TDateTime): TDateTime;
|
|
function Reduce: Boolean;
|
|
function SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
destructor Destroy; override;
|
|
property MulOp :TffSqlMulOp read FMulOp write FMulOp;
|
|
property UnaryMinus : Boolean read FUnaryMinus write FUnaryMinus;
|
|
property CondExp : TffSqlCondExp read FCondExp write FCondExp;
|
|
property FieldRef : TffSqlFieldRef read FFieldRef write FFieldRef;
|
|
property Literal : TffSqlLiteral read FLiteral write FLiteral;
|
|
property Param : TffSqlParam read FParam write FParam;
|
|
property Aggregate : TffSqlAggregate read FAggregate write FAggregate;
|
|
property SubQuery : TffSqlSELECT read FSubQuery write FSubQuery;
|
|
property ScalarFunc : TffSqlScalarFunc read FScalarFunc write FScalarFunc;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
function HasFieldRef: Boolean;
|
|
function IsField(var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
function IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy; var SameCase: Boolean): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function IsNull: Boolean;
|
|
function WasWildcard : Boolean; {!!.11}
|
|
end;
|
|
|
|
TffSqlAddOp = (aoPlus, aoMinus, aoConcat);
|
|
TffSqlTerm = class(TffSqlNode)
|
|
protected
|
|
TypeKnown : Boolean;
|
|
FType : TffFieldType;
|
|
FAddOp: TffSqlAddOp;
|
|
FactorList : TList;
|
|
FIsConstantChecked: Boolean;
|
|
FIsConstant: Boolean;
|
|
ConstantValue: Variant;
|
|
procedure Clear;
|
|
procedure CheckIsConstant;
|
|
function IsConstant: Boolean;
|
|
function GetFactor(Index: Integer): TffSqlFactor;
|
|
procedure SetFactor(Index: Integer; const Value: TffSqlFactor);
|
|
function GetFactorCount: Integer;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
procedure CheckType;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function IsAggregate: Boolean; override;
|
|
//function GetAgg: TffSqlAggregate; override;
|
|
function AddIntervalTo(Target: TDateTime): TDateTime;
|
|
function SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddFactor(Factor: TffSqlFactor): TffSqlFactor;
|
|
property FactorCount : Integer read GetFactorCount;
|
|
property Factor[Index: Integer] : TffSqlFactor read GetFactor write SetFactor;
|
|
property AddOp :TffSqlAddOp read FAddOp write FAddOp;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
function HasFieldRef: Boolean;
|
|
function IsField(var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
function IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy; var SameCase: Boolean): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function IsAggregateExpression: Boolean;
|
|
function IsNull: Boolean;
|
|
function WasWildcard : Boolean; {!!.11}
|
|
end;
|
|
|
|
TffSqlSimpleExpression = class(TffSqlNode)
|
|
protected
|
|
TypeKnown : Boolean;
|
|
FType : TffFieldType;
|
|
BoundHaving : Boolean;
|
|
BoundHavingField : TFFSqlFieldProxy;
|
|
FIsConstant : Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
ConstantValue: Variant;
|
|
BindingHaving: Boolean;
|
|
procedure BindHaving;
|
|
procedure Clear;
|
|
function ConcatBLOBValues(const Value1, Value2 : Variant) : Variant; {!!.13}
|
|
function GetTerm(Index: Integer): TffSqlTerm;
|
|
procedure SetTerm(Index: Integer; const Value: TffSqlTerm);
|
|
function GetTermCount: Integer;
|
|
function GetSize: Integer; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetType: TffFieldType; override;
|
|
procedure CheckType;
|
|
function GetTitle(const Qualified : boolean): string; {!!.11}
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function IsAggregate: Boolean; override;
|
|
function IsConstant: Boolean;
|
|
function IsParameter: Boolean;
|
|
procedure CheckIsConstant;
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
protected
|
|
TermList : TList;
|
|
public
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddTerm(Term : TffSqlTerm): TffSqlTerm;
|
|
property TermCount : Integer read GetTermCount;
|
|
property Term[Index: Integer] : TffSqlTerm read GetTerm write SetTerm;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetValue: Variant;
|
|
function HasFieldRef: Boolean;
|
|
function IsField(var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
function IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy; var SameCase: Boolean): Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function IsAggregateExpression: Boolean;
|
|
function IsNull: Boolean;
|
|
function WasWildcard : Boolean; {!!.11}
|
|
end;
|
|
|
|
TffSqlSimpleExpressionList = class(TffSqlNode)
|
|
protected
|
|
FExpressionList : TList;
|
|
FIsConstant: Boolean;
|
|
FIsConstantChecked: Boolean;
|
|
procedure CheckIsConstant;
|
|
procedure Clear;
|
|
function IsConstant: Boolean;
|
|
function GetExpression(Index: Integer): TffSqlSimpleExpression;
|
|
function GetExpressionCount: Integer;
|
|
procedure SetExpression(Index: Integer;
|
|
const Value: TffSqlSimpleExpression);
|
|
procedure MatchType(ExpectedType: TffFieldType);
|
|
function Contains(const TestValue: Variant): Boolean;
|
|
function Reduce: Boolean;
|
|
procedure ResetConstant; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddExpression(Expression: TffSqlSimpleExpression):
|
|
TffSqlSimpleExpression;
|
|
property ExpressionCount : Integer read GetExpressionCount;
|
|
property Expression[Index: Integer] : TffSqlSimpleExpression
|
|
read GetExpression write SetExpression;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
end;
|
|
|
|
TffSqlSelection = class(TffSqlNode)
|
|
protected
|
|
FColumn: TffSqlColumn;
|
|
FSimpleExpression: TffSqlSimpleExpression;
|
|
AddedByWildcard: Boolean;
|
|
procedure AddColumnDef(Target: TffSqlColumnListOwner); override;
|
|
function GetIndex: Integer;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
property SimpleExpression : TffSqlSimpleExpression
|
|
read FSimpleExpression write FSimpleExpression;
|
|
property Column : TffSqlColumn read FColumn write FColumn;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
property Index: Integer read GetIndex;
|
|
function IsAggregateExpression: Boolean;
|
|
end;
|
|
|
|
TffSqlSelectionList = class(TffSqlNode)
|
|
protected
|
|
FSelections : TList;
|
|
procedure Clear;
|
|
function GetSelection(Index: Integer): TffSqlSelection;
|
|
procedure SetSelection(Index: Integer;
|
|
const Value: TffSqlSelection);
|
|
function GetSelectionCount: Integer;
|
|
function Reduce: Boolean;
|
|
// procedure ResetConstant; override;
|
|
function GetNonWildSelection(Index: Integer): TffSqlSelection;
|
|
property NonWildSelection[Index: Integer]: TffSqlSelection
|
|
read GetNonWildSelection;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddSelection(NewSelection: TffSqlSelection): TffSqlSelection;
|
|
procedure InsertSelection(Index: Integer; NewSelection: TffSqlSelection);
|
|
property SelectionCount : Integer read GetSelectionCount;
|
|
property Selection[Index: Integer]: TffSqlSelection
|
|
read GetSelection write SetSelection;
|
|
function FindSelection(GroupCol : TffSqlGroupColumn): TffSqlSelection;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function NonWildSelectionCount: Integer;
|
|
end;
|
|
|
|
TffSqlInsertColumnList = class;
|
|
|
|
TffSqlTableRef = class(TffSqlNode)
|
|
protected
|
|
FAlias : string;
|
|
FTableName : string;
|
|
FTableExp: TffSqlTableExp;
|
|
FColumnList: TFFSqlInsertColumnList;
|
|
FDatabaseName: string;
|
|
FTable: TffSqlTableProxy;
|
|
procedure AddTableReference(Select: TffSqlSELECT); override;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
procedure Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
function GetResultTable: TFFSqlTableProxy;
|
|
function GetSQLName: string;
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
function BindTable(AOwner: TObject; const TableName: string): TFFSqlTableProxy;
|
|
function Reduce: Boolean; {!!.11}
|
|
function TargetFieldFromSourceField(const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property TableName : string read FTableName write FTableName;
|
|
property DatabaseName: string read FDatabaseName write FDatabaseName;
|
|
property Alias : string read FAlias write FAlias;
|
|
property TableExp: TffSqlTableExp read FTableExp write FTableExp;
|
|
property ColumnList : TFFSqlInsertColumnList
|
|
read FColumnList write FColumnList;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Clear;
|
|
destructor Destroy; override;
|
|
property SQLName: string read GetSQLName;
|
|
function GetTable(AOwner: TObject; const ExclContLock : Boolean): TffSqlTableProxy;
|
|
property ResultTable: TFFSqlTableProxy read GetResultTable;
|
|
end;
|
|
|
|
TffSqlTableRefList = class(TffSqlNode)
|
|
protected
|
|
FTableRefList : TList;
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
procedure Clear;
|
|
function GetTableRef(Index: Integer): TffSqlTableRef;
|
|
procedure SetTableRef(Index: Integer;
|
|
const Value: TffSqlTableRef);
|
|
function GetTableRefCount: Integer;
|
|
function Reduce: Boolean;
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddTableRef(NewTableRef: TffSqlTableRef): TffSqlTableRef;
|
|
function GetNameForAlias(const Alias : string) : string;
|
|
property TableRefCount : Integer read GetTableRefCount;
|
|
property TableRef[Index: Integer]: TffSqlTableRef
|
|
read GetTableRef write SetTableRef;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function GetFieldsFromTable(const TableName: string; List: TList): TffSqlTableProxy;
|
|
end;
|
|
|
|
TffSqlOrderItem = class(TffSqlNode)
|
|
protected
|
|
FColumn: TFFSqlOrderColumn;
|
|
FIndex: string;
|
|
FDescending: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
property Column: TFFSqlOrderColumn read FColumn write FColumn;
|
|
property Index: string read FIndex write FIndex;
|
|
property Descending: Boolean read FDescending write FDescending;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
end;
|
|
|
|
TffSqlOrderList = class(TffSqlNode)
|
|
protected
|
|
FOrderItemList : TList;
|
|
procedure Clear;
|
|
function GetOrderItem(Index: Integer): TffSqlOrderItem;
|
|
procedure SetOrderItem(Index: Integer;
|
|
const Value: TffSqlOrderItem);
|
|
function GetOrderCount: Integer;
|
|
function Reduce: Boolean;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddOrderItem(NewOrder: TffSqlOrderItem): TffSqlOrderItem;
|
|
property OrderCount : Integer read GetOrderCount;
|
|
property OrderItem[Index: Integer]: TffSqlOrderItem
|
|
read GetOrderItem write SetOrderItem;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
end;
|
|
|
|
TffSqlOuterJoinMode = (jmNone, jmLeft, jmRight, jmFull);
|
|
TffSqlJoiner = class(TffObject)
|
|
protected
|
|
FSources : TFFSqlTableProxySubsetList;
|
|
FTargetTable : TFFSqlTableProxy;
|
|
Level : integer;
|
|
FRecordsRead : Longint;
|
|
{$IFDEF CountWrites}
|
|
FRecordsWritten : Longint;
|
|
{$ENDIF}
|
|
FieldCopier : TffFieldCopier;
|
|
FSX, FT : TList;
|
|
FCondExpWhere: TffSqlCondExp;
|
|
RecListL, RecListR,
|
|
DupList : TffNRecordHash;
|
|
FirstCondTerm, LastCondTerm : Boolean;
|
|
OptimizeCalled: Boolean;
|
|
WasOptimized: Boolean;
|
|
P: procedure of object;
|
|
FOwner: TffSqlStatement;
|
|
procedure CreateResultRecord;
|
|
function ProcessLevel(Cookie1: TffWord32): Boolean;
|
|
procedure ReadSources;
|
|
function FindRelation(Term: TffSqlCondTerm; CurFactor,
|
|
CurFactor2: TffSqlCondFactor; Table : TFFSqlTableProxy;
|
|
TargetField : TFFSqlFieldProxy;
|
|
var Operator: TffSqlRelOp;
|
|
var ArgExpression: TffSqlSimpleExpression;
|
|
var SameCase: Boolean): TffSqlCondFactor;
|
|
procedure Optimize(UseIndex: Boolean);
|
|
function WriteNull(Cookie: TffWord32): Boolean;
|
|
public
|
|
constructor Create(AOwner: TffSqlStatement; CondExp: TffSqlCondExp);
|
|
destructor Destroy; override;
|
|
procedure Execute(UseIndex: Boolean; LoopProc: TFFObjectProc;
|
|
OuterJoinMode: TffSqlOuterJoinMode);
|
|
property Sources : TFFSqlTableProxySubsetList read FSources;
|
|
procedure AddColumn(
|
|
SourceExpression: TffSqlSimpleExpression;
|
|
SourceField : TffSqlFieldProxy;
|
|
Target: TFFSqlFieldProxy);
|
|
procedure ClearColumnList;
|
|
property RecordsRead : Longint read FRecordsRead;
|
|
{$IFDEF CountWrites}
|
|
property RecordsWritten: Longint read FRecordsWritten;
|
|
{$ENDIF}
|
|
property CondExpWhere : TffSqlCondExp read FCondExpWhere write FCondExpWhere;
|
|
property Target : TFFSqlTableProxy read FTargetTable write FTargetTable;
|
|
property Owner: TffSqlStatement read FOwner;
|
|
end;
|
|
|
|
TffSqlColumnListOwner = class(TffSqlNode)
|
|
protected
|
|
T : TffSqlTableProxy; {!!.11}
|
|
Columns : TStringList;
|
|
public
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TffSqlJoinTableExp = class;
|
|
TffSqlNonJoinTableExp = class;
|
|
|
|
TffSqlTableExp = class(TffSqlNode)
|
|
protected
|
|
FJoinTableExp: TffSqlJoinTableExp;
|
|
FNonJoinTableExp: TffSqlNonJoinTableExp;
|
|
FNestedTableExp: TffSqlTableExp;
|
|
procedure EnsureResultTable(NeedData: Boolean);
|
|
function CheckNoDups: Boolean;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
function TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
public
|
|
function GetResultTable: TFFSqlTableProxy;
|
|
property JoinTableExp: TffSqlJoinTableExp
|
|
read FJoinTableExp write FJoinTableExp;
|
|
property NonJoinTableExp: TffSqlNonJoinTableExp
|
|
read FNonJoinTableExp write FNonJoinTableExp;
|
|
property NestedTableExp: TffSqlTableExp
|
|
read FNestedTableExp write FNestedTableExp;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Clear;
|
|
destructor Destroy; override;
|
|
procedure Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
function Reduce: Boolean;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
property ResultTable: TFFSqlTableProxy read GetResultTable;
|
|
function GetFieldsFromTable(const TableName: string; List: TList):
|
|
TffSqlTableProxy; {!!.11}
|
|
end;
|
|
|
|
TFFSqlUsingItem = class(TffSqlNode)
|
|
protected
|
|
FColumnName: string;
|
|
public
|
|
property ColumnName: string read FColumnName write FColumnName;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
end;
|
|
|
|
TffSqlUsingList = class(TffSqlNode)
|
|
protected
|
|
FUsingItemList : TList;
|
|
procedure Clear;
|
|
function GetUsingItem(Index: Integer): TffSqlUsingItem;
|
|
procedure SetUsingItem(Index: Integer;
|
|
const Value: TffSqlUsingItem);
|
|
function GetUsingCount: Integer;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddItem(NewUsing: TffSqlUsingItem): TffSqlUsingItem;
|
|
property UsingCount : Integer read GetUsingCount;
|
|
property UsingItem[Index: Integer]: TffSqlUsingItem
|
|
read GetUsingItem write SetUsingItem;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
end;
|
|
|
|
TffSqlJoinType = (jtCross, jtInner, jtLeftOuter, jtRightOuter,
|
|
jtFullOuter, jtUnion);
|
|
TffSqlJoinTableExp = class(TffSqlNode)
|
|
protected
|
|
FTableRef1: TffSqlTableRef;
|
|
FTableRef2: TffSqlTableRef;
|
|
FCondExp: TFFSqlCondExp;
|
|
FJoinType: TffSqlJoinType;
|
|
FNatural: Boolean;
|
|
Bound: Boolean;
|
|
TL, TR : TffSqlTableProxy;
|
|
Columns: TStringList;
|
|
Joiner : TffSqlJoiner;
|
|
FUsingList: TFFSqlUsingList;
|
|
UsingCondExp: TFFSqlCondExp;
|
|
FResultTable : TFFSqlTableProxy;
|
|
HaveData: Boolean;
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
function GetResultTable: TffSqlTableProxy;
|
|
function Execute2(NeedData: Boolean): TffSqlTableProxy;
|
|
procedure Bind;
|
|
procedure ClearBindings(Node: TffSqlNode);
|
|
procedure ClearColumns;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function DoJoin(NeedData: Boolean): TffSqlTableProxy;
|
|
function BuildSimpleFieldExpr(AOwner: TffSqlNode; const ATableName,
|
|
AFieldName: string;
|
|
AField: TffSqlFieldProxy): TffSqlSimpleExpression;
|
|
procedure EnsureResultTable(NeedData: Boolean);
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
function TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
public
|
|
function BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy; override;
|
|
property JoinType: TffSqlJoinType read FJoinType write FJoinType;
|
|
property Natural: Boolean read FNatural write FNatural;
|
|
property TableRef1: TffSqlTableRef read FTableRef1 write FTableRef1;
|
|
property TableRef2: TffSqlTableRef read FTableRef2 write FTableRef2;
|
|
property CondExp: TFFSqlCondExp read FCondExp write FCondExp;
|
|
property UsingList : TFFSqlUsingList read FUsingList write FUsingList;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Clear;
|
|
destructor Destroy; override;
|
|
procedure Execute(var aLiveResult: Boolean;
|
|
var aCursorID: TffCursorID; var RecordsRead: Integer);
|
|
function Reduce: Boolean;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
property ResultTable: TffSqlTableProxy read GetResultTable;
|
|
function GetFieldsFromTable(const TableName: string; List: TList):
|
|
TffSqlTableProxy; {!!.11}
|
|
end;
|
|
|
|
TffSqlValueList = class;
|
|
|
|
TffSqlNonJoinTablePrimary = class(TffSqlNode)
|
|
protected
|
|
FSelectSt: TFFSqlSELECT;
|
|
FValueList: TffSqlValueList;
|
|
FNonJoinTableExp: TffSqlNonJoinTableExp;
|
|
FTableRef: TffSqlTableRef;
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
procedure EnsureResultTable(NeedData: Boolean);
|
|
function GetResultTable: TffSqlTableProxy;
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
function TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
public
|
|
destructor Destroy; override;
|
|
property SelectSt: TFFSqlSELECT read FSelectSt write FSelectSt;
|
|
property ValueList: TffSqlValueList read FValueList write FValueList;
|
|
property NonJoinTableExp: TffSqlNonJoinTableExp
|
|
read FNonJoinTableExp write FNonJoinTableExp;
|
|
property TableRef: TffSqlTableRef read FTableRef write FTableRef;
|
|
procedure Clear;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
function Reduce: Boolean;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
property ResultTable: TffSqlTableProxy read GetResultTable;
|
|
end;
|
|
|
|
TffSqlNonJoinTableTerm = class(TffSqlNode)
|
|
protected
|
|
FNonJoinTablePrimary: TffSqlNonJoinTablePrimary;
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
procedure EnsureResultTable(NeedData: Boolean);
|
|
function GetResultTable: TffSqlTableProxy;
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
function TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
public
|
|
property NonJoinTablePrimary: TffSqlNonJoinTablePrimary
|
|
read FNonJoinTablePrimary write FNonJoinTablePrimary;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Clear;
|
|
destructor Destroy; override;
|
|
procedure Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
function Reduce: Boolean;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
property ResultTable: TffSqlTableProxy read GetResultTable;
|
|
end;
|
|
|
|
TffSqlNonJoinTableExp = class(TffSqlNode)
|
|
protected
|
|
FNonJoinTableTerm: TffSqlNonJoinTableTerm;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
function GetResultTable: TffSqlTableProxy;
|
|
procedure EnsureResultTable(NeedData: Boolean);
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
function BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
function TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
public
|
|
property NonJoinTableTerm: TffSqlNonJoinTableTerm
|
|
read FNonJoinTableTerm write FNonJoinTableTerm;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure Clear;
|
|
destructor Destroy; override;
|
|
procedure Execute(var aLiveResult: Boolean;
|
|
var aCursorID: TffCursorID; var RecordsRead: Integer);
|
|
function Reduce: Boolean;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
property ResultTable: TffSqlTableProxy read GetResultTable;
|
|
function GetFieldsFromTable(const TableName: string; List: TList):
|
|
TffSqlTableProxy; {!!.11}
|
|
end;
|
|
|
|
TffSqlValueItem = class(TffSqlNode)
|
|
protected
|
|
FDefault : Boolean;
|
|
FSimplex: TffSqlSimpleExpression;
|
|
function GetType: TffFieldType; override;
|
|
function GetSize: Integer; override;
|
|
function GetDecimals: Integer; override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
property Default : Boolean read FDefault write FDefault;
|
|
property Simplex: TffSqlSimpleExpression read FSimplex write FSimplex;
|
|
end;
|
|
|
|
TffSqlValueList = class(TffSqlNode)
|
|
protected
|
|
FValueItemList : TList;
|
|
FResultTable: TFFSqlTableProxy;
|
|
procedure Clear;
|
|
function GetValueItem(Index: Integer): TffSqlValueItem;
|
|
procedure SetValueItem(Index: Integer;
|
|
const Value: TffSqlValueItem);
|
|
function GetValueCount: Integer;
|
|
function GetResultTable: TFFSqlTableProxy;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddItem(NewValue: TffSqlValueItem): TffSqlValueItem;
|
|
property ValueCount : Integer read GetValueCount;
|
|
property ValueItem[Index: Integer]: TffSqlValueItem
|
|
read GetValueItem write SetValueItem;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
function Reduce: Boolean;
|
|
property ResultTable: TFFSqlTableProxy read GetResultTable;
|
|
end;
|
|
|
|
TffSqlSELECT = class(TffSqlColumnListOwner)
|
|
protected
|
|
FDistinct : Boolean;
|
|
FSelectionList : TffSqlSelectionList;
|
|
FTableRefList : TffSqlTableRefList;
|
|
FGroupColumnList : TffSqlGroupColumnList;
|
|
FCondExpWhere: TffSqlCondExp;
|
|
FCondExpHaving: TffSqlCondExp;
|
|
FOrderList : TffSqlOrderList;
|
|
FGrpTable : TffSqlTableProxy;
|
|
AggList : TList;
|
|
FResultTable : TFFSqlTableProxy;
|
|
TablesReferencedByOrder : TStringList;
|
|
TableAliases : TStringList;
|
|
HaveAggregates : Boolean;
|
|
AggQueryMode : TffSqlAggQueryMode;
|
|
HavingTable: TffSqlTableProxy;
|
|
IsDependent: Boolean;
|
|
Bound: Boolean;
|
|
Joiner : TffSqlJoiner;
|
|
FInWhere: Boolean;
|
|
WasStar: Boolean;
|
|
HaveData: Boolean;
|
|
RequestLive: Boolean;
|
|
TypeKnown: Boolean;
|
|
FType: TffFieldType;
|
|
FDecimals: Integer;
|
|
FSize: Integer; {!!.13}
|
|
BindingDown: Boolean; {!!.11}
|
|
procedure AddTableFields(Table : TffSqlTableProxy;
|
|
const StartPoint : Integer;
|
|
FieldRef : TffSqlFieldRef);
|
|
procedure AddTableFieldsFromList(Table : TffSqlTableProxy;
|
|
const StartPoint : Integer;
|
|
FieldRef : TffSqlFieldRef;
|
|
List: TList); {!!.11}
|
|
procedure Bind;
|
|
function BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
procedure AddTableRefs(Node: TffSqlNode);
|
|
procedure AddColumns(Node: TffSqlNode);
|
|
procedure BuildSortList(Table: TffSqlTableProxy;
|
|
var SortList: TffSqlSortArray); {!!.11}
|
|
procedure DoGroupCopy(GroupColumnsIn: Integer; AggExpList,
|
|
GroupColumnTargetField: TList);
|
|
procedure DoAggOrderBy;
|
|
procedure DoHaving;
|
|
procedure DoSortOnAll;
|
|
procedure DoRemoveDups(NeedData: Boolean);
|
|
procedure DoBuildGroupingTable(GroupColumnsIn : Integer; FSF, FSX,
|
|
GroupColumnTargetField: TList);
|
|
procedure DoOrderBy(NeedData: Boolean; Table: TffSqlTableProxy);
|
|
procedure DoCheckAggregates;
|
|
function CheckAnyValue(RelOp: TffSqlRelOp;
|
|
const Val: Variant): Boolean;
|
|
function CheckAllValues(RelOp: TffSqlRelOp;
|
|
const Val: Variant): Boolean;
|
|
{procedure CheckTableList;} {!!.12 debug code}
|
|
procedure Clear;
|
|
procedure ClearBindings(Node: TffSqlNode);
|
|
procedure ResetIsConstant(Node: TffSqlNode);
|
|
procedure FlagAggregates(Node: TffSqlNode);
|
|
procedure EnumAggregates(Node: TffSqlNode);
|
|
function BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy; override;
|
|
function FindField(const FieldName: string): TFFSqlFieldProxy;
|
|
procedure ExpandWildcards;
|
|
procedure MatchType(ExpectedType: TffFieldType; AllowMultiple: Boolean);
|
|
function NormalQueryResult(NeedData: Boolean): TffSqlTableProxy;
|
|
function CheckForValue(Value: Variant):Boolean;
|
|
function Match(Value: Variant; Unique: Boolean;
|
|
MatchOption: TffSqlMatchOption): Boolean;
|
|
function AggregateQueryResult(NeedData: Boolean): TffSqlTableProxy;
|
|
function CheckHaving: Boolean;
|
|
function Execute2(NeedData: Boolean): TffSqlTableProxy;
|
|
procedure EnsureResultTable(NeedData: Boolean);
|
|
procedure ClearTableList;
|
|
function Reduce: Boolean;
|
|
function GetValue: Variant;
|
|
function CheckNonEmpty: Boolean;
|
|
function IsSubQuery: Boolean;
|
|
function GetType: TffFieldType; override;
|
|
function GetDecimals: Integer; override;
|
|
function GetSize: Integer; override; {!!.13}
|
|
function GetResultTable: TFFSqlTableProxy;
|
|
function TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
function TableWithCount(const ColumnName: string): TffSqlTableProxy; {!!.12}
|
|
public
|
|
property InWhere: Boolean read FInWhere write FInWhere; //used only during parsing
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
property Distinct: Boolean read FDistinct write FDistinct;
|
|
property SelectionList : TffSqlSelectionList
|
|
read FSelectionList write FSelectionList;
|
|
property TableRefList : TffSqlTableRefList
|
|
read FTableRefList write FTableRefList;
|
|
property CondExpWhere : TffSqlCondExp read FCondExpWhere write FCondExpWhere;
|
|
property GroupColumnList : TffSqlGroupColumnList
|
|
read FGroupColumnList write FGroupColumnList;
|
|
property CondExpHaving : TffSqlCondExp
|
|
read FCondExpHaving write FCondExpHaving;
|
|
property OrderList : TffSqlOrderList read FOrderList write FOrderList;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
procedure Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
property ResultTable : TFFSqlTableProxy read GetResultTable;
|
|
end;
|
|
|
|
TffSqlInsertItem = class(TffSqlNode)
|
|
protected
|
|
FColumnName: string;
|
|
procedure AddColumnDef(Target: TffSqlColumnListOwner); override;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property ColumnName: string read FColumnName write FColumnName;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
end;
|
|
|
|
TffSqlInsertColumnList = class(TffSqlNode)
|
|
protected
|
|
FInsertColumnItemList : TList;
|
|
procedure Clear;
|
|
function GetInsertColumnItem(Index: Integer): TffSqlInsertItem;
|
|
procedure SetInsertColumnItem(Index: Integer;
|
|
const Value: TffSqlInsertItem);
|
|
function GetInsertColumnCount: Integer;
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddItem(NewInsertColumn: TffSqlInsertItem): TffSqlInsertItem;
|
|
property InsertColumnCount : Integer read GetInsertColumnCount;
|
|
property InsertColumnItem[Index: Integer]: TffSqlInsertItem
|
|
read GetInsertColumnItem write SetInsertColumnItem;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
end;
|
|
|
|
TffSqlINSERT = class(TffSqlColumnListOwner)
|
|
protected
|
|
FTableName: string;
|
|
FInsertColumnList: TFFSqlInsertColumnList;
|
|
FDefaultValues: Boolean;
|
|
Bound: Boolean;
|
|
// T : TffSqlTableProxy; {!!.11}
|
|
FTableExp: TffSqlTableExp;
|
|
procedure AddColumns(Node: TffSqlNode);
|
|
procedure Bind;
|
|
procedure ClearBindings(Node: TffSqlNode);
|
|
function Reduce: Boolean; {!!.11}
|
|
public
|
|
destructor Destroy; override;
|
|
property TableName : string read FTableName write FTableName;
|
|
property InsertColumnList: TFFSqlInsertColumnList
|
|
read FInsertColumnList write FInsertColumnList;
|
|
property TableExp: TffSqlTableExp read FTableExp write FTableExp;
|
|
property DefaultValues: Boolean read FDefaultValues write FDefaultValues;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure Clear;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function Execute(var RowsAffected: Integer) : TffResult; {!!.11}
|
|
end;
|
|
|
|
TffSqlDELETE = class(TffSqlColumnListOwner) {!!.11}
|
|
protected
|
|
FTableRef: TffSqlTableRef;
|
|
FCondExpWhere: TffSqlCondExp;
|
|
Bound: Boolean;
|
|
// T : TffSqlTableProxy; {!!.11}
|
|
Joiner : TffSqlJoiner;
|
|
DeleteList: TList;
|
|
procedure Bind;
|
|
function BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy; override;
|
|
procedure DeleteRecord;
|
|
function Reduce: Boolean; {!!.11}
|
|
public
|
|
destructor Destroy; override;
|
|
property TableRef: TffSqlTableRef read FTableRef write FTableRef;
|
|
property CondExpWhere : TffSqlCondExp
|
|
read FCondExpWhere write FCondExpWhere;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure Clear;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function Execute(var RowsAffected: Integer) : TffResult; {!!.11}
|
|
end;
|
|
|
|
TffSqlUpdateItem = class(TffSqlNode)
|
|
protected
|
|
FSimplex: TffSqlSimpleExpression;
|
|
FColumnName: string;
|
|
FDefault: Boolean;
|
|
F: TffSqlFieldProxy;
|
|
procedure AddColumnDef(Target: TffSqlColumnListOwner); override;
|
|
function Reduce: Boolean; {!!.11}
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
destructor Destroy; override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
property ColumnName: string read FColumnName write FColumnName;
|
|
property Default: Boolean read FDefault write FDefault;
|
|
property Simplex: TffSqlSimpleExpression read FSimplex write FSimplex;
|
|
procedure Update;
|
|
end;
|
|
|
|
TffSqlUpdateList = class(TffSqlNode)
|
|
protected
|
|
FUpdateItemList : TList;
|
|
procedure Clear;
|
|
function GetUpdateItem(Index: Integer): TffSqlUpdateItem;
|
|
function GetUpdateCount: Integer;
|
|
function Reduce: Boolean; {!!.11}
|
|
public
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
constructor Create(AParent: TffSqlNode);
|
|
destructor Destroy; override;
|
|
function AddItem(NewValue: TffSqlUpdateItem): TffSqlUpdateItem;
|
|
property UpdateCount : Integer read GetUpdateCount;
|
|
property UpdateItem[Index: Integer]: TffSqlUpdateItem
|
|
read GetUpdateItem;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function Update : TffResult; {!!.11}
|
|
end;
|
|
|
|
TffSqlUPDATE = class(TffSqlColumnListOwner)
|
|
protected
|
|
FTableRef: TffSqlTableRef;
|
|
FCondExpWhere: TffSqlCondExp;
|
|
FUpdateList: TFFSqlUpdateList;
|
|
Bound: Boolean;
|
|
// T : TffSqlTableProxy; {!!.11}
|
|
Joiner : TffSqlJoiner;
|
|
FRowsAffected: Integer;
|
|
UpdateRecList: TList;
|
|
procedure AddColumns(Node: TffSqlNode);
|
|
procedure Bind;
|
|
function BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy; override;
|
|
procedure ClearBindings(Node: TffSqlNode);
|
|
function Reduce: Boolean; {!!.11}
|
|
procedure UpdateRecord;
|
|
public
|
|
destructor Destroy; override;
|
|
property TableRef: TffSqlTableRef read FTableRef write FTableRef;
|
|
property CondExpWhere : TffSqlCondExp read FCondExpWhere write FCondExpWhere;
|
|
property UpdateList: TFFSqlUpdateList read FUpdateList write FUpdateList;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure Clear;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
function Execute(var RowsAffected: Integer) : TffResult; {!!.11}
|
|
end;
|
|
|
|
TffSqlStatement = class(TffSqlNode)
|
|
protected
|
|
FClientID: TffClientID;
|
|
FSessionID: TffSessionID;
|
|
FInsert: TffSqlINSERT;
|
|
StartDate,
|
|
StartDateTime,
|
|
StartTime : TDateTime;
|
|
ParmCount : Integer;
|
|
ParmList : TFFVariantList;
|
|
FUseIndex: Boolean;
|
|
FUpdate: TffSqlUPDATE;
|
|
FDelete: TffSqlDELETE;
|
|
FReduce: Boolean;
|
|
FDatabase : TffSqlDatabaseProxy;
|
|
RecordsRead: Integer;
|
|
FTableExp: TffSqlTableExp;
|
|
public
|
|
property UseIndex: Boolean read FUseIndex write FUseIndex;
|
|
property Reduce: Boolean read FReduce write FReduce;
|
|
procedure Assign(const Source: TffSqlNode); override;
|
|
procedure EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean); override;
|
|
property Insert: TffSqlINSERT read FInsert write FInsert;
|
|
property Update: TffSqlUPDATE read FUpdate write FUpdate;
|
|
property Delete: TffSqlDELETE read FDelete write FDelete;
|
|
property TableExp: TffSqlTableExp read FTableExp write FTableExp;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
{Begin !!.11}
|
|
procedure Bind(const ClientID: TffClientID;
|
|
const SessionID: TffSessionID;
|
|
Database : TffSqlDatabaseProxy);
|
|
{End !!.11}
|
|
procedure EmitSQL(Stream : TStream); override;
|
|
{- write the SQL statement represented by this hierarchy}
|
|
{Begin !!.11}
|
|
function Execute(var aLiveResult: Boolean;
|
|
var aCursorID: TffCursorID;
|
|
var RowsAffected,
|
|
aRecordsRead: integer) : TffResult;
|
|
{End !!.11}
|
|
function Equals(Other: TffSqlNode): Boolean; override;
|
|
procedure SetParameter(Index: Integer; Value: Variant);
|
|
procedure ReduceStrength;
|
|
property Owner: TffSqlStatement read FOwner;
|
|
procedure Clear;
|
|
end;
|
|
|
|
TffGroupColumnTargetInfo = class(TffObject)
|
|
{ This class helps correlate a selection field to a slot in the
|
|
LastValues list that is created when grouping fields. There
|
|
is not a one-to-one correspondence between the two lists
|
|
because the Group By clause may reference fields not in the
|
|
selection list. }
|
|
public
|
|
SelFldIndex,
|
|
LastValueIndex : Longint;
|
|
end;
|
|
|
|
{$IFDEF ExposeLastStatement}
|
|
var
|
|
LastStatement : TffSqlStatement; {debug hook}
|
|
{$ENDIF}
|
|
|
|
implementation
|
|
|
|
uses
|
|
ffllExcp,
|
|
ffsrbase,
|
|
ffsrbde,
|
|
ffsrlock,
|
|
Math; {!!.11}
|
|
|
|
{$I ffconst.inc}
|
|
|
|
var
|
|
TimeDelta : double;
|
|
|
|
const
|
|
RelOpStr : array[TffSqlRelOp] of string =
|
|
('', '=', '<=', '<', '>', '>=', '<>');
|
|
DefStr : array[TffSqlIntervalDef] of string = (
|
|
'Unspec', 'YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND');
|
|
CanOptimizeOnOperator: array[TffSqlRelOp] of Boolean = (
|
|
{roNone, roEQ, roLE, roL, roG, roGE, roNE}
|
|
FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE);
|
|
AgString : array[TffSqlAggFunction] of string =
|
|
('COUNT','MIN','MAX','SUM','AVG');
|
|
ffSqlInConvThreshold = 8; {maximum length of expression list in
|
|
an IN clause to convert to simple expressions}
|
|
|
|
function PosCh(const SearchCh: Char; const SearchString: string): Integer;
|
|
{-same as POS but searches for a single Char}
|
|
var
|
|
Len: Integer;
|
|
begin
|
|
Len := length(SearchString);
|
|
if Len <> 0 then begin
|
|
Result := 1;
|
|
repeat
|
|
if SearchString[Result] = SearchCh then
|
|
exit;
|
|
inc(Result);
|
|
until Result > Len;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
function PosChI(const SearchCh: Char; const SearchString: string): Integer;
|
|
{-same as PosCh above, but ignores case}
|
|
var
|
|
Len: Integer;
|
|
SearchChU: Char;
|
|
begin
|
|
Len := length(SearchString);
|
|
if Len <> 0 then begin
|
|
SearchChU := UpCase(SearchCh);
|
|
Result := 1;
|
|
repeat
|
|
if SearchString[Result] = SearchCh then
|
|
exit;
|
|
if UpCase(SearchString[Result]) = SearchChU then
|
|
exit;
|
|
inc(Result);
|
|
until Result > Len;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
function PosI(const SearchFor, SearchIn: string): Integer;
|
|
{-same as POS but ignores case on both strings}
|
|
var
|
|
LenFor, LenIn, j: Integer;
|
|
FirstCh: Char;
|
|
begin
|
|
LenFor := length(SearchFor);
|
|
if LenFor = 0 then begin
|
|
Result := 0;
|
|
exit;
|
|
end;
|
|
Result := PosChI(SearchFor[1], SearchIn);
|
|
if (Result = 0) or (LenFor = 1) then
|
|
exit;
|
|
LenIn := length(SearchIn);
|
|
if LenIn <> 0 then begin
|
|
dec(LenIn, LenFor);
|
|
FirstCh := UpCase(SearchFor[1]);
|
|
repeat
|
|
if UpCase(SearchIn[Result]) = FirstCh then begin
|
|
J := 1;
|
|
repeat
|
|
inc(J);
|
|
until (J > LenFor) or (UpCase(SearchIn[Result + J - 1]) <> UpCase(SearchFor[J]));
|
|
if J > LenFor then
|
|
exit;
|
|
end;
|
|
inc(Result);
|
|
until Result > LenIn;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
{$IFNDEF DCC5OrLater}
|
|
function CompareText(const S1, S2: string): Integer; assembler;
|
|
asm
|
|
PUSH ESI
|
|
PUSH EDI
|
|
PUSH EBX
|
|
MOV ESI,EAX
|
|
MOV EDI,EDX
|
|
OR EAX,EAX
|
|
JE @@0
|
|
MOV EAX,[EAX-4]
|
|
@@0: OR EDX,EDX
|
|
JE @@1
|
|
MOV EDX,[EDX-4]
|
|
@@1: MOV ECX,EAX
|
|
CMP ECX,EDX
|
|
JBE @@2
|
|
MOV ECX,EDX
|
|
@@2: CMP ECX,ECX
|
|
@@3: REPE CMPSB
|
|
JE @@6
|
|
MOV BL,BYTE PTR [ESI-1]
|
|
CMP BL,'a'
|
|
JB @@4
|
|
CMP BL,'z'
|
|
JA @@4
|
|
SUB BL,20H
|
|
@@4: MOV BH,BYTE PTR [EDI-1]
|
|
CMP BH,'a'
|
|
JB @@5
|
|
CMP BH,'z'
|
|
JA @@5
|
|
SUB BH,20H
|
|
@@5: CMP BL,BH
|
|
JE @@3
|
|
MOVZX EAX,BL
|
|
MOVZX EDX,BH
|
|
@@6: SUB EAX,EDX
|
|
POP EBX
|
|
POP EDI
|
|
POP ESI
|
|
end;
|
|
|
|
function SameText(const S1, S2: string): Boolean; assembler;
|
|
asm
|
|
CMP EAX,EDX
|
|
JZ @1
|
|
OR EAX,EAX
|
|
JZ @2
|
|
OR EDX,EDX
|
|
JZ @3
|
|
MOV ECX,[EAX-4]
|
|
CMP ECX,[EDX-4]
|
|
JNE @3
|
|
CALL CompareText
|
|
TEST EAX,EAX
|
|
JNZ @3
|
|
@1: MOV AL,1
|
|
@2: RET
|
|
@3: XOR EAX,EAX
|
|
end;
|
|
{$ENDIF}
|
|
|
|
type
|
|
TReadSourceEvent = procedure(Sender: TObject;
|
|
var OkToCopy: boolean) of object;
|
|
TEvaluateFieldEvent = procedure(Sender: TObject;
|
|
ColumnIndex : Integer; var Res : variant) of object;
|
|
|
|
function CreateLiteralStringExp(Parent: TffSqlNode; const S: string): TffSqlSimpleExpression;
|
|
var
|
|
T : TffSqlTerm;
|
|
F : TffSqlFactor;
|
|
L : TffSqlLiteral;
|
|
SL : TffSqlStringLiteral;
|
|
begin
|
|
Result := TffSqlSimpleExpression.Create(Parent);
|
|
T := TffSqlTerm.Create(Result);
|
|
F := TffSqlFactor.Create(T);
|
|
L := TffSqlLiteral.Create(F);
|
|
SL := TffSqlStringLiteral.Create(L);
|
|
SL.Value := '''' + S + '''';
|
|
L.StringLiteral := SL;
|
|
F.Literal := L;
|
|
T.AddFactor(F);
|
|
Result.AddTerm(T);
|
|
end;
|
|
|
|
constructor TffSqlJoiner.Create(AOwner: TffSqlStatement;
|
|
CondExp: TffSqlCondExp);
|
|
begin
|
|
Assert(AOwner <> nil);
|
|
inherited Create;
|
|
FOwner := AOwner;
|
|
FCondExpWhere := CondExp;
|
|
FSources := TFFSqlTableProxySubsetList.Create(AOwner);
|
|
FieldCopier := TffFieldCopier.Create;
|
|
FSX := TList.Create;
|
|
FT := TList.Create;
|
|
end;
|
|
|
|
destructor TffSqlJoiner.Destroy;
|
|
begin
|
|
FieldCopier.Free;
|
|
FSX.Free;
|
|
FT.Free;
|
|
FSources.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TffSqlJoiner.AddColumn(
|
|
SourceExpression: TffSqlSimpleExpression;
|
|
SourceField : TffSqlFieldProxy;
|
|
Target: TFFSqlFieldProxy);
|
|
begin
|
|
Assert((SourceExpression = nil) or (SourceField = nil));
|
|
if (SourceExpression = nil) and (SourceField = nil) then {!!.13}
|
|
FSX.Add(Pointer(1)) // flag - see CreateResultRecord {!!.13}
|
|
else {!!.13}
|
|
FSX.Add(SourceExpression);
|
|
Target.IsTarget := True;
|
|
if SourceField <> nil then begin
|
|
FieldCopier.Add(SourceField, Target);
|
|
Target.SrcField := SourceField;
|
|
end
|
|
else
|
|
Target.SrcIndex := Pred(FSX.Count);
|
|
FT.Add(Target);
|
|
end;
|
|
|
|
procedure TffSqlJoiner.ClearColumnList;
|
|
begin
|
|
FSX.Clear;
|
|
FT.Clear;
|
|
FieldCopier.Free;
|
|
FieldCopier := TffFieldCopier.Create;
|
|
end;
|
|
|
|
function TffSqlJoiner.ProcessLevel(Cookie1: TffWord32): Boolean;
|
|
begin
|
|
inc(FRecordsRead);
|
|
inc(Owner.RecordsRead);
|
|
{ Time to check for timeout? }
|
|
if FRecordsRead mod 1000 = 0 then
|
|
FFCheckRemainingTime;
|
|
if Level > 0 then begin
|
|
if (CondExpWhere = nil) or CondExpWhere.AsBooleanLevel(Level) then begin
|
|
dec(Level);
|
|
ReadSources;
|
|
inc(Level);
|
|
end;
|
|
end else
|
|
if (CondExpWhere = nil) or CondExpWhere.AsBoolean then
|
|
P;
|
|
Result := True; {continue}
|
|
end;
|
|
|
|
procedure TffSqlJoiner.CreateResultRecord;
|
|
var
|
|
i : Integer;
|
|
V : Variant;
|
|
begin
|
|
if (DupList <> nil)
|
|
and not FirstCondTerm
|
|
and DupList.Exists then exit;
|
|
FTargetTable.Insert;
|
|
for i := 0 to pred(FTargetTable.FieldCount) do
|
|
if FSX[i] <> nil then begin
|
|
if Integer(FSX[i]) = 1 then {!!.13}
|
|
TFFSqlFieldProxy(Ft[i]).SetValue(1) {!!.13}
|
|
else begin {!!.13}
|
|
V := TFFSqlSimpleExpression(FSX[i]).GetValue;
|
|
TFFSqlFieldProxy(Ft[i]).SetValue(V);
|
|
end; {!!.13}
|
|
end;
|
|
FieldCopier.Execute;
|
|
FTargetTable.Post;
|
|
if (DupList <> nil)
|
|
and not LastCondTerm then
|
|
DupList.Add;
|
|
{$IFDEF CountWrites}
|
|
inc(FRecordsWritten);
|
|
{$ENDIF}
|
|
if assigned(RecListL) then
|
|
if not RecListL.Exists then
|
|
RecListL.Add;
|
|
if assigned(RecListR) then
|
|
if not RecListR.Exists then
|
|
RecListR.Add;
|
|
end;
|
|
|
|
function TffSqlJoiner.WriteNull(Cookie: TffWord32): Boolean;
|
|
begin
|
|
if not TffNRecordHash(Cookie).Exists then
|
|
CreateResultRecord;
|
|
Result := True; {continue}
|
|
end;
|
|
|
|
procedure TffSqlJoiner.ReadSources;
|
|
begin
|
|
with Sources.Item[Level] do
|
|
Iterate(ProcessLevel, 0);
|
|
end;
|
|
|
|
function TffSqlJoiner.FindRelation(
|
|
Term: TffSqlCondTerm;
|
|
CurFactor, CurFactor2: TffSqlCondFactor;
|
|
Table : TFFSqlTableProxy;
|
|
TargetField : TFFSqlFieldProxy;
|
|
var Operator: TffSqlRelOp;
|
|
var ArgExpression: TffSqlSimpleExpression;
|
|
var SameCase: Boolean): TffSqlCondFactor;
|
|
var
|
|
k, l : Integer;
|
|
F : TFFSqlFieldProxy;
|
|
DepFound : Boolean;
|
|
begin
|
|
with Term do begin
|
|
for k := 0 to pred(CondFactorCount) do
|
|
if (CondFactor[k] <> CurFactor)
|
|
and (CondFactor[k] <> CurFactor2)
|
|
and not OrderedSources.RelationUsed(CondFactor[k]) then
|
|
with CondFactor[k] do
|
|
if IsRelationTo(Table,
|
|
F, Operator, ArgExpression, SameCase)
|
|
and CanOptimizeOnOperator[Operator] then begin
|
|
if F = TargetField then begin
|
|
{check that it doesn't depend on something we haven't seen
|
|
at this point}
|
|
DepFound := False;
|
|
|
|
for l := 0 to pred(OrderedSources.Count) do
|
|
if ArgExpression.DependsOn(OrderedSources.Item[l].Table) then begin
|
|
DepFound := True;
|
|
break;
|
|
end;
|
|
|
|
if not DepFound then begin
|
|
Result := CondFactor[k];
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TffSqlJoiner.Execute(UseIndex: Boolean; LoopProc: TFFObjectProc;
|
|
OuterJoinMode: TffSqlOuterJoinMode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
FRecordsRead := 0;
|
|
{$IFDEF CountWrites}
|
|
FRecordsWritten := 0;
|
|
{$ENDIF}
|
|
|
|
if assigned(LoopProc) then
|
|
P := LoopProc
|
|
else
|
|
P := CreateResultRecord;
|
|
|
|
case OuterJoinMode of
|
|
jmLeft, jmFull :
|
|
begin
|
|
Sources.Item[0].Outer := True;
|
|
Sources.Item[0].Opposite := Sources.Item[1].Table;
|
|
Sources.OuterJoin := True;
|
|
end;
|
|
jmRight :
|
|
begin
|
|
Sources.Item[1].Outer := True;
|
|
Sources.Item[1].Opposite := Sources.Item[0].Table;
|
|
Sources.OuterJoin := True;
|
|
end;
|
|
end;
|
|
|
|
Optimize(UseIndex);
|
|
|
|
if WasOptimized then begin
|
|
|
|
if CondExpWhere.GetCondTermCount > 1 then begin
|
|
DupList := TffNRecordHash.Create;
|
|
for i := 0 to pred(Sources.Count) do
|
|
Duplist.AddTable(Sources.Item[i].Table);
|
|
end else
|
|
DupList := nil;
|
|
|
|
{process each term separately}
|
|
FirstCondTerm := True;
|
|
for i := 0 to pred(CondExpWhere.GetCondTermCount) do begin
|
|
LastCondTerm := i = pred(CondExpWhere.GetCondTermCount);
|
|
with CondExpWhere.CondTerm[i] do begin
|
|
OrderedSources.OuterJoin := OuterJoinMode <> jmNone;
|
|
OrderedSources.Join(CondExpWhere.CondTerm[i], P);
|
|
end;
|
|
FirstCondTerm := False;
|
|
end;
|
|
|
|
DupList.Free;
|
|
DupList := nil;
|
|
|
|
if OuterJoinMode = jmFull then begin
|
|
Sources.Item[0].Outer := False;
|
|
Sources.Item[1].Outer := True;
|
|
Sources.Item[1].Opposite := Sources.Item[0].Table;
|
|
|
|
OptimizeCalled := False;
|
|
Optimize(UseIndex);
|
|
|
|
if WasOptimized then begin
|
|
|
|
if CondExpWhere.GetCondTermCount > 1 then begin
|
|
DupList := TffNRecordHash.Create;
|
|
for i := 0 to pred(Sources.Count) do
|
|
Duplist.AddTable(Sources.Item[i].Table);
|
|
end else
|
|
DupList := nil;
|
|
|
|
{process each term separately}
|
|
FirstCondTerm := True;
|
|
for i := 0 to pred(CondExpWhere.GetCondTermCount) do begin
|
|
LastCondTerm := i = pred(CondExpWhere.GetCondTermCount);
|
|
with CondExpWhere.CondTerm[i] do begin
|
|
OrderedSources.OuterJoin := True;
|
|
OrderedSources.SkipInner := True;
|
|
OrderedSources.Join(CondExpWhere.CondTerm[i], P);
|
|
end;
|
|
FirstCondTerm := False;
|
|
end;
|
|
|
|
DupList.Free;
|
|
DupList := nil;
|
|
|
|
end else begin
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.SetLevelDep(Sources);
|
|
Level := Sources.Count - 1;
|
|
ReadSources;
|
|
end;
|
|
OptimizeCalled := False;
|
|
|
|
end;
|
|
|
|
end else begin
|
|
case OuterJoinMode of
|
|
jmLeft :
|
|
begin
|
|
RecListL := TffNRecordHash.Create;
|
|
ReclistL.AddTable(Sources.Item[0].Table);
|
|
end;
|
|
jmRight :
|
|
begin
|
|
RecListR := TffNRecordHash.Create;
|
|
ReclistR.AddTable(Sources.Item[1].Table);
|
|
end;
|
|
jmFull :
|
|
begin
|
|
RecListL := TffNRecordHash.Create;
|
|
ReclistL.AddTable(Sources.Item[0].Table);
|
|
RecListR := TffNRecordHash.Create;
|
|
ReclistR.AddTable(Sources.Item[1].Table);
|
|
end;
|
|
end;
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.SetLevelDep(Sources);
|
|
Level := Sources.Count - 1;
|
|
ReadSources;
|
|
case OuterJoinMode of
|
|
jmLeft :
|
|
begin
|
|
Sources.Item[1].Table.NullRecord;
|
|
Sources.Item[0].Table.Iterate(WriteNull, TffWord32(RecListL));
|
|
RecListL.Free;
|
|
RecListL := nil;
|
|
end;
|
|
jmRight :
|
|
begin
|
|
Sources.Item[0].Table.NullRecord;
|
|
Sources.Item[1].Table.Iterate(WriteNull, TffWord32(RecListR));
|
|
RecListR.Free;
|
|
RecListR := nil;
|
|
end;
|
|
jmFull :
|
|
begin
|
|
Sources.Item[1].Table.NullRecord;
|
|
Sources.Item[0].Table.Iterate(WriteNull, TffWord32(RecListL));
|
|
Sources.Item[0].Table.NullRecord;
|
|
Sources.Item[1].Table.Iterate(WriteNull, TffWord32(RecListR));
|
|
RecListL.Free;
|
|
RecListL := nil;
|
|
RecListR.Free;
|
|
RecListR := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CompareRelations(const R1, R2: TFFSqlTableProxySubset): Boolean;
|
|
{ Returns True if R1 is 'better' than R2, e.g. it is likely to better
|
|
limit the number of rows we have to read to produce a result}
|
|
var
|
|
U1, U2: Boolean;
|
|
I1, I2: Integer;
|
|
begin
|
|
if R2 = nil then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Comparing relations');
|
|
writeln(IALog, ' Rel1:');
|
|
writeln(IALog, ' Table name:',R1.Table.Name, ' (', R1.Table.Alias,')');
|
|
writeln(IALog, ' Unique:',R1.UniqueValue);
|
|
writeln(IALog, ' Closed segment:',R1.ClosedSegment);
|
|
writeln(IALog, ' Equal key depth:',R1.EqualKeyDepth);
|
|
writeln(IALog, ' Key depth:',R1.KeyDepth);
|
|
writeln(IALog, ' Relation key is unique:',R1.KeyRelation.RelationKeyIsUnique);
|
|
writeln(IALog, ' Relation key is case insensitive:',R1.KeyRelation.RelationKeyIsCaseInsensitive);
|
|
writeln(IALog, ' Record count:',R1.Table.GetRecordCount);
|
|
writeln(IALog, ' Expression:',R1.KeyRelation.CondF.SqlText);
|
|
writeln(IALog, ' Rel2:');
|
|
writeln(IALog, ' Table name:',R2.Table.Name, ' (', R2.Table.Alias,')');
|
|
writeln(IALog, ' Unique:',R2.UniqueValue);
|
|
writeln(IALog, ' Closed segment:',R2.ClosedSegment);
|
|
writeln(IALog, ' Equal key depth:',R2.EqualKeyDepth);
|
|
writeln(IALog, ' Key depth:',R2.KeyDepth);
|
|
writeln(IALog, ' Relation key is unique:',R2.KeyRelation.RelationKeyIsUnique);
|
|
writeln(IALog, ' Relation key is case insensitive:',R2.KeyRelation.RelationKeyIsCaseInsensitive);
|
|
writeln(IALog, ' Record count:',R2.Table.GetRecordCount);
|
|
writeln(IALog, ' Expression:',R2.KeyRelation.CondF.SqlText);
|
|
{$ENDIF}
|
|
U1 := R1.UniqueValue;
|
|
U2 := R2.UniqueValue;
|
|
if U1 then
|
|
if not U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' 1 is unique but 2 is not');
|
|
{$ENDIF}
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
else
|
|
if U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' 2 is unique but 1 is not');
|
|
{$ENDIF}
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
U1 := R1.ClosedSegment;
|
|
U2 := R2.ClosedSegment;
|
|
if U1 then
|
|
if U2 then
|
|
if R1.EqualKeyDepth > R2.EqualKeyDepth then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' EqualKeyDepth(1) > EqualKeyDepth(2)');
|
|
{$ENDIF}
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
if R1.EqualKeyDepth < R2.EqualKeyDepth then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' EqualKeyDepth(1) < EqualKeyDepth(2)');
|
|
{$ENDIF}
|
|
Result := False;
|
|
exit;
|
|
end else
|
|
if R1.KeyDepth > R2.KeyDepth then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' KeyDepth(1) > KeyDepth(2)');
|
|
{$ENDIF}
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
if R1.KeyDepth < R2.KeyDepth then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' KeyDepth(1) < KeyDepth(2)');
|
|
{$ENDIF}
|
|
Result := False;
|
|
exit;
|
|
end else
|
|
else begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Closed(1) and not Closed(2)');
|
|
{$ENDIF}
|
|
Result := True;
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' not Closed(1) and Closed(2)');
|
|
{$ENDIF}
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
U1 := R1.KeyRelation.RelationKeyIsUnique;
|
|
U2 := R2.KeyRelation.RelationKeyIsUnique;
|
|
if U1 then
|
|
if not U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' RelationKeyIsUnique(1) and not RelationKeyIsUnique(2)');
|
|
{$ENDIF}
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
else
|
|
if U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' not RelationKeyIsUnique(1) and RelationKeyIsUnique(2)');
|
|
{$ENDIF}
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
U1 := R1.KeyRelation.RelationKeyIsCaseInsensitive;
|
|
U2 := R2.KeyRelation.RelationKeyIsCaseInsensitive;
|
|
if U1 then
|
|
if not U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' RelationKeyIsCaseInsensitive(1) and not RelationKeyIsCaseInsensitive(2)');
|
|
{$ENDIF}
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
else
|
|
if U2 then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' not RelationKeyIsCaseInsensitive(1) and RelationKeyIsCaseInsensitive(2)');
|
|
{$ENDIF}
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
I1 := R1.Table.GetRecordCount;
|
|
I2 := R2.Table.GetRecordCount;
|
|
{$IFDEF LogIndexAnalysis}
|
|
if I1 > I2 then
|
|
writeln(IALog, ' RecordCount(1) > RecordCount(2)')
|
|
else
|
|
writeln(IALog, ' RecordCount(1) < RecordCount(2)');
|
|
{$ENDIF}
|
|
if I1 > I2 then
|
|
Result := True
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
function CompareKeyRelations(const K1, K2: TFFSqlKeyRelation): Boolean;
|
|
{ Returns True if K1 is 'better' than K2, e.g. it is likely to better
|
|
limit the number of rows we have to read to produce a result}
|
|
var
|
|
U1, U2: Boolean;
|
|
|
|
function UniqueValue(const K: TFFSqlKeyRelation): Boolean;
|
|
begin
|
|
Result :=
|
|
(K.RelationFieldCount = K.RelationKeyFieldCount)
|
|
and (K.RelationOperators[K.RelationKeyFieldCount - 1] = roEQ);
|
|
end;
|
|
|
|
function ClosedSegment(const K: TFFSqlKeyRelation): Boolean;
|
|
begin
|
|
Result :=
|
|
(K.RelationOperators[K.RelationFieldCount - 1] = roEQ) or
|
|
(K.RelationOperatorB[K.RelationFieldCount - 1] <> roNone); {!!.11}
|
|
end;
|
|
|
|
function KeyDepth(const K: TFFSqlKeyRelation): Integer;
|
|
begin
|
|
Result := K.RelationFieldCount;
|
|
end;
|
|
|
|
function EqualKeyDepth(const K: TFFSqlKeyRelation): Integer;
|
|
begin
|
|
Result := 0;
|
|
while (Result < K.RelationFieldCount)
|
|
and (K.RelationOperators[Result] = roEQ) do
|
|
inc(Result);
|
|
end;
|
|
|
|
begin
|
|
U1 := UniqueValue(K1);
|
|
U2 := UniqueValue(K2);
|
|
if U1 then
|
|
if not U2 then begin
|
|
Result := True;
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
U1 := ClosedSegment(K1);
|
|
U2 := ClosedSegment(K2);
|
|
if U1 then
|
|
if U2 then
|
|
if EqualKeyDepth(K1) > EqualKeyDepth(K2) then begin
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
if EqualKeyDepth(K1) < EqualKeyDepth(K2) then begin
|
|
Result := False;
|
|
exit;
|
|
end else
|
|
if KeyDepth(K1) > KeyDepth(K2) then begin
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
if KeyDepth(K1) < KeyDepth(K2) then begin
|
|
Result := False;
|
|
exit;
|
|
end else
|
|
else begin
|
|
Result := True;
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
U1 := K1.RelationKeyIsUnique;
|
|
U2 := K2.RelationKeyIsUnique;
|
|
if U1 then
|
|
if not U2 then begin
|
|
Result := True;
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
U1 := K1.RelationKeyIsCaseInsensitive;
|
|
U2 := K2.RelationKeyIsCaseInsensitive;
|
|
if U1 then
|
|
if not U2 then begin
|
|
Result := True;
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
procedure ShowComparison(const K1, K2: TFFSqlKeyRelation);
|
|
var
|
|
U1, U2: Boolean;
|
|
|
|
function UniqueValue(const K: TFFSqlKeyRelation): Boolean;
|
|
begin
|
|
Result :=
|
|
(K.RelationFieldCount = K.RelationKeyFieldCount)
|
|
and (K.RelationOperators[K.RelationKeyFieldCount - 1] = roEQ);
|
|
end;
|
|
|
|
function ClosedSegment(const K: TFFSqlKeyRelation): Boolean;
|
|
begin
|
|
Result := (K.RelationOperators[K.RelationFieldCount - 1] = roEQ)
|
|
or (K.RelationOperatorB[K.RelationFieldCount - 1] <> roNone); {!!.11}
|
|
end;
|
|
|
|
function KeyDepth(const K: TFFSqlKeyRelation): Integer;
|
|
begin
|
|
Result := K.RelationFieldCount;
|
|
end;
|
|
|
|
function EqualKeyDepth(const K: TFFSqlKeyRelation): Integer;
|
|
begin
|
|
Result := 0;
|
|
while (Result < K.RelationFieldCount)
|
|
and (K.RelationOperators[Result] = roEQ) do
|
|
inc(Result);
|
|
end;
|
|
|
|
begin
|
|
U1 := UniqueValue(K1);
|
|
U2 := UniqueValue(K2);
|
|
if U1 then
|
|
if not U2 then begin
|
|
writeln(IALog,' New is unique value');
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
raise Exception.Create('Internal error');
|
|
end;
|
|
U1 := ClosedSegment(K1);
|
|
U2 := ClosedSegment(K2);
|
|
if U1 then
|
|
if U2 then
|
|
if EqualKeyDepth(K1) > EqualKeyDepth(K2) then begin
|
|
writeln(IALog,'New has deeper equal key');
|
|
exit;
|
|
end else
|
|
if KeyDepth(K1) > KeyDepth(K2) then begin
|
|
writeln(IALog,'New is deeper');
|
|
exit;
|
|
end else
|
|
if KeyDepth(K1) < KeyDepth(K2) then begin
|
|
raise Exception.Create('Internal error');
|
|
end else
|
|
else begin
|
|
writeln(IALog, 'New is closed interval');
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
raise Exception.Create('Internal error');
|
|
end;
|
|
U1 := K1.RelationKeyIsUnique;
|
|
U2 := K2.RelationKeyIsUnique;
|
|
if U1 then
|
|
if not U2 then begin
|
|
writeln(IALog, 'New has unique key');
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
raise Exception.Create('Internal error');
|
|
end;
|
|
U1 := K1.RelationKeyIsCaseInsensitive;
|
|
U2 := K2.RelationKeyIsCaseInsensitive;
|
|
if U1 then
|
|
if not U2 then begin
|
|
writeln(IALog, 'New has case insensitive key');
|
|
exit;
|
|
end
|
|
else
|
|
if U2 then begin
|
|
raise Exception.Create('Internal error');
|
|
end;
|
|
raise Exception.Create('Internal error');
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TffSqlJoiner.Optimize;
|
|
var
|
|
IndexAsc : Boolean;
|
|
RestSources : TFFSqlTableProxySubsetList;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
|
|
procedure DumpOrderedList(OrderedSources : TFFSqlTableProxySubsetList; const Title: string);
|
|
var
|
|
j, y: integer;
|
|
begin
|
|
writeln(IALog, Title);
|
|
for j := 0 to pred(OrderedSources.Count) do begin
|
|
write(IALog, OrderedSources.Item[j].Table.Name, ' (', OrderedSources.Item[j].Table.Alias, ')');
|
|
if OrderedSources.Item[j].KeyRelation.CondF <> nil then begin
|
|
write(IALog, ' relation fields: ',OrderedSources.Item[j].KeyRelation.RelationFieldCount);
|
|
write(IALog, '(');
|
|
for y := 0 to pred(OrderedSources.Item[j].KeyRelation.RelationFieldCount) do begin
|
|
write(IALog, ' field:', OrderedSources.Item[j].KeyRelation.RelationFields[y].Name);
|
|
write(IALog, ' argexp:',OrderedSources.Item[j].KeyRelation.ArgExpressions[y].SQLText);
|
|
write(IALog, ' Operator:',RelOpStr[OrderedSources.Item[j].KeyRelation.RelationOperators[y]]);
|
|
{!!.11 begin}
|
|
if (OrderedSources.Item[j].KeyRelation.ArgExpressionB[y] <> nil)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationOperatorB[y] <> roNone)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationB[y] <> nil) then
|
|
write(IALog, 'secondary expression:',OrderedSources.Item[j].KeyRelation.ArgExpressionB[y].SQLText,
|
|
' operator:',RelOpStr[OrderedSources.Item[j].KeyRelation.RelationOperatorB[y]]);
|
|
{!!.11 end}
|
|
end;
|
|
write(IALog, ')');
|
|
write(IALog, ' index:',OrderedSources.Item[j].KeyRelation.NativeKeyIndex{RelationKeyIndexNative});
|
|
(* !!.11
|
|
if (OrderedSources.Item[j].KeyRelation.ArgExpressionB <> nil)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationOperatorB <> roNone)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationB <> nil) then
|
|
write(IALog, 'secondary expression:',OrderedSources.Item[j].KeyRelation.ArgExpressionB.SQLText,
|
|
' operator:',RelOpStr[OrderedSources.Item[j].KeyRelation.RelationOperatorB]);
|
|
*)
|
|
writeln(IALog);
|
|
end else
|
|
writeln(IALog, ' no relation');
|
|
end;
|
|
end;
|
|
|
|
{$ENDIF}
|
|
|
|
function FindRelations(CondTerm: TffSqlCondTerm;
|
|
MoreThanOne: Boolean): Boolean;
|
|
var
|
|
l, j, k, y : Integer;
|
|
Best, x : Integer;
|
|
F, F2 : TFFSqlFieldProxy;
|
|
IndexRefs : array[0..pred(ffcl_MaxIndexes)] of Integer;
|
|
IgnoreCase: Boolean;
|
|
IndexFields : array[0..pred(ffcl_MaxIndexFlds)] of Integer;
|
|
IndxFldCnt : Integer;
|
|
Found: Boolean;
|
|
CF : TFFSqlCondFactor;
|
|
CurIgnoreCase : Boolean;
|
|
DepFound: Integer;
|
|
BestRelation: TFFSqlTableProxySubset;
|
|
BestKeyRelation, CurKeyRelation: TFFSqlKeyRelation;
|
|
HaveKeyRelation: Boolean;
|
|
SameCase: Boolean;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
procedure DumpBest;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
with BestKeyRelation do begin
|
|
writeln(IALog,' condition:',CondF.SQLText);
|
|
writeln(IALog,' key:',NativeKeyIndex);
|
|
writeln(IALog,' Fields in key:',RelationKeyFieldCount);
|
|
writeln(IALog,' Fields:',RelationFieldCount);
|
|
for i := 0 to pred(RelationFieldCount) do begin
|
|
writeln(IALog, ' ',RelationFields[i].Name,' ',RelOpStr[RelationOperators[i]], ' ',
|
|
ArgExpressions[i].SQLText);
|
|
{!!.11 begin}
|
|
if RelationOperatorB[i] <> roNone then
|
|
writeln(IALog, ' Secondary relation:',
|
|
RelOpStr[RelationOperatorB[i]], ' ',
|
|
ArgExpressionB[i].SQLText);
|
|
{!!.11 end}
|
|
end;
|
|
{!!.11 begin
|
|
if RelationOperatorB <> roNone then
|
|
writeln(IALog, ' Secondary relation on last key field:',
|
|
RelOpStr[RelationOperatorB], ' ',
|
|
ArgExpressionB.SQLText);
|
|
!!.11 end}
|
|
end;
|
|
end;
|
|
|
|
|
|
{$ENDIF}
|
|
var
|
|
z: Integer;
|
|
begin
|
|
Result := False;
|
|
{CurKeyRelation.ArgExpressionB := nil;} {!!.11}
|
|
for z := 0 to pred(ffcl_MaxIndexFlds) do begin {!!.11}
|
|
CurKeyRelation.ArgExpressionB[z] := nil; {!!.11}
|
|
CurKeyRelation.RelationOperatorB[z] := roNone; {!!.11}
|
|
end; {!!.11}
|
|
|
|
with CondTerm do
|
|
repeat
|
|
|
|
//KeyState := ksNone;
|
|
//Depth := 0;
|
|
|
|
for j := 0 to pred(RestSources.Count) do begin
|
|
RestSources.Item[j].Relations := 0;
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' looking for relations on ',
|
|
RestSources.Item[j].Table.Name, ' (', RestSources.Item[j].Table.Alias,')');
|
|
{$ENDIF}
|
|
|
|
{we select among multiple keys as follows:}
|
|
{if we find a unique key on the available field(s) we use that
|
|
otherwise,
|
|
we use the deepest key we can find, i.e. the key where the
|
|
most segments can be satisfied.
|
|
among keys with the same depth, we pick the ones with
|
|
the tightest or the most relations, e.g.
|
|
= is better than >
|
|
> and < is better than only >
|
|
ties could be further settled based on the number of
|
|
key values in an index, but we don't currently do that}
|
|
|
|
HaveKeyRelation := False;
|
|
CurKeyRelation.RelationFieldCount := 0;
|
|
|
|
for k := 0 to pred(CondFactorCount) do begin
|
|
if not OrderedSources.RelationUsed(CondFactor[k]) then
|
|
with CondFactor[k] do begin
|
|
if IsRelationTo(RestSources.Item[j].Table,
|
|
F, CurKeyRelation.RelationOperators[0],
|
|
CurKeyRelation.ArgExpressions[0], SameCase)
|
|
and CanOptimizeOnOperator[CurKeyRelation.
|
|
RelationOperators[0]] then begin
|
|
|
|
if RestSources.Item[j].Outer
|
|
and CurKeyRelation.ArgExpressions[0].DependsOn(
|
|
RestSources.Item[j].Opposite) then begin
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALOG,' ',CondFactor[k].SQLText,' is a relation to ',
|
|
RestSources.Item[j].Table.Name,' (',RestSources.Item[j].Table.Alias,'). Arg expression:', CurKeyRelation.ArgExpressions[0].SQLText);
|
|
writeln(IALOG,' but using would violate the outer join, so we can''t use it. Skipped.');
|
|
{$ENDIF}
|
|
|
|
end else begin
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALOG,' ',CondFactor[k].SQLText,' is a relation to ',
|
|
RestSources.Item[j].Table.Name,' (',RestSources.Item[j].Table.Alias,'). Arg expression:',
|
|
CurKeyRelation.ArgExpressions[0].SQLText);
|
|
{$ENDIF}
|
|
|
|
CurKeyRelation.CondF := CondFactor[k];
|
|
{CurKeyRelation.RelationB := nil;} {!!.11}
|
|
for z := 0 to pred(ffcl_MaxIndexFlds) do begin {!!.11}
|
|
CurKeyRelation.ArgExpressionB[z] := nil; {!!.11}
|
|
CurKeyRelation.RelationOperatorB[z] := roNone; {!!.11}
|
|
end; {!!.11}
|
|
|
|
{Check that this relation does not depend on something
|
|
we can't determine at this level. For example, if we
|
|
have table1 at the deepest level, then table2 at the
|
|
next, we are looking for conditional expressions on
|
|
table2 that will limit the number of rows we need to
|
|
read but we can't use conditions whose other side
|
|
refer to anything in table1.}
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Checking dependencies on deeper tables for :' +
|
|
CurKeyRelation.ArgExpressions[0].SQLText);
|
|
{$ENDIF}
|
|
|
|
DepFound := -1;
|
|
|
|
for l := pred(OrderedSources.Count) downto 0 do
|
|
if CurKeyRelation.ArgExpressions[0].DependsOn(
|
|
OrderedSources.Item[l].Table) then begin
|
|
DepFound := l;
|
|
break;
|
|
end;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
if DepFound <> -1 then
|
|
writeln(IALog, ' Deeper dependency found:',
|
|
CurKeyRelation.ArgExpressions[0].SQLText,' : ',
|
|
OrderedSources.Item[l].Table.Name,' (',OrderedSources.Item[l].Table.Alias,')')
|
|
else
|
|
writeln(IALog, ' No deeper dependency found on ',
|
|
CurKeyRelation.ArgExpressions[0].SQLText);
|
|
{$ENDIF}
|
|
|
|
{Part of the expression opposite our field is from a table, which
|
|
has already been put in the list. We can still use this relation
|
|
by putting it below that other table *unless* something in the
|
|
existing list depends on us (the table we're looking at now)}
|
|
|
|
if (DepFound <> -1)
|
|
and OrderedSources.DependencyExists(RestSources.
|
|
Item[j].Table) then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Can''t use this - something else depends on it');
|
|
{$ENDIF}
|
|
CurKeyRelation.CondF := nil;
|
|
end else begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Relation found:', SQLText);
|
|
writeln(IALog, ' field:',F.Name);
|
|
writeln(IALog, ' same case:', SameCase); {!!.10}
|
|
writeln(IALog, ' operator:', RelOpStr[CurKeyRelation.RelationOperators[0]]);
|
|
writeln(IALog, ' arg expression:', CurKeyRelation.ArgExpressions[0].SQLTExt);
|
|
writeln(IALog, ' looking for indexes on that field');
|
|
{$ENDIF}
|
|
|
|
x := RestSources.Item[j].Table.IndexesOnField(F,
|
|
not SameCase, IndexRefs);
|
|
|
|
CurKeyRelation.RelationFieldCount := 1;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
CurKeyRelation.RelationFields[0] := F;
|
|
{$ENDIF}
|
|
|
|
if x <> 0 then begin
|
|
|
|
case CurKeyRelation.RelationOperators[0] of
|
|
roEQ :
|
|
begin
|
|
for y := 0 to pred(x) do begin
|
|
RestSources.Item[j].Table.GetIndexProperties
|
|
(IndexRefs[y], CurKeyRelation.RelationKeyIsUnique,
|
|
CurIgnoreCase, IndexAsc, IndxFldCnt,
|
|
IndexFields);
|
|
|
|
CurKeyRelation.RelationFieldCount := 1;
|
|
CurKeyRelation.RelationKeyFieldCount := IndxFldCnt;
|
|
CurKeyRelation.RelationOperators[0] := roEQ;
|
|
CurKeyRelation.RelationOperatorB[0] := roNone; {!!.11}
|
|
CurKeyRelation.RelationKeyIsCaseInsensitive :=
|
|
CurIgnoreCase; {!!.11}
|
|
CurKeyRelation.RelationKeyIndexAsc := IndexAsc;
|
|
CurKeyRelation.NativeKeyIndex := IndexRefs[y];
|
|
CurKeyRelation.DepIndex := DepFound;
|
|
|
|
(* !!.11 actually, whether relation key is unique is irrelevant here
|
|
if CurKeyRelation.RelationKeyIsUnique then begin
|
|
if IndxFldCnt = 1 then begin
|
|
IgnoreCase := CurIgnoreCase;
|
|
end else begin
|
|
{Multi-segment key.
|
|
See if we have other relations that satisfy
|
|
the following fields in the key}
|
|
CurKeyRelation.RelationFieldCount := 1;
|
|
repeat
|
|
F2 := RestSources.Item[j].Table.
|
|
Field(IndexFields[CurKeyRelation.
|
|
RelationFieldCount]);
|
|
CF := FindRelation(CondTerm, CondFactor[k],
|
|
nil, RestSources.Item[j].Table, F2,
|
|
CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount],
|
|
CurKeyRelation.ArgExpressions[
|
|
CurKeyRelation.RelationFieldCount],
|
|
CurKeyRelation.SameCases[
|
|
CurKeyRelation.RelationFieldCount]);
|
|
if CF = nil then begin
|
|
{No further fields found.
|
|
We have a key, but not a unique one}
|
|
IgnoreCase := CurIgnoreCase;
|
|
break;
|
|
end else begin
|
|
{we have a relation on this key segment}
|
|
{$IFDEF LogIndexAnalysis}
|
|
CurKeyRelation.RelationFields[
|
|
CurKeyRelation.RelationFieldCount] := F2;
|
|
{$ENDIF}
|
|
|
|
if CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount] = roEQ then begin
|
|
{operator is = which means we can continue searching if
|
|
there are more fields in the key. Otherwise, we have a full
|
|
key}
|
|
IgnoreCase := CurIgnoreCase;
|
|
end else begin
|
|
{Operator wasn't =, so we can't continue.
|
|
We can use this field, though, as the last one}
|
|
IgnoreCase := CurIgnoreCase;
|
|
{See if we have a secondary expression to close the interval}
|
|
CF := FindRelation(CondTerm, CondFactor[k],
|
|
CF, RestSources.Item[j].Table, F2,
|
|
CurKeyRelation.RelationOperatorB,
|
|
CurKeyRelation.ArgExpressionB,
|
|
CurKeyRelation.SameCaseB);
|
|
if CF <> nil then begin
|
|
{we do - record data and update key state}
|
|
|
|
CurKeyRelation.RelationB := CF;
|
|
IgnoreCase := CurIgnoreCase;
|
|
|
|
end else begin
|
|
CurKeyRelation.ArgExpressionB := nil;
|
|
CurKeyRelation.RelationOperatorB := roNone;
|
|
end;
|
|
inc(CurKeyRelation.RelationFieldCount);
|
|
break;
|
|
end;
|
|
end;
|
|
inc(CurKeyRelation.RelationFieldCount);
|
|
until CurKeyRelation.RelationFieldCount >=
|
|
IndxFldCnt;
|
|
end;
|
|
end else begin {not a unique key}
|
|
*)
|
|
if IndxFldCnt = 1 then begin
|
|
IgnoreCase := CurIgnoreCase;
|
|
end else begin
|
|
{Multi-segment key.
|
|
See if we have other relations that satisfy
|
|
the following fields in the key}
|
|
CurKeyRelation.RelationFieldCount := 1;
|
|
repeat
|
|
F2 := RestSources.Item[j].Table.
|
|
Field(IndexFields[
|
|
CurKeyRelation.RelationFieldCount]);
|
|
CF := FindRelation(CondTerm, CondFactor[k],
|
|
nil, RestSources.Item[j].Table, F2,
|
|
CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount],
|
|
CurKeyRelation.ArgExpressions[
|
|
CurKeyRelation.RelationFieldCount],
|
|
CurKeyRelation.SameCases[
|
|
CurKeyRelation.RelationFieldCount]);
|
|
if CF = nil then begin
|
|
{No further fields found, but
|
|
we have a key but not a full one}
|
|
IgnoreCase := CurIgnoreCase;
|
|
break;
|
|
end else begin
|
|
{we have a relation on this key segment}
|
|
{$IFDEF LogIndexAnalysis}
|
|
CurKeyRelation.RelationFields[CurKeyRelation.RelationFieldCount] := F2;
|
|
{$ENDIF}
|
|
|
|
if CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount] = roEQ then begin
|
|
{operator is = which means we can continue searching if
|
|
there are more fields in the key. Otherwise, we have a full
|
|
key}
|
|
IgnoreCase := CurIgnoreCase;
|
|
CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount] := roNone; {!!.11}
|
|
end else begin
|
|
{Operator wasn't =, so we can't continue.
|
|
We can use this field, though, as the last one}
|
|
IgnoreCase := CurIgnoreCase;
|
|
{see if we have other relations on this same segment}
|
|
CF := FindRelation(CondTerm, CondFactor[k],
|
|
CF, RestSources.Item[j].Table,
|
|
F2, CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount], {!!.11}
|
|
CurKeyRelation.ArgExpressionB[CurKeyRelation.RelationFieldCount], {!!.11}
|
|
CurKeyRelation.SameCaseB[CurKeyRelation.RelationFieldCount]); {!!.11}
|
|
if CF <> nil then begin
|
|
{we do - record data and update key state}
|
|
|
|
CurKeyRelation.RelationB[CurKeyRelation.RelationFieldCount] := CF; {!!.11}
|
|
IgnoreCase := CurIgnoreCase;
|
|
|
|
end else begin
|
|
CurKeyRelation.ArgExpressionB[CurKeyRelation.RelationFieldCount] := nil; {!!.11}
|
|
CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount] := roNone; {!!.11}
|
|
end;
|
|
inc(CurKeyRelation.RelationFieldCount);
|
|
break;
|
|
end;
|
|
end;
|
|
inc(CurKeyRelation.RelationFieldCount);
|
|
until CurKeyRelation.RelationFieldCount >=
|
|
IndxFldCnt;
|
|
end;
|
|
{end;} {!!.11}
|
|
if HaveKeyRelation then
|
|
if CompareKeyRelations(CurKeyRelation, BestKeyRelation) then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog,' New best key relation');
|
|
ShowComparison(CurKeyRelation, BestKeyrelation);
|
|
{$ENDIF}
|
|
BestKeyRelation := CurKeyRelation;
|
|
{$IFDEF LogIndexAnalysis}
|
|
DumpBest;
|
|
{$ENDIF}
|
|
end else
|
|
else begin
|
|
BestKeyRelation := CurKeyRelation;
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog,' initial key relation');
|
|
DumpBest;
|
|
{$ENDIF}
|
|
HaveKeyRelation := True;
|
|
end;
|
|
end;
|
|
end;
|
|
else {~ Op <> roEQ}
|
|
{non equal join operator}
|
|
for y := 0 to pred(x) do begin
|
|
RestSources.Item[j].Table.GetIndexProperties
|
|
(IndexRefs[y], CurKeyRelation.RelationKeyIsUnique,
|
|
IgnoreCase, IndexAsc, IndxFldCnt, IndexFields);
|
|
|
|
CurKeyRelation.RelationFieldCount := 1;
|
|
CurKeyRelation.RelationKeyFieldCount := IndxFldCnt;
|
|
CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount-1] := roNone; {!!.11}
|
|
CurKeyRelation.RelationKeyIsCaseInsensitive :=
|
|
CurIgnoreCase; {!!.11}
|
|
CurKeyRelation.RelationKeyIndexAsc := IndexAsc;
|
|
CurKeyRelation.NativeKeyIndex := IndexRefs[y];
|
|
CurKeyRelation.DepIndex := DepFound;
|
|
|
|
IgnoreCase := CurIgnoreCase;
|
|
|
|
{see if we have other relations on this same segment}
|
|
CF := FindRelation(CondTerm, CondFactor[k], nil,
|
|
RestSources.Item[j].Table, F, CurKeyRelation.
|
|
RelationOperatorB[CurKeyRelation.RelationFieldCount-1], {!!.11}
|
|
CurKeyRelation.ArgExpressionB[CurKeyRelation.RelationFieldCount-1], {!!.11}
|
|
CurKeyRelation.SameCaseB[CurKeyRelation.RelationFieldCount-1]); {!!.11}
|
|
|
|
if CF <> nil then begin
|
|
{we do - record data and update key state}
|
|
|
|
IgnoreCase := CurIgnoreCase;
|
|
|
|
CurKeyrelation.RelationB[CurKeyRelation.RelationFieldCount-1] := CF; {!!.11}
|
|
|
|
{!!.11 begin}
|
|
{check for more interval segments}
|
|
if (IndxFldCnt > 1)
|
|
and (CurKeyRelation.RelationOperators[0] in [roEQ, roGE, roLE])
|
|
and (CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount-1] in [roEQ, roGE, roLE]) then begin
|
|
{Multi-segment key.
|
|
See if we have other relations that satisfy
|
|
the following fields in the key}
|
|
repeat
|
|
F2 := RestSources.Item[j].Table.
|
|
Field(IndexFields[
|
|
CurKeyRelation.RelationFieldCount]);
|
|
CF := FindRelation(CondTerm, CondFactor[k],
|
|
nil, RestSources.Item[j].Table, F2,
|
|
CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount],
|
|
CurKeyRelation.ArgExpressions[
|
|
CurKeyRelation.RelationFieldCount],
|
|
CurKeyRelation.SameCases[
|
|
CurKeyRelation.RelationFieldCount]);
|
|
if CF = nil then begin
|
|
{No further fields found, but
|
|
we have a key but not a full one}
|
|
IgnoreCase := CurIgnoreCase;
|
|
break;
|
|
end else
|
|
if CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount] in [roEQ, roGE, roLE] then
|
|
begin
|
|
{we have a relation on this key segment}
|
|
{$IFDEF LogIndexAnalysis}
|
|
CurKeyRelation.RelationFields[CurKeyRelation.RelationFieldCount] := F2;
|
|
{$ENDIF}
|
|
|
|
if CurKeyRelation.RelationOperators[
|
|
CurKeyRelation.RelationFieldCount] = roEQ then begin
|
|
{operator is = which means we can continue searching if
|
|
there are more fields in the key. Otherwise, we have a full
|
|
key}
|
|
IgnoreCase := CurIgnoreCase;
|
|
CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount] := roNone; {!!.11}
|
|
end else begin
|
|
{Operator wasn't =}
|
|
IgnoreCase := CurIgnoreCase;
|
|
{see if we have other relations on this same segment}
|
|
CF := FindRelation(CondTerm, CondFactor[k],
|
|
CF, RestSources.Item[j].Table,
|
|
F2, CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount], {!!.11}
|
|
CurKeyRelation.ArgExpressionB[CurKeyRelation.RelationFieldCount], {!!.11}
|
|
CurKeyRelation.SameCaseB[CurKeyRelation.RelationFieldCount]); {!!.11}
|
|
if CF <> nil then begin
|
|
if not (CurKeyRelation.RelationOperatorB[
|
|
CurKeyRelation.RelationFieldCount] in [roEQ, roGE, roLE]) then
|
|
break;
|
|
|
|
{we do - record data and update key state}
|
|
|
|
CurKeyRelation.RelationB[CurKeyRelation.RelationFieldCount] := CF; {!!.11}
|
|
IgnoreCase := CurIgnoreCase;
|
|
|
|
end else begin
|
|
CurKeyRelation.ArgExpressionB[CurKeyRelation.RelationFieldCount] := nil; {!!.11}
|
|
CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount] := roNone; {!!.11}
|
|
inc(CurKeyRelation.RelationFieldCount);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
inc(CurKeyRelation.RelationFieldCount);
|
|
until CurKeyRelation.RelationFieldCount >=
|
|
IndxFldCnt;
|
|
end;
|
|
{!!.11 end}
|
|
end else begin
|
|
CurKeyRelation.ArgExpressionB[CurKeyRelation.RelationFieldCount-1] := nil; {!!.11}
|
|
CurKeyRelation.RelationOperatorB[CurKeyRelation.RelationFieldCount-1] := roNone; {!!.11}
|
|
end;
|
|
|
|
if HaveKeyRelation then
|
|
if CompareKeyRelations(CurKeyRelation,
|
|
BestKeyRelation) then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
ShowComparison(CurKeyRelation, BestKeyrelation);
|
|
{$ENDIF}
|
|
BestKeyRelation := CurKeyRelation;
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog,' new best key relation');
|
|
DumpBest;
|
|
{$ENDIF}
|
|
end else
|
|
else begin
|
|
BestKeyRelation := CurKeyRelation;
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog,' initial key relation');
|
|
DumpBest;
|
|
{$ENDIF}
|
|
HaveKeyRelation := True;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' ', x, ' found!');
|
|
for y := 0 to pred(x) do begin
|
|
RestSources.Item[j].Table.GetIndexProperties
|
|
(IndexRefs[y], CurKeyRelation.RelationKeyIsUnique,
|
|
IgnoreCase, IndexAsc, IndxFldCnt, IndexFields);
|
|
writeln(IALog, ' key', y, ': ',
|
|
' Unique:', CurKeyRelation.RelationKeyIsUnique,
|
|
' IgnoreCase:', IgnoreCase,
|
|
' IndexAsc:', IndexAsc,
|
|
' Segments:',IndxFldCnt);
|
|
if IndxFldCnt <> 0 then begin
|
|
write(IALog, ' (');
|
|
for z := 0 to pred(IndxFldCnt) do begin
|
|
write(IALog, RestSources.Item[j].Table.
|
|
Field(IndexFields[z]).Name,' ');
|
|
end;
|
|
writeln(IALog, ')');
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
inc(RestSources.Item[j].Relations);
|
|
|
|
end else
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' none found');
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if HaveKeyRelation then
|
|
RestSources.Item[j].KeyRelation := BestKeyRelation;
|
|
end;
|
|
|
|
Found := False;
|
|
Best := -1;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Comparing relations');
|
|
{$ENDIF}
|
|
|
|
BestRelation := nil;
|
|
for j := 0 to pred(RestSources.Count) do begin
|
|
if (not MoreThanOne and (RestSources.Item[j].Relations = 1))
|
|
or (MoreThanOne and (RestSources.Item[j].Relations > 0)) then begin
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' ', RestSources.Item[j].Table.Name,' (',
|
|
RestSources.Item[j].Table.Alias,') relations:',
|
|
RestSources.Item[j].Relations);
|
|
{$ENDIF}
|
|
if CompareRelations(RestSources.Item[j], BestRelation) then begin
|
|
BestRelation := RestSources.Item[j];
|
|
Best := j;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
if BestRelation <> nil then begin
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Best:', BestRelation.Table.Name,' (',BestRelation.Table.Alias,')');
|
|
{$ENDIF}
|
|
if BestRelation.KeyRelation.DepIndex = -1 then
|
|
OrderedSources.Add(RestSources.Item[Best])
|
|
else
|
|
OrderedSources.Insert(RestSources.Item[Best]);
|
|
RestSources.Delete(Best);
|
|
Found := True;
|
|
{$IFDEF LogIndexAnalysis}
|
|
DumpOrderedList(OrderedSources, ' Ordered list so far(inner to outer):');
|
|
{$ENDIF}
|
|
Result := True;
|
|
end;
|
|
|
|
until not Found;
|
|
end;
|
|
|
|
var
|
|
i, j : Integer;
|
|
{$IFDEF LogIndexAnalysis}
|
|
y : Integer;
|
|
{$ENDIF}
|
|
begin
|
|
if OptimizeCalled then exit;
|
|
|
|
WasOptimized := False;
|
|
|
|
if (CondExpWhere <> nil) and UseIndex then begin
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
AssignFile(IALog, IALogFile);
|
|
{$I-}
|
|
Append(IALog);
|
|
if IOResult <> 0 then
|
|
Rewrite(IALog);
|
|
writeln(IALog);
|
|
writeln(IALog, 'Analyzing ' + CondExpWhere.Owner.SQLText);
|
|
writeln(IALog, 'Analysis started at :',DateTimeToStr(Now));
|
|
{$ENDIF}
|
|
|
|
{look for relations that might be used for optimizing the join}
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, 'Scanning for relations');
|
|
{$ENDIF}
|
|
|
|
for i := 0 to pred(CondExpWhere.GetCondTermCount) do begin
|
|
|
|
{process each term separately}
|
|
with CondExpWhere.CondTerm[i] do begin
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, 'Term ', i, ' : ',SQLText);
|
|
{$ENDIF}
|
|
|
|
OrderedSources.Free;
|
|
|
|
OrderedSources := TFFSqlTableProxySubsetList.Create(Owner);
|
|
RestSources := TFFSqlTableProxySubsetList.Create(Owner);
|
|
try
|
|
{We build an ordered list of tables to process so that
|
|
the inner-most table in the list is first.}
|
|
|
|
{Specifically, we do this by looking for key relations
|
|
which will limit the number of rows we need to read from
|
|
each table.}
|
|
|
|
{RestSources are the tables at any time which have not
|
|
yet been selected for processing.
|
|
When RestSources.Count = 0, we're done.}
|
|
|
|
RestSources.Assign(Sources);
|
|
|
|
{First, find and process the relations with
|
|
exactly one key resolution.}
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Looking for relations with exactly one resolution');
|
|
{$ENDIF}
|
|
|
|
if FindRelations(CondExpWhere.CondTerm[i], False) then
|
|
WasOptimized := True;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
DumpOrderedList(OrderedSources, 'Final ordered list (inner to outer):');
|
|
{$ENDIF}
|
|
|
|
{Then, find and process the relations with
|
|
more than one key resolution, if any.}
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, ' Looking for relations with more than one resolution');
|
|
{$ENDIF}
|
|
|
|
if FindRelations(CondExpWhere.CondTerm[i], True) then
|
|
WasOptimized := True;
|
|
|
|
{Finally, add the sources with no key relations - if any}
|
|
|
|
for j := pred(RestSources.Count) downto 0 do begin
|
|
RestSources.Item[j].KeyRelation.CondF := nil;
|
|
OrderedSources.Add(RestSources.Item[j]);
|
|
RestSources.Delete(j);
|
|
end;
|
|
|
|
Assert(RestSources.Count = 0);
|
|
|
|
{done re-ordering}
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog, 'Ordered list (inner to outer):');
|
|
for j := 0 to pred(OrderedSources.Count) do begin
|
|
write(IALog, OrderedSources.Item[j].Table.Name,' (',OrderedSources.Item[j].Table.Alias,')');
|
|
if OrderedSources.Item[j].KeyRelation.CondF <> nil then begin
|
|
write(IALog, ' relation fields: ',OrderedSources.Item[j].KeyRelation.RelationFieldCount);
|
|
write(IALog, '(');
|
|
for y := 0 to pred(OrderedSources.Item[j].KeyRelation.RelationFieldCount) do begin
|
|
write(IALog, ' field:', OrderedSources.Item[j].KeyRelation.RelationFields[y].Name);
|
|
write(IALog, ' argexp:',OrderedSources.Item[j].KeyRelation.ArgExpressions[y].SQLText);
|
|
write(IALog, ' Operator:',RelOpStr[OrderedSources.Item[j].KeyRelation.RelationOperators[y]]);
|
|
{!!.11 begin}
|
|
if (OrderedSources.Item[j].KeyRelation.ArgExpressionB[y] <> nil)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationOperatorB[y] <> roNone)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationB[y] <> nil) then
|
|
write(IALog, 'secondary expression:',OrderedSources.Item[j].KeyRelation.ArgExpressionB[y].SQLText,
|
|
' operator:',RelOpStr[OrderedSources.Item[j].KeyRelation.RelationOperatorB[y]]);
|
|
{!!.11 end}
|
|
end;
|
|
write(IALog, ')');
|
|
write(IALog, ' index:',OrderedSources.Item[j].KeyRelation.NativeKeyIndex);
|
|
{!!.11 begin
|
|
if (OrderedSources.Item[j].KeyRelation.ArgExpressionB <> nil)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationOperatorB <> roNone)
|
|
and (OrderedSources.Item[j].KeyRelation.RelationB <> nil) then
|
|
write(IALog, 'secondary expression:',OrderedSources.Item[j].KeyRelation.ArgExpressionB.SQLText,
|
|
' operator:',RelOpStr[OrderedSources.Item[j].KeyRelation.RelationOperatorB]);
|
|
!!.11 end}
|
|
writeln(IALog);
|
|
end else
|
|
writeln(IALog, ' no relation');
|
|
end;
|
|
{$ENDIF}
|
|
finally
|
|
RestSources.Free;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
{$IFDEF LogIndexAnalysis}
|
|
writeln(IALog);
|
|
writeln(IALog, 'Analysis ended at :',DateTimeToStr(Now));
|
|
CloseFile(IALog);
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
OptimizeCalled := True;
|
|
end;
|
|
|
|
{===Utility routines=================================================}
|
|
function BothNil(O1, O2: TffSqlNode): Boolean;
|
|
begin
|
|
Result := (O1 = nil) and (O2 = nil);
|
|
end;
|
|
{--------}
|
|
function BothNonNil(O1, O2: TffSqlNode): Boolean;
|
|
begin
|
|
Result := (O1 <> nil) and (O2 <> nil);
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlNode=======================================================}
|
|
{--------}
|
|
procedure TffSqlNode.AddAggregate(Target: TList);
|
|
begin
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.FlagAggregate;
|
|
begin
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.GetDecimals: Integer;
|
|
begin
|
|
raise Exception.CreateFmt('Internal error:GetDecimals not implemented for %s',
|
|
[ClassName]);
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.GetSize: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.GetType: TffFieldType;
|
|
begin
|
|
raise Exception.CreateFmt('Internal error:GetType not implemented for %s',
|
|
[ClassName]);
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.IsAggregate: Boolean;
|
|
begin
|
|
raise Exception.CreateFmt('Internal error:IsAggregate not implemented for %s',
|
|
[ClassName]);
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.GetOwner: TffSqlStatement;
|
|
begin
|
|
if (FOwner = nil)
|
|
and not (Self is TffSqlStatement) then begin
|
|
Assert(Parent <> nil);
|
|
FOwner := TffSqlStatement(Parent);
|
|
while FOwner.Parent <> nil do
|
|
FOwner := TffSqlStatement(FOwner.Parent);
|
|
Assert(Owner is TffSqlStatement);
|
|
end;
|
|
Result := FOwner;
|
|
end;
|
|
{--------}
|
|
{Begin !!.11}
|
|
function TffSqlNode.GetOwnerStmt: TFFSqlColumnListOwner;
|
|
begin
|
|
if (FOwnerStmt = nil) then begin
|
|
FOwnerStmt := TFFSqlColumnListOwner(Self);
|
|
while (FOwnerStmt <> nil)
|
|
and not (TObject(FOwnerStmt) is TFFSqlColumnListOwner) do
|
|
FOwnerStmt := TFFSqlColumnListOwner(FOwnerStmt.Parent);
|
|
if not (TObject(FOwnerStmt) is TFFSqlColumnListOwner) then
|
|
FOwnerStmt := nil;
|
|
end;
|
|
Result := FOwnerStmt;
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.GetOwnerSelect: TFFSqlSelect;
|
|
begin
|
|
if (FOwnerStmt = nil) then begin
|
|
FOwnerStmt := TFFSqlSelect(Self);
|
|
while (FOwnerStmt <> nil)
|
|
and not (TObject(FOwnerStmt) is TFFSqlSelect) do
|
|
FOwnerStmt := TFFSqlSelect(FOwnerStmt.Parent);
|
|
if not (TObject(FOwnerStmt) is TFFSqlSelect) then
|
|
FOwnerStmt := nil;
|
|
end;
|
|
Result := TffSqlSelect(FOwnerStmt);
|
|
end;
|
|
{End !!.11}
|
|
{--------}
|
|
procedure TffSqlNode.TypeMismatch;
|
|
begin
|
|
SQLError('Type mismatch');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.WriteEOF(Stream: TStream);
|
|
const
|
|
NullChar : Char = #0;
|
|
begin
|
|
Stream.Write(NullChar, 1);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.WriteStr(Stream: TStream; const S: string);
|
|
begin
|
|
if S <> '' then
|
|
Stream.Write(S[1], length(S));
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.AddTableReference;
|
|
begin
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.AddColumnDef;
|
|
begin
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.AssignError(Source: TffSqlNode);
|
|
begin
|
|
raise Exception.Create(Source.ClassName + ' not compatible with ' + ClassName);
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
if Parent <> nil then
|
|
Result := Parent.BindField(TableName, FieldName)
|
|
else
|
|
raise Exception.CreateFmt('No node could resolve the field %s.%s', {!!.11}
|
|
[TableName, FieldName]); {!!.11}
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.ClearBinding;
|
|
begin
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.IsAncestor(const Node : TffSqlNode) : Boolean;
|
|
var
|
|
aParent : TffSqlNode;
|
|
begin
|
|
aParent := FParent;
|
|
repeat
|
|
Result := (aParent = Node);
|
|
aParent := aParent.Parent;
|
|
until Result or (aParent = nil);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.ResetConstant;
|
|
begin
|
|
end;
|
|
{--------}
|
|
function TffSqlNode.SQLText: string;
|
|
var
|
|
M : TMemoryStream;
|
|
begin
|
|
M := TMemoryStream.Create;
|
|
try
|
|
EmitSQL(M);
|
|
SetLength(Result, M.Size);
|
|
M.Seek(0, 0);
|
|
M.Read(Result[1], M.Size);
|
|
finally
|
|
M.Free;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.SQLError(const ErrorMsg: string);
|
|
begin
|
|
raise Exception.CreateFmt('Error in statement: %s', [ErrorMsg]);
|
|
end;
|
|
{--------}
|
|
constructor TffSqlNode.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create;
|
|
FParent := AParent;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlNode.EmitSQL(Stream: TStream);
|
|
begin
|
|
raise Exception.CreateFmt('Internal error:EmitSQL not implemented for %s',
|
|
[ClassName]);
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlSelectionList==============================================}
|
|
function TffSqlSelectionList.AddSelection(
|
|
NewSelection: TffSqlSelection): TffSqlSelection;
|
|
begin
|
|
FSelections.Add(NewSelection);
|
|
Result := NewSelection;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelectionList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlSelectionList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlSelectionList(Source).SelectionCount) do
|
|
AddSelection(TffSqlSelection.Create(Self)).Assign(
|
|
TffSqlSelectionList(Source).Selection[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
{--------}
|
|
constructor TffSqlSelectionList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FSelections := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelectionList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(SelectionCount) do
|
|
Selection[i].Free;
|
|
FSelections.Clear;
|
|
end;
|
|
|
|
{--------}
|
|
destructor TffSqlSelectionList.Destroy;
|
|
begin
|
|
Clear;
|
|
FSelections.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelectionList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
First: Boolean;
|
|
begin
|
|
if SelectionCount > 0 then begin
|
|
First := True;
|
|
for i := 0 to pred(SelectionCount) do begin
|
|
if not First then
|
|
WriteStr(Stream, ', ');
|
|
if not Selection[i].AddedByWildcard then begin
|
|
Selection[i].EmitSQL(Stream);
|
|
First := False;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelectionList.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Assert(TObject(Self) is TffSqlSelectionList);
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(SelectionCount) do
|
|
Selection[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlSelectionList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlSelectionList then begin
|
|
if NonWildSelectionCount <> TffSqlSelectionList(Other).NonWildSelectionCount then
|
|
exit;
|
|
for i := 0 to pred(NonWildSelectionCount) do
|
|
if not NonWildSelection[i].Equals(TffSqlSelectionList(Other).
|
|
NonWildSelection[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlSelectionList.FindSelection(GroupCol :
|
|
TffSqlGroupColumn) : TffSqlSelection;
|
|
var
|
|
i : Integer;
|
|
F : TffSqlFieldProxy;
|
|
Name : string;
|
|
begin
|
|
Name := GroupCol.QualColumnName;
|
|
|
|
for i := 0 to pred(SelectionCount) do
|
|
if Assigned(Selection[i].SimpleExpression.Term[0].Factor[0].FieldRef) and
|
|
(AnsiCompareText(Trim(Selection[i].SimpleExpression.Term[0].Factor[0].
|
|
FieldRef.QualName), Name) = 0) then begin
|
|
Result := Selection[i];
|
|
exit;
|
|
end else
|
|
if AnsiCompareText(Trim(Selection[i].SQLText), Name) = 0 then begin
|
|
Result := Selection[i];
|
|
exit;
|
|
end else
|
|
if Selection[i].Column <> nil then
|
|
if AnsiCompareText(Selection[i].Column.ColumnName, Name) = 0 then begin
|
|
Result := Selection[i];
|
|
exit;
|
|
end else
|
|
else
|
|
if Selection[i].SimpleExpression.IsField(F) then
|
|
if (AnsiCompareText(F.Name, Name) = 0) or
|
|
(AnsiCompareText(F.QualName, Name) = 0) then begin
|
|
Result := Selection[i];
|
|
exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
{--------}
|
|
function TffSqlSelectionList.GetNonWildSelection(
|
|
Index: Integer): TffSqlSelection;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to pred(SelectionCount) do
|
|
if not Selection[i].AddedByWildcard then begin
|
|
dec(Index);
|
|
if Index < 0 then begin
|
|
Result := Selection[i];
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
{--------}
|
|
function TffSqlSelectionList.GetSelection(
|
|
Index: Integer): TffSqlSelection;
|
|
begin
|
|
Result := TffSqlSelection(FSelections[Index]);
|
|
Assert(TObject(Result) is TffSqlSelection);
|
|
end;
|
|
{--------}
|
|
function TffSqlSelectionList.GetSelectionCount: Integer;
|
|
begin
|
|
Result := FSelections.Count;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelectionList.InsertSelection(Index: Integer;
|
|
NewSelection: TffSqlSelection);
|
|
begin
|
|
FSelections.Insert(Index, NewSelection);
|
|
end;
|
|
{--------}
|
|
function TffSqlSelectionList.NonWildSelectionCount: Integer;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to pred(SelectionCount) do
|
|
if not Selection[i].AddedByWildcard then
|
|
inc(Result);
|
|
end;
|
|
|
|
function TffSqlSelectionList.Reduce: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
for i := 0 to pred(SelectionCount) do
|
|
Result := Result or Selection[i].Reduce;
|
|
end;
|
|
|
|
procedure TffSqlSelectionList.SetSelection(Index: Integer;
|
|
const Value: TffSqlSelection);
|
|
begin
|
|
FSelections[Index] := Value;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlSimpleExpression===========================================}
|
|
function TffSqlSimpleExpression.AddTerm(Term: TffSqlTerm): TffSqlTerm;
|
|
begin
|
|
TermList.Add(Term);
|
|
Result := Term;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlSimpleExpression then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlSimpleExpression(Source).TermCount) do begin
|
|
AddTerm(TffSqlTerm.Create(Self)).Assign(
|
|
TffSqlSimpleExpression(Source).Term[i]);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
constructor TffSqlSimpleExpression.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
TermList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
Term[i].Free;
|
|
TermList.Clear;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
{Begin !!.13}
|
|
function TffSqlSimpleExpression.ConcatBLOBValues(const Value1, Value2 : Variant) : Variant;
|
|
var
|
|
VPtr1, VPtr2 : PAnsiChar;
|
|
VStr1, VStr2 : string;
|
|
VLen1, VLen2 : Integer;
|
|
VPtrResult : PAnsiChar;
|
|
begin
|
|
try
|
|
if VarType(Value1) and VarTypeMask = varByte then begin
|
|
VPtr1 := VarArrayLock(Value1);
|
|
VStr1 := '';
|
|
VLen1 := VarArrayHighBound(Value1, 1);
|
|
end
|
|
else begin
|
|
VStr1 := VarToStr(Value1);
|
|
VPtr1 := PAnsiChar(VStr1);
|
|
VLen1 := Length(VStr1);
|
|
end;
|
|
|
|
if VarType(Value2) and VarTypeMask = varByte then begin
|
|
VPtr2 := VarArrayLock(Value2);
|
|
VStr2 := '';
|
|
VLen2 := VarArrayHighBound(Value2, 1);
|
|
end
|
|
else begin
|
|
VStr2 := VarToStr(Value2);
|
|
VPtr2 := PAnsiChar(VStr2);
|
|
VLen2 := Length(VStr2);
|
|
end;
|
|
|
|
{ Assumption: The result may always be returned as a BLOB value. }
|
|
Result := VarArrayCreate([1, VLen1 + VLen2], varByte);
|
|
VPtrResult := VarArrayLock(Result);
|
|
try
|
|
Move(VPtr1^, VPtrResult^, VLen1);
|
|
inc(VPtrResult, VLen1);
|
|
Move(VPtr2^, VPtrResult^, VLen2);
|
|
finally
|
|
VarArrayUnlock(Result);
|
|
end;
|
|
|
|
finally
|
|
if VStr1 = '' then
|
|
VarArrayUnlock(Value1);
|
|
if VStr2 = '' then
|
|
VarArrayUnlock(Value2);
|
|
end;
|
|
end;
|
|
{End !!.13}
|
|
{--------}
|
|
function TffSqlSimpleExpression.DependsOn(
|
|
Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
if Term[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlSimpleExpression.Destroy;
|
|
begin
|
|
Clear;
|
|
TermList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.EmitSQL(Stream: TStream);
|
|
const
|
|
AddOpStr : array[TffSqlAddOp] of string = (' + ', ' - ', ' || ');
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Term[0].EmitSQL(Stream);
|
|
for i := 1 to pred(TermCount) do begin
|
|
WriteStr(Stream, AddOpStr[Term[i].AddOp]);
|
|
Term[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(TermCount) do
|
|
Term[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlSimpleExpression then begin
|
|
if TermCount <> TffSqlSimpleExpression(Other).TermCount then
|
|
exit;
|
|
for i := 0 to pred(TermCount) do
|
|
if not Term[i].Equals(TffSqlSimpleExpression(Other).Term[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetValue: Variant;
|
|
var
|
|
i : Integer;
|
|
Op: Variant;
|
|
Type1, Type2 : TffFieldType; {!!.13}
|
|
begin
|
|
if assigned(OwnerSelect) and
|
|
(OwnerSelect.AggQueryMode = aqmHaving) and not IsConstant
|
|
and not IsParameter then begin
|
|
Assert(BoundHaving);
|
|
Result := BoundHavingField.GetValue;
|
|
exit;
|
|
end;
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
Result := Term[0].GetValue;
|
|
if VarIsNull(Result) then exit;
|
|
for i := 1 to pred(TermCount) do begin
|
|
Op := Term[i].GetValue;
|
|
if VarIsNull(Op) then begin
|
|
Result := Null;
|
|
exit;
|
|
end;
|
|
Type1 := Term[0].GetType;
|
|
Type2 := Term[i].GetType;
|
|
case Term[i].AddOp of
|
|
aoPlus :
|
|
if (Type1 in [fftStDate, fftStTime, fftDateTime]) and
|
|
(Type2 = fftInterval) then
|
|
Result := Term[i].AddIntervalTo(Result)
|
|
else if (Type1 in [fftBLOB..fftBLOBTypedBin]) or
|
|
(Type2 in [fftBLOB..fftBLOBTypedBin]) then
|
|
Result := ConcatBLOBValues(Result, Op)
|
|
else
|
|
Result := Result + Op;
|
|
aoMinus :
|
|
if (Type1 in [fftStDate, fftStTime, fftDateTime]) and
|
|
(Type2 = fftInterval) then
|
|
Result := Term[i].SubtractIntervalFrom(Result)
|
|
else
|
|
Result := Result - Op;
|
|
aoConcat :
|
|
if (Type1 in [fftBLOB..fftBLOBTypedBin]) or
|
|
(Type2 in [fftBLOB..fftBLOBTypedBin]) then
|
|
Result := ConcatBLOBValues(Result, Op)
|
|
else
|
|
Result := Result + Op;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.HasFieldRef: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
if Term[i].HasFieldRef then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsAggregateExpression: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
if Term[i].IsAggregateExpression then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsField(var FieldReferenced:
|
|
TFFSqlFieldProxy): Boolean;
|
|
begin
|
|
Result := (TermCount = 1) and Term[0].IsField(FieldReferenced);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsFieldFrom(
|
|
Table: TFFSqlTableProxy; var FieldReferenced: TFFSqlFieldProxy;
|
|
var SameCase: Boolean): Boolean;
|
|
begin
|
|
Result := (TermCount = 1) and Term[0].IsFieldFrom(Table,
|
|
FieldReferenced, SameCase);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsNull: Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
if Term[i].IsNull then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetTerm(
|
|
Index: Integer): TffSqlTerm;
|
|
begin
|
|
Result := TffSqlTerm(TermList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetTermCount: Integer;
|
|
begin
|
|
Result := TermList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
if TermCount = 1 then
|
|
Result := Term[0].GetTitle(Qualified) {!!.11}
|
|
else
|
|
Result := 'EXP';
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsParameter: Boolean;
|
|
begin
|
|
Result := (TermCount = 1)
|
|
and (Term[0].FactorCount = 1)
|
|
and (Term[0].Factor[0].Param <> nil);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.BindHaving;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
BindingHaving := True;
|
|
try
|
|
if IsConstant
|
|
or IsParameter then
|
|
exit;
|
|
finally
|
|
BindingHaving := False;
|
|
end;
|
|
for i := 0 to pred(OwnerSelect.SelectionList.SelectionCount) do
|
|
if OwnerSelect.SelectionList.Selection[i].SimpleExpression.Equals(
|
|
Self) then begin
|
|
BoundHavingField := OwnerSelect.HavingTable.Field(i);
|
|
BoundHaving := True;
|
|
exit;
|
|
end;
|
|
(* test code
|
|
{attempt to bind to aliased expression}
|
|
else
|
|
if OwnerSelect.SelectionList.Selection[i].Column <> nil then begin
|
|
if SameText(OwnerSelect.SelectionList.Selection[i].Column.ColumnName,
|
|
trim(Self.SQLText)) then begin
|
|
BoundHavingField := OwnerSelect.HavingTable.Field(i);
|
|
BoundHaving := True;
|
|
exit;
|
|
end;
|
|
end;
|
|
*)
|
|
SQLError('Expression in HAVING clause doesn''t match any columns');
|
|
end;
|
|
{--------}
|
|
function PropagateType(Type1, Type2: TffFieldType): TffFieldType;
|
|
|
|
function IsInt(Type1: TffFieldType): Boolean;
|
|
begin
|
|
Result := Type1 in [fftByte, fftWord16, fftWord32,
|
|
fftInt8, fftInt16, fftInt32, fftAutoInc];
|
|
end;
|
|
|
|
function IsSigned(Type1: TffFieldType): Boolean;
|
|
begin
|
|
Result := Type1 in [fftInt8, fftInt16, fftInt32];
|
|
end;
|
|
|
|
begin
|
|
if Type1 = Type2 then
|
|
Result := Type1
|
|
else
|
|
if IsInt(Type1) then
|
|
if IsInt(Type2) then
|
|
if IsSigned(Type1) then
|
|
if IsSigned(Type2) then
|
|
Result := fftInt32
|
|
else
|
|
Result := fftSingle
|
|
else
|
|
if IsSigned(Type2) then
|
|
Result := fftSingle
|
|
else
|
|
Result := fftWord32
|
|
else
|
|
Result := Type2
|
|
else
|
|
if IsInt(Type2) then
|
|
Result := Type1
|
|
else
|
|
Result := fftExtended;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.CheckType;
|
|
var
|
|
i : Integer;
|
|
Type2: TffFieldType;
|
|
begin
|
|
FType := Term[0].GetType;
|
|
if TermCount > 1 then begin
|
|
case Term[1].AddOp of
|
|
aoPlus :
|
|
case FType of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency,
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime,
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString,
|
|
fftShortAnsiStr,
|
|
fftNullString,
|
|
fftNullAnsiStr,
|
|
fftWideString :
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
aoMinus :
|
|
case FType of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency:
|
|
;
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime :
|
|
case Term[1].GetType of
|
|
fftStDate, fftStTime, fftDateTime :
|
|
FType := fftDouble;
|
|
end; { case }
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
aoConcat :
|
|
case FType of
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString,
|
|
fftShortAnsiStr,
|
|
fftNullString,
|
|
fftNullAnsiStr,
|
|
fftWideString :
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
end;
|
|
for i := 1 to pred(TermCount) do begin
|
|
Type2 := Term[i].GetType;
|
|
case Term[i].AddOp of
|
|
aoPlus :
|
|
case Type2 of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency,
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString,
|
|
fftShortAnsiStr,
|
|
fftNullString,
|
|
fftNullAnsiStr,
|
|
fftWideString,
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime,
|
|
fftInterval:
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
aoMinus :
|
|
case Type2 of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency,
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime,
|
|
fftInterval:
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
aoConcat :
|
|
case Type2 of
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString,
|
|
fftShortAnsiStr,
|
|
fftNullString,
|
|
fftNullAnsiStr,
|
|
fftWideString :
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
end;
|
|
case Type2 of
|
|
fftByte, fftWord16, fftWord32, fftInt8, fftInt16, fftInt32,
|
|
fftAutoInc, fftSingle, fftDouble, fftExtended, fftComp, fftCurrency :
|
|
FType := PropagateType(FType, Type2);
|
|
end;
|
|
end;
|
|
end;
|
|
TypeKnown := True;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetDecimals: Integer;
|
|
var
|
|
i, j : Integer;
|
|
begin
|
|
Result := Term[0].GetDecimals;
|
|
for i := 1 to pred(TermCount) do begin
|
|
j := Term[i].GetDecimals;
|
|
if j > Result then
|
|
Result := j;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetSize: Integer;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := Term[0].GetSize;
|
|
{operator here can only be aoConcat
|
|
(because GetSize is only called for text fields)}
|
|
for i := 1 to pred(TermCount) do
|
|
inc(Result, Term[i].GetSize);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.GetType: TffFieldType;
|
|
begin
|
|
if not TypeKnown then
|
|
CheckType;
|
|
Result := FType
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsAggregate: Boolean;
|
|
begin
|
|
Result := (TermCount = 1) and Term[0].IsAggregate;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.CheckIsConstant;
|
|
var
|
|
i : Integer;
|
|
Save : TffSqlAggQueryMode;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
for i := 0 to pred(TermCount) do
|
|
if not Term[i].IsConstant then begin
|
|
FIsConstant := False;
|
|
exit;
|
|
end;
|
|
if not BindingHaving then begin
|
|
Save := aqmIdle;
|
|
if assigned(OwnerSelect) then begin
|
|
Save := OwnerSelect.AggQueryMode;
|
|
OwnerSelect.AggQueryMode := aqmIdle;
|
|
end;
|
|
ConstantValue := GetValue;
|
|
if assigned(OwnerSelect) then
|
|
OwnerSelect.AggQueryMode := Save;
|
|
end;
|
|
FIsConstant := True;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.MatchType(ExpectedType: TffFieldType);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
Term[i].MatchType(ExpectedType);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpression.Reduce: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TermCount) do
|
|
if Term[i].Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpression.SetTerm(Index: Integer;
|
|
const Value: TffSqlTerm);
|
|
begin
|
|
TermList[Index] := Value;
|
|
end;
|
|
{Begin !!.11}
|
|
{--------}
|
|
function TffSqlSimpleExpression.WasWildcard : Boolean;
|
|
begin
|
|
if TermCount = 1 then
|
|
Result := Term[0].WasWildcard
|
|
else
|
|
Result := False;
|
|
end;
|
|
{End !!.11}
|
|
{====================================================================}
|
|
|
|
{===TffSqlTerm=======================================================}
|
|
function TffSqlTerm.AddFactor(Factor: TffSqlFactor): TffSqlFactor;
|
|
begin
|
|
FactorList.Add(Factor);
|
|
Result := Factor;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.AddIntervalTo(Target: TDateTime): TDateTime;
|
|
begin
|
|
Result := Factor[0].AddIntervalTo(Target);
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
begin
|
|
Result := Factor[0].SubtractIntervalFrom(Target);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.CheckIsConstant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
for i := 0 to pred(FactorCount) do
|
|
if not Factor[i].IsConstant then begin
|
|
FIsConstant := False;
|
|
exit;
|
|
end;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.CheckType;
|
|
var
|
|
i : Integer;
|
|
Type2: TffFieldType;
|
|
begin
|
|
FType := Factor[0].GetType;
|
|
if FactorCount > 1 then begin
|
|
case Factor[1].MulOp of
|
|
moMul, moDiv :
|
|
case FType of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency :
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
end;
|
|
for i := 1 to pred(FactorCount) do begin
|
|
case Factor[i].MulOp of
|
|
moMul, moDiv :
|
|
begin
|
|
Type2 := Factor[i].GetType;
|
|
case Type2 of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency :
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
case Type2 of
|
|
fftByte, fftWord16, fftWord32, fftInt8, fftInt16, fftInt32,
|
|
fftAutoInc, fftSingle, fftDouble, fftExtended, fftComp,
|
|
fftCurrency :
|
|
FType := PropagateType(FType, Type2);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
TypeKnown := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlTerm then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlTerm(Source).FactorCount) do begin
|
|
AddFactor(TffSqlFactor.Create(Self)).Assign(
|
|
TffSqlTerm(Source).Factor[i]);
|
|
end;
|
|
AddOp := TffSqlTerm(Source).AddOp;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
constructor TffSqlTerm.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FactorList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
Factor[i].Free;
|
|
FactorList.Clear;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
if Factor[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlTerm.Destroy;
|
|
begin
|
|
Clear;
|
|
FactorList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.EmitSQL(Stream: TStream);
|
|
const
|
|
MulOpStr : array[TffSqlMulOp] of string = (' * ', ' / ');
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Factor[0].EmitSQL(Stream);
|
|
for i := 1 to pred(FactorCount) do begin
|
|
WriteStr(Stream, MulOpStr[Factor[i].MulOp]);
|
|
Factor[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(FactorCount) do
|
|
Factor[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if (Other is TffSqlTerm)
|
|
and (AddOp = TffSqlTerm(Other).AddOp) then begin
|
|
if FactorCount <> TffSqlTerm(Other).FactorCount then
|
|
exit;
|
|
for i := 0 to pred(FactorCount) do
|
|
if not Factor[i].Equals(TffSqlTerm(Other).Factor[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetFactor(Index: Integer): TffSqlFactor;
|
|
begin
|
|
Result := TffSqlFactor(FactorList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetFactorCount: Integer;
|
|
begin
|
|
Result := FactorList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetDecimals: Integer;
|
|
var
|
|
i, j : Integer;
|
|
begin
|
|
Result := Factor[0].GetDecimals;
|
|
for i := 1 to pred(FactorCount) do begin
|
|
j := Factor[i].GetDecimals;
|
|
if j > Result then
|
|
Result := j;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetSize: Integer;
|
|
begin
|
|
Result := Factor[0].GetSize;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
if FactorCount = 1 then
|
|
Result := Factor[0].GetTitle(Qualified) {!!.11}
|
|
else
|
|
Result := 'EXP';
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetType: TffFieldType;
|
|
begin
|
|
if not TypeKnown then
|
|
CheckType;
|
|
Result := FType
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.GetValue: Variant;
|
|
var
|
|
i : Integer;
|
|
Op: Variant;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
Result := Factor[0].GetValue;
|
|
if VarIsNull(Result) then exit;
|
|
for i := 1 to pred(FactorCount) do begin
|
|
Op := Factor[i].GetValue;
|
|
if VarIsNull(Op) then begin
|
|
Result := Null;
|
|
exit;
|
|
end;
|
|
case Factor[i{1}].MulOp of {!!.11}
|
|
moMul :
|
|
Result := Result * Op;
|
|
moDiv :
|
|
Result := Result / Op;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.IsAggregate: Boolean;
|
|
begin
|
|
Result := (FactorCount = 1) and Factor[0].IsAggregate;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.HasFieldRef: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
if Factor[i].HasFieldRef then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.IsAggregateExpression: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
if Factor[i].IsAggregate then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.IsField(var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
begin
|
|
Result := (FactorCount = 1) and Factor[0].IsField(FieldReferenced);
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy; var SameCase: Boolean): Boolean;
|
|
begin
|
|
Result := (FactorCount = 1) and Factor[0].IsFieldFrom(Table, FieldReferenced,
|
|
SameCase);
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.IsNull: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
if Factor[i].IsNull then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.MatchType(ExpectedType: TffFieldType);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
Factor[i].MatchType(ExpectedType);
|
|
end;
|
|
{--------}
|
|
function TffSqlTerm.Reduce: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FactorCount) do
|
|
if Factor[i].Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTerm.SetFactor(Index: Integer;
|
|
const Value: TffSqlFactor);
|
|
begin
|
|
FactorList[Index] := Value;
|
|
end;
|
|
{Begin !!.11}
|
|
{--------}
|
|
function TffSqlTerm.WasWildcard : Boolean;
|
|
begin
|
|
if FactorCount = 1 then
|
|
Result := Factor[0].WasWildcard
|
|
else
|
|
Result := False;
|
|
end;
|
|
{End !!.11}
|
|
{====================================================================}
|
|
|
|
{===TffSqlCondExp====================================================}
|
|
function TffSqlCondExp.AddCondTerm(Term: TffSqlCondTerm): TffSqlCondTerm;
|
|
begin
|
|
CondTermList.Add(Term);
|
|
Result := Term;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.AsBooleanLevel(Level: Integer): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
for i := 0 to pred(CondTermCount) do
|
|
if CondTerm[i].AsBooleanLevel(Level) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.AsBoolean: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
for i := 0 to pred(CondTermCount) do
|
|
if CondTerm[i].AsBoolean then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondExp.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlCondExp then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlCondExp(Source).CondTermCount) do
|
|
AddCondTerm(TffSqlCondTerm.Create(Self)).Assign(
|
|
TffSqlCondExp(Source).CondTerm[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlCondExp.BindHaving;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondTermCount) do
|
|
CondTerm[i].BindHaving;
|
|
end;
|
|
|
|
procedure TffSqlCondExp.CheckIsConstant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
for i := 0 to pred(CondTermCount) do
|
|
if not CondTerm[i].IsConstant then begin
|
|
FIsConstant := False;
|
|
exit;
|
|
end;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
|
|
constructor TffSqlCondExp.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
CondTermList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondExp.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondTermCount) do
|
|
CondTerm[i].Free;
|
|
CondTermList.Clear;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondTermCount) do
|
|
if CondTerm[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlCondExp.Destroy;
|
|
begin
|
|
Clear;
|
|
CondTermList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondExp.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
CondTerm[0].EmitSQL(Stream);
|
|
for i := 1 to pred(CondTermCount) do begin
|
|
WriteStr(Stream, ' OR');
|
|
CondTerm[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondExp.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(CondTermCount) do
|
|
CondTerm[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlCondExp then begin
|
|
if CondTermCount <> TffSqlCondExp(Other).CondTermCount then
|
|
exit;
|
|
for i := 0 to pred(CondTermCount) do
|
|
if not CondTerm[i].Equals(TffSqlCondExp(Other).CondTerm[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.GetCondTerm(
|
|
Index: Integer): TffSqlCondTerm;
|
|
begin
|
|
Result := TffSqlCondTerm(CondTermList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.GetCondTermCount: Integer;
|
|
begin
|
|
Result := CondTermList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.GetDecimals: Integer;
|
|
begin
|
|
if CondTermCount > 1 then
|
|
TypeMismatch;
|
|
Result := CondTerm[0].GetDecimals;
|
|
end;
|
|
{--------}
|
|
{!!.10 new}
|
|
function TffSqlCondExp.GetSize: Integer;
|
|
begin
|
|
if CondTermCount > 1 then
|
|
Result := 1
|
|
else
|
|
Result := CondTerm[0].GetSize;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
if CondTermCount > 1 then
|
|
Result := 'COND'
|
|
else
|
|
Result := CondTerm[0].GetTitle(Qualified); {!!.11}
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.GetType: TffFieldType;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if CondTermCount > 1 then begin
|
|
{force type conversion at lower level if necessary}
|
|
for i := 0 to pred(CondTermCount) do
|
|
CondTerm[i].GetType;
|
|
Result := fftBoolean
|
|
end else
|
|
Result := CondTerm[0].GetType;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.GetValue: Variant;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
if CondTermCount > 1 then
|
|
Result := AsBoolean
|
|
else
|
|
Result := CondTerm[0].GetValue;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
procedure TffSqlCondExp.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
if CondTermCount = 1 then {!!.11}
|
|
CondTerm[0].MatchType(ExpectedType) {!!.11}
|
|
else {!!.11}
|
|
if GetType <> ExpectedType then
|
|
TypeMismatch;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondExp.Reduce: Boolean;
|
|
var
|
|
i,j : Integer;
|
|
InFactIX,
|
|
InTermIX: Integer;
|
|
NewTerm, LiftTerm : TffSqlCondTerm;
|
|
NewFactor: TffSqlCondFactor;
|
|
NewPrimary: TffSqlCondPrimary;
|
|
LiftInClause: TffSqlInClause;
|
|
LiftInExp: TffSqlSimpleExpression;
|
|
LiftExp : TffSqlCondExp;
|
|
begin
|
|
Result := False;
|
|
LiftInClause := nil;
|
|
LiftInExp := nil;
|
|
LiftExp := nil;
|
|
InTermIX := -1; //just to make the compiler happy
|
|
InFactIX := -1; //just to make the compiler happy
|
|
for i := 0 to pred(CondTermCount) do begin
|
|
{look for conditional terms nested inside redundant parens}
|
|
{eliminate parens when found}
|
|
LiftTerm := nil;
|
|
LiftExp := nil;
|
|
with CondTerm[i] do begin
|
|
if CondFactorCount = 1 then begin
|
|
with CondFactor[0] do
|
|
if not UnaryNot then
|
|
if (CondPrimary.RelOp = roNone) then
|
|
if CondPrimary.SimpleExp1 <> nil then
|
|
if CondPrimary.JustSimpleExpression then
|
|
with CondPrimary.SimpleExp1 do
|
|
if TermCount = 1 then begin
|
|
with Term[0] do
|
|
if FactorCount = 1 then
|
|
with Factor[0] do
|
|
if CondExp <> nil then
|
|
with CondExp do
|
|
if CondTermCount = 1 then begin
|
|
LiftTerm := TffSqlCondTerm.Create(Self);
|
|
LiftTerm.Assign(CondTerm[0]);
|
|
end;
|
|
end;
|
|
end;
|
|
if LiftTerm <> nil then begin
|
|
Clear;
|
|
Assign(LiftTerm);
|
|
LiftTerm.Free;
|
|
Result := True;
|
|
{Get out. We may have more to do here, but Global Logic will
|
|
call us again, and there may be other transformations that can
|
|
be applied first.}
|
|
break;
|
|
end;
|
|
if Reduce then begin
|
|
{term itself was reduced}
|
|
Result := True;
|
|
break;
|
|
end;
|
|
if not Result then begin
|
|
{look for IN expressions to be converted to simple comparisons}
|
|
for j := 0 to pred(CondFactorCount) do
|
|
with CondFactor[j] do
|
|
if not UnaryNot then {can't handle negated expressions}
|
|
if CondPrimary.RelOp = roNone then
|
|
if (CondPrimary.InClause <> nil)
|
|
and not (CondPrimary.InClause.Negated)
|
|
and (CondPrimary.InClause.SubQuery = nil)
|
|
and (CondPrimary.InClause.SimpleExpList.ExpressionCount <=
|
|
ffSqlInConvThreshold) then begin
|
|
{Here's one. Make a copy of it and get up to the
|
|
root level since we'll be doing surgery on this
|
|
very node hierarchy we're current looking at}
|
|
LiftInClause := TffSqlInClause.Create(Self);
|
|
LiftInClause.Assign(CondPrimary.InClause);
|
|
LiftInExp := TffSqlSimpleExpression.Create(Self);
|
|
LiftInExp.Assign(CondPrimary.SimpleExp1);
|
|
InTermIX := i; // just a reference back to here
|
|
if CondFactorCount > 1 then
|
|
{we have other factors that need to be copied -
|
|
make note of where the IN is - we should copy
|
|
everything BUT}
|
|
InFactIX := j
|
|
{we're the only factor, make a note of that by
|
|
setting the InFactIX flag to -1 indicating no
|
|
other factors should be copied}
|
|
else
|
|
InFactIX := -1;
|
|
break;
|
|
end;
|
|
end;
|
|
if not Result then begin
|
|
{look for nested conditional expressions to be lifted out, like
|
|
(A OR B) AND C to be converted to A AND C OR B AND C}
|
|
for j := 0 to pred(CondFactorCount) do
|
|
with CondFactor[j] do
|
|
if not UnaryNot then
|
|
if (CondPrimary.RelOp = roNone) then
|
|
if CondPrimary.SimpleExp1 <> nil then
|
|
if CondPrimary.JustSimpleExpression then
|
|
with CondPrimary.SimpleExp1 do
|
|
if TermCount = 1 then begin
|
|
with Term[0] do
|
|
if FactorCount = 1 then
|
|
with Factor[0] do
|
|
if CondExp <> nil then begin
|
|
LiftExp := TffSqlCondExp.Create(Self);
|
|
LiftExp.Assign(CondExp);
|
|
InTermIX := i; // A reference back to here
|
|
InFactIX := j; // A reference back to here
|
|
end;
|
|
end;
|
|
end;
|
|
if LiftInClause <> nil then
|
|
break;
|
|
if LiftExp <> nil then
|
|
break;
|
|
end;
|
|
end;
|
|
if LiftExp <> nil then begin
|
|
{create a top-level conditional term for each nested term,
|
|
then copy each conditional factor except the one we're converting
|
|
to each new term:}
|
|
for i := 0 to pred(LiftExp.CondTermCount) do begin
|
|
NewTerm := TffSqlCondTerm.Create(Self);
|
|
NewTerm.Assign(LiftExp.CondTerm[i]);
|
|
for j := 0 to pred(CondTerm[InTermIX].CondFactorCount) do
|
|
if j <> InFactIX then begin
|
|
NewFactor := TffSqlCondFactor.Create(NewTerm);
|
|
NewFactor.Assign(CondTerm[InTermIX].CondFactor[j]);
|
|
NewTerm.AddCondFactor(NewFactor);
|
|
end;
|
|
AddCondTerm(NewTerm);
|
|
end;
|
|
LiftInClause.Free;
|
|
LiftInExp.Free;
|
|
LiftExp.Free;
|
|
CondTerm[InTermIX].Free;
|
|
CondTermList.Delete(InTermIX);
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if (LiftInClause <> nil)
|
|
and (InFactIX = -1) then begin {only do this optimization if no other factors} {!!.11}
|
|
{Okay - that was the easy bit, finding the IN clause.
|
|
We now need to build conditional terms for each of the
|
|
alternatives - each with a simple comparison corresponding
|
|
to each entry in the IN clause list.}
|
|
for i := 0 to pred(LiftInClause.SimpleExpList.ExpressionCount) do begin
|
|
NewTerm := TffSqlCondTerm.Create(Self);
|
|
NewFactor := TffSqlCondFactor.Create(NewTerm);
|
|
NewPrimary := TffSqlCondPrimary.Create(NewFactor);
|
|
NewPrimary.SimpleExp1 := TffSqlSimpleExpression.Create(NewPrimary);
|
|
NewPrimary.SimpleExp1.Assign(LiftInExp);
|
|
NewPrimary.SimpleExp2 := TffSqlSimpleExpression.Create(NewPrimary);
|
|
NewPrimary.SimpleExp2.Assign(LiftInClause.SimpleExpList.Expression[i]);
|
|
NewPrimary.RelOp := roEQ;
|
|
NewFactor.CondPrimary := NewPrimary;
|
|
NewTerm.AddCondFactor(NewFactor);
|
|
{If we didn't have any other conditional factors
|
|
combined with the IN clause - IOW, we didn't have something like
|
|
Exp IN [blahblah] AND something else,
|
|
then we're actually done. All we need to do is add each term, then
|
|
finish off by deleting the original term which held the IN clause.
|
|
|
|
On the other hand, if we did have other factors, they all need to
|
|
be copied to the new term:}
|
|
if InFactIX <> -1 then begin
|
|
with CondTerm[InTermIX] do
|
|
for j := 0 to pred(CondFactorCount) do
|
|
if j <> InFactIX then begin
|
|
NewFactor := TffSqlCondFactor.Create(NewTerm);
|
|
NewFactor.Assign(CondFactor[j]);
|
|
NewTerm.AddCOndFactor(NewFactor);
|
|
end;
|
|
end;
|
|
|
|
AddCondTerm(NewTerm);
|
|
end;
|
|
{LiftInClause.Free;} {!!.12}
|
|
{LiftInExp.Free;} {!!.12}
|
|
//get rid of the original term with the IN clause
|
|
CondTerm[InTermIX].Free;
|
|
CondTermList.Delete(InTermIX);
|
|
Result := True;
|
|
end;
|
|
LiftInClause.Free; {!!.12}
|
|
LiftInExp.Free; {!!.12}
|
|
{!!.11 begin}
|
|
if not Result then
|
|
for i := 0 to pred(CondTermCount) do
|
|
if CondTerm[i].Reduce then begin
|
|
Result := True;
|
|
break;
|
|
end;
|
|
{!!.11 end}
|
|
end;
|
|
|
|
procedure TffSqlCondExp.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
procedure TffSqlCondExp.SetCondTerm(Index: Integer;
|
|
const Value: TffSqlCondTerm);
|
|
begin
|
|
CondTermList[Index] := Value;
|
|
end;
|
|
|
|
procedure TffSqlCondExp.SetLevelDep(List: TFFSqlTableProxySubsetList);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondTermCount) do
|
|
CondTerm[i].SetLevelDep(List);
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
|
|
{===TffSqlCondTerm===================================================}
|
|
function TffSqlCondTerm.AddCondFactor(Factor: TffSqlCondFactor): TffSqlCondFactor;
|
|
begin
|
|
CondFactorList.Add(Factor);
|
|
Result := Factor;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.InsertCondFactor(Index: Integer;
|
|
Factor : TffSqlCondFactor): TffSqlCondFactor;
|
|
begin
|
|
CondFactorList.Insert(Index, Factor);
|
|
Result := Factor;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondTerm.SetLevelDep(List: TFFSqlTableProxySubsetList);
|
|
var
|
|
F, Level : Integer;
|
|
begin
|
|
for F := 0 to pred(CondFactorCount) do
|
|
with CondFactor[F] do begin
|
|
EvalLevel := List.Count;
|
|
for Level := pred(List.Count) downto 0 do
|
|
if DependsOn(List.Item[Level].Table) then
|
|
EvalLevel := Level;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlCondTerm.AsBoolean: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
for i := 0 to pred(CondFactorCount) do
|
|
if not CondFactor[i].AsBoolean then begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.AsBooleanLevel(Level: Integer): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
for i := 0 to pred(CondFactorCount) do
|
|
if (CondFactor[i].EvalLevel >= Level)
|
|
and not CondFactor[i].AsBoolean then begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondTerm.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlCondTerm then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlCondTerm(Source).CondFactorCount) do begin
|
|
AddCondFactor(TffSqlCondFactor.Create(Self)).Assign(
|
|
TffSqlCondTerm(Source).CondFactor[i]);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlCondTerm.BindHaving;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondFactorCount) do
|
|
CondFactor[i].BindHaving;
|
|
end;
|
|
|
|
procedure TffSqlCondTerm.CheckIsConstant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
for i := 0 to pred(CondFactorCount) do
|
|
if not CondFactor[i].IsConstant then begin
|
|
FIsConstant := False;
|
|
exit;
|
|
end;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
|
|
constructor TffSqlCondTerm.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
CondFactorList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondTerm.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondFactorCount) do
|
|
CondFactor[i].Free;
|
|
CondFactorList.Clear;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(CondFactorCount) do
|
|
if CondFactor[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlCondTerm.Destroy;
|
|
begin
|
|
Clear;
|
|
CondFactorList.Free;
|
|
OrderedSources.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondTerm.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
CondFactor[0].EmitSQL(Stream);
|
|
for i := 1 to pred(CondFactorCount) do begin
|
|
WriteStr(Stream,' AND');
|
|
CondFactor[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondTerm.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(CondFactorCount) do
|
|
CondFactor[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlCondTerm then begin
|
|
if CondFactorCount <> TffSqlCondTerm(Other).CondFactorCount then
|
|
exit;
|
|
for i := 0 to pred(CondFactorCount) do
|
|
if not CondFactor[i].Equals(TffSqlCondTerm(Other).CondFactor[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.GetCondFactor(
|
|
Index: Integer): TffSqlCondFactor;
|
|
begin
|
|
Result := TffSqlCondFactor(CondFactorList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.GetCondFactorCount: Integer;
|
|
begin
|
|
Result := CondFactorList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.GetDecimals: Integer;
|
|
begin
|
|
if CondFactorCount > 1 then
|
|
TypeMismatch;
|
|
Result := CondFactor[0].GetDecimals;
|
|
end;
|
|
{--------}
|
|
{!!.10 new}
|
|
function TffSqlCondTerm.GetSize: Integer;
|
|
begin
|
|
if CondFactorCount > 1 then
|
|
Result := 1
|
|
else
|
|
Result := CondFactor[0].GetSize;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
if CondFactorCount > 1 then
|
|
Result := 'COND'
|
|
else
|
|
Result := CondFactor[0].GetTitle(Qualified); {!!.11}
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.GetType: TffFieldType;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if CondFactorCount > 1 then begin
|
|
{force type conversion at lower level if necessary}
|
|
for i := 0 to pred(CondFactorCount) do
|
|
CondFactor[i].GetType;
|
|
Result := fftBoolean
|
|
end else
|
|
Result := CondFactor[0].GetType;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.GetValue: Variant;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
if CondFactorCount > 1 then
|
|
Result := AsBoolean
|
|
else
|
|
Result := CondFactor[0].GetValue;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondTerm.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
{!!.11 new}
|
|
procedure TffSqlCondTerm.MatchType(ExpectedType: TffFieldType);
|
|
var
|
|
i: Integer;
|
|
T: TffFieldType;
|
|
begin
|
|
if CondFactorCount > 1 then begin
|
|
if ExpectedType <> fftBoolean then
|
|
TypeMismatch;
|
|
{force necessary type conversion at lower level}
|
|
T := CondFactor[0].GetType;
|
|
for i := 1 to CondFactorCount - 1 do
|
|
CondFactor[i].MatchType(T);
|
|
end else
|
|
CondFactor[0].MatchType(ExpectedType);
|
|
end;
|
|
|
|
function TffSqlCondTerm.Reduce: Boolean;
|
|
var
|
|
i, j : Integer;
|
|
LiftFactor : TffSqlCondFactor;
|
|
LiftTerm: TffSqlCondTerm;
|
|
B : Boolean;
|
|
begin
|
|
{Look for conditional factors nested inside redundant parens}
|
|
{ - eliminate parens when found}
|
|
{Look for BETWEEN expressions and convert them to two comparisons}
|
|
Result := False;
|
|
for i := 0 to pred(CondFactorCount) do begin
|
|
//LiftFactor := nil;
|
|
LiftTerm := nil;
|
|
with CondFactor[i] do begin
|
|
if (CondPrimary.RelOp = roNone) then
|
|
if CondPrimary.BetweenClause <> nil then begin
|
|
if not CondPrimary.BetweenClause.Negated xor UnaryNot then begin
|
|
{create a new CondPrimary to hold the >= comparison}
|
|
LiftFactor := TffSqlCondFactor.Create(Self);
|
|
LiftFactor.CondPrimary := TffSqlCondPrimary.Create(LiftFactor);
|
|
LiftFactor.CondPrimary.RelOp := roGE;
|
|
LiftFactor.CondPrimary.SimpleExp1 :=
|
|
TffSqlSimpleExpression.Create(LiftFactor.CondPrimary);
|
|
LiftFactor.CondPrimary.SimpleExp1.Assign(CondPrimary.SimpleExp1);
|
|
LiftFactor.CondPrimary.SimpleExp2 :=
|
|
TffSqlSimpleExpression.Create(LiftFactor.CondPrimary);
|
|
LiftFactor.CondPrimary.SimpleExp2.Assign(
|
|
CondPrimary.BetweenClause.SimpleLow);
|
|
InsertCondFactor(i, LiftFactor);
|
|
{convert current CondPrimary to a >= comparison}
|
|
CondPrimary.RelOp := roLE;
|
|
CondPrimary.SimpleExp2 := TffSqlSimpleExpression.Create(CondPrimary);
|
|
CondPrimary.SimpleExp2.Assign(CondPrimary.BetweenClause.SimpleHigh);
|
|
CondPrimary.BetweenClause.Free;
|
|
CondPrimary.BetweenClause := nil;
|
|
Result := True;
|
|
UnaryNot := False;
|
|
break;
|
|
end;
|
|
end else
|
|
if CondPrimary.LikeClause <> nil then begin
|
|
if not CondPrimary.LikeClause.Negated xor UnaryNot then begin
|
|
if CondPrimary.LikeClause.CanLimit then begin
|
|
{create a new CondPrimary to hold the >= comparison}
|
|
LiftFactor := TffSqlCondFactor.Create(Self);
|
|
LiftFactor.CondPrimary := TffSqlCondPrimary.Create(LiftFactor);
|
|
LiftFactor.CondPrimary.RelOp := roGE;
|
|
LiftFactor.CondPrimary.SimpleExp1 := TffSqlSimpleExpression.Create(LiftFactor.CondPrimary);
|
|
LiftFactor.CondPrimary.SimpleExp1.Assign(CondPrimary.SimpleExp1);
|
|
LiftFactor.CondPrimary.SimpleExp2 := CreateLiteralStringExp(LiftFactor, CondPrimary.LikeClause.GetLowLimit);
|
|
InsertCondFactor(i, LiftFactor);
|
|
{create a new CondPrimary to hold the <= comparison}
|
|
LiftFactor := TffSqlCondFactor.Create(Self);
|
|
LiftFactor.CondPrimary := TffSqlCondPrimary.Create(LiftFactor);
|
|
LiftFactor.CondPrimary.RelOp := roL;
|
|
LiftFactor.CondPrimary.SimpleExp1 := TffSqlSimpleExpression.Create(LiftFactor.CondPrimary);
|
|
LiftFactor.CondPrimary.SimpleExp1.Assign(CondPrimary.SimpleExp1);
|
|
LiftFactor.CondPrimary.SimpleExp2 := CreateLiteralStringExp(LiftFactor, CondPrimary.LikeClause.GetHighLimit);
|
|
InsertCondFactor(i, LiftFactor);
|
|
if CondPrimary.LikeClause.CanReplaceWithCompare then begin
|
|
{we no longer need the LIKE clause}
|
|
CondFactor[i + 2].Free;
|
|
CondFactorList.Delete(i + 2); // adjust for the two we just inserted
|
|
end else
|
|
CondPrimary.LikeClause.Limited := True;
|
|
Result := True;
|
|
break;
|
|
end;
|
|
end;
|
|
end else
|
|
if CondPrimary.InClause <> nil then
|
|
else
|
|
if CondPrimary.IsTest <> nil then
|
|
else
|
|
if CondPrimary.ExistsClause <> nil then
|
|
else
|
|
if CondPrimary.UniqueClause <> nil then
|
|
else
|
|
if CondPrimary.MatchClause <> nil then
|
|
else
|
|
if CondPrimary.SimpleExp1 <> nil then
|
|
with CondPrimary.SimpleExp1 do
|
|
if TermCount = 1 then begin
|
|
with Term[0] do
|
|
if FactorCount = 1 then
|
|
with Factor[0] do
|
|
if CondExp <> nil then
|
|
with CondExp do
|
|
if CondTermCount = 1 then
|
|
LiftTerm := CondTerm[0];
|
|
end;
|
|
if LiftTerm <> nil then begin
|
|
//first lift all but the very first conditional factor to this level
|
|
for j := 1 to pred(LiftTerm.CondFactorCount) do
|
|
Self.AddCondFactor(TffSqlCondFactor.Create(Self)).
|
|
Assign(LiftTerm.CondFactor[j]);
|
|
//then copy the contents of the first conditional factor
|
|
// (possibly the only one) into this one
|
|
B := UnaryNot; // save UnaryNot setting
|
|
LiftFactor := TffSqlCondFactor.Create(Self);
|
|
LiftFactor.Assign(LiftTerm.CondFactor[0]);
|
|
Clear;
|
|
Assign(LiftFactor);
|
|
LiftFactor.Free;
|
|
UnaryNot := UnaryNot xor B;
|
|
Result := True;
|
|
{Get out. We may have more to do here, but Global Logic will
|
|
call us again, and there may be other transformations that can
|
|
be applied first.}
|
|
break;
|
|
end;
|
|
if Reduce then begin
|
|
{factor itself was reduced}
|
|
Result := True;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
{!!.11 begin}
|
|
if not Result then
|
|
for i := 0 to pred(CondFactorCount) do
|
|
if CondFactor[i].Reduce then begin
|
|
Result := True;
|
|
break;
|
|
end;
|
|
{!!.11 end}
|
|
end;
|
|
|
|
procedure TffSqlCondTerm.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
procedure TffSqlCondTerm.SetCondFactor(Index: Integer;
|
|
const Value: TffSqlCondFactor);
|
|
begin
|
|
CondFactorList[Index] := Value;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlGroupColumnList=================================================}
|
|
function TffSqlGroupColumnList.AddColumn(Column: TffSqlGroupColumn):
|
|
TffSqlGroupColumn;
|
|
begin
|
|
ColumnList.Add(Column);
|
|
Result := Column;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlGroupColumnList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlGroupColumnList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlGroupColumnList(Source).ColumnCount) do
|
|
AddColumn(TffSqlGroupColumn.Create(Self)).Assign(
|
|
TffSqlGroupColumnList(Source).Column[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumnList.Contains(const aColName : string;
|
|
Se: TffSqlSelection): Boolean;
|
|
{Rewritten !!.06}
|
|
var
|
|
i : Integer;
|
|
aGrpColText,
|
|
aSelText : string;
|
|
begin
|
|
if Assigned(Se.SimpleExpression.Term[0].Factor[0].FieldRef) then
|
|
aSelText := Trim(Se.SimpleExpression.Term[0].Factor[0].FieldRef.QualName)
|
|
else
|
|
aSelText := Trim(Se.SQLText);
|
|
|
|
for i := 0 to pred(ColumnCount) do begin
|
|
aGrpColText := Trim(Column[i].QualColumnName);
|
|
Result := (AnsiCompareText(aColName, aGrpColText) = 0) or
|
|
(AnsiCompareText(aSelText, aGrpColText) = 0);
|
|
if Result then
|
|
Exit;
|
|
end; { for }
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
constructor TffSqlGroupColumnList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
ColumnList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlGroupColumnList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ColumnCount) do
|
|
Column[i].Free;
|
|
ColumnList.Clear;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlGroupColumnList.Destroy;
|
|
begin
|
|
Clear;
|
|
ColumnList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlGroupColumnList.EmitSQL(Stream: TStream);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Column[0].EmitSQL(Stream);
|
|
for i := 1 to pred(ColumnCount) do begin
|
|
WriteStr(Stream,', ');
|
|
Column[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlGroupColumnList.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(ColumnCount) do
|
|
Column[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumnList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlGroupColumnList then begin
|
|
if ColumnCount <> TffSqlGroupColumnList(Other).ColumnCount then
|
|
exit;
|
|
for i := 0 to pred(ColumnCount) do
|
|
if not Column[i].Equals(TffSqlGroupColumnList(Other).Column[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumnList.GetColumn(Index: Integer): TffSqlGroupColumn;
|
|
begin
|
|
Result := TffSqlGroupColumn(ColumnList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumnList.GetColumnCount: Integer;
|
|
begin
|
|
Result := ColumnList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumnList.Reduce: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlGroupColumnList.SetColumn(Index: Integer;
|
|
const Value: TffSqlGroupColumn);
|
|
begin
|
|
ColumnList[Index] := VAlue;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlTableRefList===============================================}
|
|
function TffSqlTableRefList.AddTableRef(
|
|
NewTableRef: TffSqlTableRef): TffSqlTableRef;
|
|
begin
|
|
FTableRefList.Add(NewTableRef);
|
|
Result := NewTableRef;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTableRefList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Source is TffSqlTableRefList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlTableRefList(Source).TableRefCount) do
|
|
AddTableRef(TffSqlTableRef.Create(Self)).Assign(
|
|
TffSqlTableRefList(Source).TableRef[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
constructor TffSqlTableRefList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FTableRefList := TList.Create;
|
|
end;
|
|
{--------}
|
|
function TffSqlTableRefList.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := nil;
|
|
for i := 0 to pred(TableRefCount) do begin
|
|
Result := TableRef[i].BindFieldDown(TableName, FieldName);
|
|
if Result <> nil then
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlTableRefList.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := nil;
|
|
for i := 0 to pred(TableRefCount) do begin
|
|
Result := TableRef[i].BindTable(AOwner, TableName);
|
|
if Result <> nil then
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlTableRefList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(TableRefCount) do
|
|
TableRef[i].Free;
|
|
FTableRefList.Clear;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlTableRefList.Destroy;
|
|
begin
|
|
Clear;
|
|
FTableRefList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTableRefList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if TableRefCount > 0 then begin
|
|
TableRef[0].EmitSQL(Stream);
|
|
for i := 1 to pred(TableRefCount) do begin
|
|
WriteStr(Stream,' ,');
|
|
TableRef[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTableRefList.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(TableRefCount) do
|
|
TableRef[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlTableRefList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlTableRefList then begin
|
|
if TableRefCount <> TffSqlTableRefList(Other).TableRefCount then
|
|
exit;
|
|
for i := 0 to pred(TableRefCount) do
|
|
if not TableRef[i].Equals(TffSqlTableRefList(Other).TableRef[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
{!!.11 new}
|
|
function TffSqlTableRefList.GetFieldsFromTable(const TableName: string;
|
|
List: TList): TffSqlTableProxy;
|
|
{-returns fields from table that are ultimately coming from the table
|
|
specified in the TableName argument. NIL if not found.}
|
|
var
|
|
i, j: Integer;
|
|
begin
|
|
Result := nil; {!!.11}
|
|
for i := 0 to TableRefCount - 1 do
|
|
if SameText(TableRef[i].Alias, TableName)
|
|
or SameText(TableRef[i].TableName, TableName) then begin
|
|
Result := TableRef[i].ResultTable;
|
|
for j := 0 to Result.FieldCount - 1 do
|
|
List.Add(Result.Field(j));
|
|
exit;
|
|
end;
|
|
{still here, which means that if there's a match, it's in a nested table}
|
|
for i := 0 to TableRefCount - 1 do begin
|
|
if TableRef[i].TableExp <> nil then {!!.11}
|
|
Result := TableRef[i].TableExp.GetFieldsFromTable(TableName, List);
|
|
if Result <> nil then
|
|
exit;
|
|
end;
|
|
// Result := nil; {Deleted !!.11}
|
|
end;
|
|
{--------}
|
|
function TffSqlTableRefList.GetNameForAlias(const Alias : string) : string;
|
|
var
|
|
Inx : Integer;
|
|
begin
|
|
Result := '';
|
|
for Inx := 0 to Pred(FTableRefList.Count) do begin
|
|
if TffSqlTableRef(FTableRefList[Inx]).Alias = Alias then begin
|
|
Result := TffSqlTableRef(FTableRefList[Inx]).TableName;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlTableRefList.GetTableRef(
|
|
Index: Integer): TffSqlTableRef;
|
|
begin
|
|
Result := TffSqlTableRef(FTableRefList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlTableRefList.GetTableRefCount: Integer;
|
|
begin
|
|
Result := FTableRefList.Count;
|
|
end;
|
|
{--------}
|
|
{!!.11 rewritten}
|
|
function TffSqlTableRefList.Reduce: Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to TableRefCount - 1 do
|
|
if TableRef[i].Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlTableRefList.SetTableRef(Index: Integer;
|
|
const Value: TffSqlTableRef);
|
|
begin
|
|
FTableRefList[Index] := Value;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlStatement==================================================}
|
|
procedure TffSqlStatement.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlStatement then begin
|
|
Clear;
|
|
if TffSqlStatement(Source).Insert <> nil then begin
|
|
Insert := TffSqlINSERT.Create(Self);
|
|
Insert.Assign(TffSqlStatement(Source).Insert);
|
|
end;
|
|
if TffSqlStatement(Source).Update <> nil then begin
|
|
Update := TffSqlUPDATE.Create(Self);
|
|
Update.Assign(TffSqlStatement(Source).Update);
|
|
end;
|
|
if TffSqlStatement(Source).Delete <> nil then begin
|
|
Delete := TffSqlDELETE.Create(Self);
|
|
Delete.Assign(TffSqlStatement(Source).Delete);
|
|
end;
|
|
if TffSqlStatement(Source).TableExp <> nil then begin
|
|
TableExp := TffSqlTableExp.Create(Self);
|
|
TableExp.Assign(TffSqlStatement(Source).TableExp);
|
|
end;
|
|
Reduce := TffSqlStatement(Source).Reduce;
|
|
UseIndex := TffSqlStatement(Source).UseIndex;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{Begin !!.11}
|
|
{--------}
|
|
procedure TffSqlStatement.Bind(const ClientID: TffClientID;
|
|
const SessionID: TffSessionID;
|
|
Database : TffSqlDatabaseProxy);
|
|
begin
|
|
FClientID := ClientID;
|
|
FSessionID := SessionID;
|
|
FDatabase := Database;
|
|
if assigned(Insert) then
|
|
Insert.Bind
|
|
else if assigned(Update) then
|
|
Update.Bind
|
|
else if assigned(Delete) then
|
|
Delete.Bind;
|
|
end;
|
|
{--------}
|
|
{End !!.11}
|
|
procedure TffSqlStatement.Clear;
|
|
begin
|
|
Insert.Free;
|
|
Insert := nil;
|
|
Update.Free;
|
|
Update := nil;
|
|
Delete.Free;
|
|
Delete := nil;
|
|
TableExp.Free;
|
|
TableExp := nil;
|
|
end;
|
|
{--------}
|
|
constructor TffSqlStatement.Create;
|
|
begin
|
|
inherited Create(nil);
|
|
{$IFDEF ExposeLastStatement}
|
|
LastStatement := Self; {debug hook}
|
|
{$ENDIF}
|
|
end;
|
|
{--------}
|
|
destructor TffSqlStatement.Destroy;
|
|
begin
|
|
ParmList.Free;
|
|
Clear;
|
|
inherited;
|
|
{$IFDEF ExposeLastStatement}
|
|
LastStatement := nil; {debug hook}
|
|
{$ENDIF}
|
|
end;
|
|
{--------}
|
|
procedure TffSqlStatement.EmitSQL(Stream: TStream);
|
|
begin
|
|
if not UseIndex then
|
|
WriteStr(Stream,'NOINDEX ');
|
|
if not Reduce then
|
|
WriteStr(Stream,'NOREDUCE ');
|
|
if assigned(Insert) then
|
|
Insert.EmitSQL(Stream);
|
|
if assigned(Update) then
|
|
Update.EmitSQL(Stream);
|
|
if assigned(Delete) then
|
|
Delete.EmitSQL(Stream);
|
|
if assigned(TableExp) then
|
|
TableExp.EmitSQL(Stream);
|
|
WriteStr(Stream,';');
|
|
WriteEOF(Stream);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlStatement.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(Insert) then
|
|
Insert.EnumNodes(EnumMethod, Deep);
|
|
if assigned(Update) then
|
|
Update.EnumNodes(EnumMethod, Deep);
|
|
if assigned(Delete) then
|
|
Delete.EnumNodes(EnumMethod, Deep);
|
|
if assigned(TableExp) then
|
|
TableExp.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlStatement.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlStatement)
|
|
and ((BothNil(Insert, TffSqlStatement(Other).Insert)
|
|
or (BothNonNil(Insert, TffSqlStatement(Other).Insert)
|
|
and Insert.Equals(TffSqlStatement(Other).Insert))))
|
|
and ((BothNil(Update, TffSqlStatement(Other).Update)
|
|
or (BothNonNil(Update, TffSqlStatement(Other).Update)
|
|
and Update.Equals(TffSqlStatement(Other).Update))))
|
|
and ((BothNil(Delete, TffSqlStatement(Other).Delete)
|
|
or (BothNonNil(Delete, TffSqlStatement(Other).Delete)
|
|
and Delete.Equals(TffSqlStatement(Other).Delete))))
|
|
and ((BothNil(TableExp, TffSqlStatement(Other).TableExp)
|
|
or (BothNonNil(TableExp, TffSqlStatement(Other).TableExp)
|
|
and TableExp.Equals(TffSqlStatement(Other).TableExp))));
|
|
end;
|
|
{--------}
|
|
{Begin !!.11}
|
|
function TffSqlStatement.Execute(var aLiveResult: Boolean;
|
|
var aCursorID: TffCursorID;
|
|
var RowsAffected,
|
|
aRecordsRead: integer) : TffResult;
|
|
{End !!.11}
|
|
begin
|
|
Result := DBIERR_NONE; {!!.11}
|
|
StartDate := Date;
|
|
StartTime := Time;
|
|
StartDateTime := Now;
|
|
aCursorID := 0;
|
|
RecordsRead := 0;
|
|
if assigned(TableExp) then
|
|
TableExp.Execute(aLiveResult, aCursorID, RecordsRead)
|
|
{Begin !!.11}
|
|
else if assigned(Insert) then
|
|
Result := Insert.Execute(RowsAffected)
|
|
else if assigned(Update) then
|
|
Result := Update.Execute(RowsAffected)
|
|
else if assigned(Delete) then
|
|
Result := Delete.Execute(RowsAffected)
|
|
else
|
|
raise Exception.Create('Statement is empty');
|
|
{End !!.11}
|
|
aRecordsRead := RecordsRead;
|
|
end;
|
|
{-------}
|
|
procedure TffSqlStatement.ReduceStrength;
|
|
begin
|
|
{$IFDEF LogTransformations}
|
|
AssignFile(TRLog, TRLogFile);
|
|
{$I-}
|
|
Append(TRLog);
|
|
if IOResult <> 0 then
|
|
Rewrite(TRLog);
|
|
writeln(TRLog);
|
|
writeln(TRLog, 'Transforming ' + SQLText);
|
|
writeln(TRLog, 'started at :',DateTimeToStr(Now));
|
|
{$ENDIF}
|
|
|
|
if assigned(TableExp) then begin
|
|
while TableExp.Reduce do begin
|
|
{$IFDEF LogTransformations}
|
|
writeln(TRLog, 'new form:' + SQLText);
|
|
{$ENDIF}
|
|
end;
|
|
end else
|
|
{!!.11 begin}
|
|
if assigned(Insert) then begin
|
|
while Insert.Reduce do begin
|
|
{$IFDEF LogTransformations}
|
|
writeln(TRLog, 'new form:' + SQLText);
|
|
{$ENDIF}
|
|
end;
|
|
end
|
|
else
|
|
if assigned(Update) then begin
|
|
while Update.Reduce do begin
|
|
{$IFDEF LogTransformations}
|
|
writeln(TRLog, 'new form:' + SQLText);
|
|
{$ENDIF}
|
|
end;
|
|
end else
|
|
if assigned(Delete) then begin
|
|
while Delete.Reduce do begin
|
|
{$IFDEF LogTransformations}
|
|
writeln(TRLog, 'new form:' + SQLText);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
{!!.11 end}
|
|
|
|
{$IFDEF LogTransformations}
|
|
writeln(TRLog);
|
|
writeln(TRLog, 'ended at :',DateTimeToStr(Now));
|
|
CloseFile(TRLog);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TffSqlStatement.SetParameter(Index: Integer; Value: Variant);
|
|
begin
|
|
if ParmCount = 0 then
|
|
raise Exception.Create('Error: Attempt to set parameter on non-parameterized query');
|
|
if ParmList = nil then
|
|
ParmList := TFFVariantList.Create(ParmCount);
|
|
ParmList.SetValue(Index, Value);
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlSelect=====================================================}
|
|
{--------}
|
|
procedure TffSqlSELECT.AddTableRefs(Node: TffSqlNode);
|
|
begin
|
|
Node.AddTableReference(Self);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.AddColumns(Node: TffSqlNode);
|
|
begin
|
|
Node.AddColumnDef(Self);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.ClearBindings(Node: TffSqlNode);
|
|
begin
|
|
Node.ClearBinding;
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.Reduce: Boolean;
|
|
begin
|
|
if SelectionList <> nil then
|
|
Result := SelectionList.Reduce
|
|
else
|
|
Result := False;
|
|
Result := Result or TableRefList.Reduce;
|
|
if CondExpWhere <> nil then
|
|
Result := Result or CondExpWhere.Reduce;
|
|
if GroupColumnList <> nil then
|
|
Result := Result or GroupColumnList.Reduce;
|
|
if CondExpHaving <> nil then
|
|
Result := Result or CondExpHaving.Reduce;
|
|
if OrderList <> nil then
|
|
Result := Result or OrderList.Reduce;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.ResetIsConstant(Node: TffSqlNode);
|
|
begin
|
|
Node.ResetConstant;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, 'SELECT');
|
|
if Distinct then
|
|
WriteStr(Stream, ' DISTINCT')
|
|
else
|
|
WriteStr(Stream, ' ALL');
|
|
if (SelectionList = nil) or WasStar then
|
|
WriteStr(Stream, ' *')
|
|
else
|
|
SelectionList.EmitSQL(Stream);
|
|
WriteStr(Stream, ' FROM');
|
|
TableRefList.EmitSQL(Stream);
|
|
if CondExpWhere <> nil then begin
|
|
WriteStr(Stream,' WHERE');
|
|
CondExpWhere.EmitSQL(Stream);
|
|
end;
|
|
if GroupColumnList <> nil then begin
|
|
WriteStr(Stream,' GROUP BY');
|
|
GroupColumnList.EmitSQL(Stream);
|
|
end;
|
|
if CondExpHaving <> nil then begin
|
|
WriteStr(Stream,' HAVING');
|
|
CondExpHaving.EmitSQL(Stream);
|
|
end;
|
|
if OrderList <> nil then
|
|
OrderList.EmitSQL(Stream);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.AddTableFields(Table : TffSqlTableProxy;
|
|
const StartPoint : Integer;
|
|
FieldRef : TffSqlFieldRef);
|
|
var
|
|
Factor : TFFSqlFactor;
|
|
j : Integer;
|
|
Selection : TFFSqlSelection;
|
|
StartVal : Integer;
|
|
Term : TFFSqlTerm;
|
|
begin
|
|
Assert(Table <> nil);
|
|
Assert(Table is TffSqlTableProxy);
|
|
if Table.FieldCount > 0 then begin
|
|
StartVal := Pred(Table.FieldCount);
|
|
{ If passed a field reference then replace its field name with the
|
|
first field of the table. }
|
|
if FieldRef <> nil then begin
|
|
FieldRef.WasWildcard := True;
|
|
FieldRef.FieldName := Table.Field(StartVal).Name;
|
|
dec(StartVal);
|
|
end;
|
|
for j := StartVal downto 0 do begin
|
|
Selection := TffSqlSelection.Create(SelectionList);
|
|
Selection.SimpleExpression :=
|
|
TffSqlSimpleExpression.Create(Selection);
|
|
Term := TFFSqlTerm.Create(Selection.SimpleExpression);
|
|
Factor := TFFSqlFactor.Create(Term);
|
|
Factor.FieldRef := TffSqlFieldRef.Create(Factor);
|
|
if Table.Alias <> '' then {!!.12}
|
|
Factor.FieldRef.TableName := Table.Alias {!!.12}
|
|
else {!!.12}
|
|
Factor.FieldRef.TableName := Table.Name;
|
|
Factor.FieldRef.FieldName := Table.Field(j).Name;
|
|
Term.AddFactor(Factor);
|
|
Selection.AddedByWildcard := True;
|
|
Selection.SimpleExpression.AddTerm(Term);
|
|
SelectionList.InsertSelection(StartPoint, Selection);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.AddTableFieldsFromList(Table : TffSqlTableProxy;
|
|
const StartPoint : Integer;
|
|
FieldRef : TffSqlFieldRef;
|
|
List: TList);
|
|
var
|
|
Factor : TFFSqlFactor;
|
|
j : Integer;
|
|
Selection : TFFSqlSelection;
|
|
StartVal : Integer;
|
|
Term : TFFSqlTerm;
|
|
begin
|
|
Assert(Table <> nil);
|
|
Assert(Table is TffSqlTableProxy);
|
|
if Table.FieldCount > 0 then begin
|
|
StartVal := Pred(List.Count);
|
|
{ If passed a field reference then replace its field name with the
|
|
first field of the table. }
|
|
if FieldRef <> nil then begin
|
|
FieldRef.WasWildcard := True;
|
|
FieldRef.FieldName := TffSqlFieldProxy(List[StartVal]).Name;
|
|
dec(StartVal);
|
|
end;
|
|
for j := StartVal downto 0 do begin
|
|
Selection := TffSqlSelection.Create(SelectionList);
|
|
Selection.SimpleExpression :=
|
|
TffSqlSimpleExpression.Create(Selection);
|
|
Term := TFFSqlTerm.Create(Selection.SimpleExpression);
|
|
Factor := TFFSqlFactor.Create(Term);
|
|
Factor.FieldRef := TffSqlFieldRef.Create(Factor);
|
|
Factor.FieldRef.TableName := Table.Name;
|
|
Factor.FieldRef.FieldName := TffSqlFieldProxy(List[j]).Name;
|
|
Term.AddFactor(Factor);
|
|
Selection.AddedByWildcard := True;
|
|
Selection.SimpleExpression.AddTerm(Term);
|
|
SelectionList.InsertSelection(StartPoint, Selection);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.ExpandWildcards;
|
|
var
|
|
i, j, ix : Integer;
|
|
T : TffSqlTableProxy;
|
|
Simp : TFFSqlSimpleExpression;
|
|
FR : TffSqlFieldRef;
|
|
List: TList; {!!.11}
|
|
begin
|
|
if SelectionList = nil then begin
|
|
{ If the selectionlist is empty then only a wildcard was specified.
|
|
Note that with the fix of issue 481, this is dead code. }
|
|
WasStar := True;
|
|
SelectionList := TffSqlSelectionList.Create(Self);
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
for i := Pred(TablesReferencedByOrder.Count) downto 0 do begin
|
|
T := TffSqlTableProxy(TablesReferencedByOrder.Objects[i]);
|
|
AddTableFields(T, 0, nil);
|
|
end;
|
|
end else begin
|
|
for i := pred(SelectionList.SelectionCount) downto 0 do begin
|
|
Simp := SelectionList.Selection[i].SimpleExpression;
|
|
if Simp <> nil then begin
|
|
FR := Simp.Term[0].Factor[0].FieldRef;
|
|
if FR <> nil then begin
|
|
if FR.FieldName = '' then begin
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
{ If no table name specified then add fields from all tables
|
|
referenced in the FROM clause. }
|
|
if FR.TableName = '' then begin
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
for j := pred(TablesReferencedByOrder.Count) downto 0 do begin
|
|
T := TffSqlTableProxy(TablesReferencedByOrder.Objects[j]);
|
|
if j = 0 then
|
|
AddTableFields(T, i, FR)
|
|
else
|
|
AddTableFields(T, i, nil);
|
|
end;
|
|
end
|
|
else begin
|
|
{ Otherwise the wildcard was qualified with a tablename. }
|
|
ix := TablesReferencedByOrder.IndexOf(FR.TableName);
|
|
if ix = -1 then begin
|
|
Assert(Assigned(TableAliases));
|
|
with TableAliases do begin
|
|
ix := IndexOf(FR.TableName);
|
|
if ix <> -1 then
|
|
ix := Integer(Objects[ix])
|
|
else begin
|
|
{!!.11 begin}
|
|
{might be part of a nested table expression}
|
|
List := TList.Create;
|
|
try
|
|
T := TableRefList.GetFieldsFromTable(FR.TableName, List);
|
|
if T <> nil then begin
|
|
AddTableFieldsFromList(T, i, FR, List);
|
|
ix := -1;
|
|
end else
|
|
{!!.11 end}
|
|
SQLError('Unknown table: ' + FR.TableName);
|
|
finally {!!.11}
|
|
List.Free; {!!.11}
|
|
end; {!!.11}
|
|
end;
|
|
end;
|
|
end;
|
|
if ix <> -1 then begin {!!.11}
|
|
T := TffSqlTableProxy(TablesReferencedByOrder.Objects[ix]);
|
|
AddTableFields(T, i, FR);
|
|
end; {!!.11}
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
if Deep then begin
|
|
EnumMethod(Self);
|
|
if SelectionList <> nil then
|
|
SelectionList.EnumNodes(EnumMethod, Deep);
|
|
TableRefList.EnumNodes(EnumMethod, Deep);
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.EnumNodes(EnumMethod, Deep);
|
|
if GroupColumnList <> nil then
|
|
GroupColumnList.EnumNodes(EnumMethod, Deep);
|
|
if CondExpHaving <> nil then
|
|
CondExpHaving.EnumNodes(EnumMethod, Deep);
|
|
if OrderList <> nil then
|
|
OrderList.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
end;
|
|
{--------}
|
|
{!!.12 debug code
|
|
procedure TffSqlSELECT.CheckTableList;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if TablesReferencedByOrder <> nil then begin
|
|
for i := 0 to pred(TablesReferencedByOrder.Count) do
|
|
if pos('$$UNNAMED', TablesReferencedByOrder[i]) = 0 then
|
|
if assigned(TablesReferencedByOrder.Objects[i]) then
|
|
if not (TObject(TablesReferencedByOrder.Objects[i]) is TffSqlTableProxy) then
|
|
raise Exception.Create('Table list broken');
|
|
end;
|
|
end;
|
|
}
|
|
procedure TffSqlSELECT.ClearTableList;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
{CheckTableList;} {!!.12 debug code}
|
|
if TablesReferencedByOrder <> nil then begin
|
|
for i := 0 to pred(TablesReferencedByOrder.Count) do
|
|
if assigned(TablesReferencedByOrder.Objects[i]) then
|
|
if TffSqlTableProxy(TablesReferencedByOrder.Objects[i]).Owner = Self then begin {!!.10}
|
|
TffSqlTableProxy(TablesReferencedByOrder.Objects[i]).Owner := nil; {!!.10}
|
|
TObject(TablesReferencedByOrder.Objects[i]).Free;
|
|
end; {!!.10}
|
|
TablesReferencedByOrder.Clear;
|
|
end;
|
|
if TableAliases <> nil then
|
|
TableAliases.Clear;
|
|
Bound := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.Bind;
|
|
var
|
|
i, j : Integer;
|
|
T : TffSqlTableProxy;
|
|
Alias: string; {!!.11}
|
|
begin
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.EnumNodes(ClearBindings, False);
|
|
if CondExpHaving <> nil then
|
|
CondExpHaving.EnumNodes(ClearBindings, False);
|
|
ClearTableList;
|
|
TableRefList.EnumNodes(AddTableRefs, False);
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
for i := 0 to pred(TablesReferencedByOrder.Count) do begin
|
|
Assert(TablesReferencedByOrder[i] <> '');
|
|
if pos('$$UNNAMED', TablesReferencedByOrder[i]) <> 0 then
|
|
Assert(TablesReferencedByOrder.Objects[i] <> nil)
|
|
else begin
|
|
j := TableAliases.IndexOfObject(TObject(i));
|
|
if j = -1 then
|
|
Alias := ''
|
|
else
|
|
Alias := TableAliases[j];
|
|
T := Owner.FDatabase.TableByName(Self, TablesReferencedByOrder[i],
|
|
False, Alias); {!!.11}
|
|
if T = nil then
|
|
SQLError('Unable to open table: ' + TablesReferencedByOrder[i] +
|
|
'. Ensure the table exists and is not in use by ' +
|
|
'another process.');
|
|
TablesReferencedByOrder.Objects[i] := T;
|
|
end;
|
|
end;
|
|
ExpandWildcards;
|
|
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.MatchType(fftBoolean);
|
|
|
|
{build column list}
|
|
Assert(Assigned(Columns));
|
|
Columns.Clear;
|
|
SelectionList.EnumNodes(AddColumns, False);
|
|
|
|
{figure out if we're using aggregates}
|
|
{if we are, we need to prepare for those}
|
|
HaveAggregates := False;
|
|
|
|
SelectionList.EnumNodes(FlagAggregates, False);
|
|
|
|
{!!.11 begin}
|
|
if Distinct then begin
|
|
{ensure that all fields have a type we can compare}
|
|
Assert(Assigned(Columns));
|
|
for i := 0 to pred(Columns.Count) do begin
|
|
case TffSqlNode(Columns.Objects[i]).GetType of
|
|
fftBoolean..fftDateTime : ;
|
|
fftShortString..{fftShortAnsiStr}fftWideString : ; {!!.12}
|
|
else
|
|
SQLError('Field ' + Columns[i] + ' has a type, which is incompatible with DISTINCT');
|
|
end;
|
|
end;
|
|
end;
|
|
{!!.11 end}
|
|
Bound := True;
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
var
|
|
T: TFFSqlTableProxy;
|
|
j : Integer;
|
|
begin
|
|
Result := nil;
|
|
if TableName <> '' then begin
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
j := TablesReferencedByOrder.IndexOf(TableName);
|
|
if (j = -1)
|
|
{can't refer to aliased table with its actual name} {!!.12}
|
|
or (TffSqlTableProxy(TablesReferencedByOrder.Objects[j]).Alias <> '') {!!.12}
|
|
then begin
|
|
//may be an alias
|
|
Assert(Assigned(TableAliases));
|
|
with TableAliases do begin
|
|
j := IndexOf(TableName);
|
|
if j <> -1 then begin
|
|
j := Integer(Objects[j]);
|
|
T := TffSqlTableProxy(TablesReferencedByOrder.Objects[j]);
|
|
if T = nil then {!!.11}
|
|
SQLError('Invalid field reference:' + TableName + '.' + FieldName); {!!.11}
|
|
end else begin
|
|
//may be a field from an exclosed expression
|
|
if BindingDown then {!!.11}
|
|
Result := nil {!!.11}
|
|
else
|
|
try {!!.11}
|
|
BindingDown := True; {!!.11}
|
|
Result := TableRefList.BindFieldDown(TableName, FieldName); {!!.11}
|
|
finally {!!.11}
|
|
BindingDown := False; {!!.11}
|
|
end; {!!.11}
|
|
if Result = nil then
|
|
if IsSubQuery then begin
|
|
{may be field at outer level}
|
|
Result := Parent.BindField(TableName, FieldName);
|
|
IsDependent := True;
|
|
exit;
|
|
end;
|
|
{else
|
|
Result := TableRefList.BindFieldDown(TableName, FieldName);} {!!.11}
|
|
if Result = nil then
|
|
SQLError('Unknown field:' + TableName + '.' + FieldName);
|
|
exit;
|
|
end;
|
|
end;
|
|
end else begin
|
|
T := TffSqlTableProxy(TablesReferencedByOrder.Objects[j]);
|
|
Assert(T <> nil, 'Table not resolved:'
|
|
+ TffSqlTableProxy(TablesReferencedByOrder.Objects[j]).Name); {!!.11}
|
|
end;
|
|
Assert(T <> nil);
|
|
Result := T.FieldByName(FieldName);
|
|
if Result = nil then
|
|
SQLError('Unknown field:' + TableName + '.' + FieldName);
|
|
end else begin
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
for j := 0 to pred(TablesReferencedByOrder.Count) do begin
|
|
T := TffSqlTableProxy(TablesReferencedByOrder.Objects[j]);
|
|
Assert(T <> nil);
|
|
Assert(T is TffSqlTableProxy);
|
|
if T.FieldByName(FieldName) <> nil then begin
|
|
Result := T.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
end;
|
|
{ No binding found yet. See if this is an alias for a field in the
|
|
result table. }
|
|
if Joiner <> nil then
|
|
for j := 0 to Pred(Joiner.FT.Count) do begin
|
|
if AnsiCompareText(TFFSqlFieldProxy(Joiner.FT[j]).Name, FieldName) = 0 then begin
|
|
Result := Joiner.FT[j];
|
|
Exit;
|
|
end;
|
|
end;
|
|
SQLError('Unknown field:' + FieldName);
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
Result := TableRefList.BindTable(AOwner, TableName);
|
|
end;
|
|
|
|
{--------}
|
|
function TffSqlSELECT.FindField(const FieldName: string): TFFSqlFieldProxy;
|
|
var
|
|
P : Integer;
|
|
begin
|
|
P := PosCh('.', FieldName);
|
|
if P = 0 then
|
|
Result := BindField('', FieldName)
|
|
else
|
|
Result := BindField(copy(FieldName, 1, P - 1), copy(FieldName, P + 1, MaxInt));
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlSELECT then begin
|
|
Clear;
|
|
Distinct := TffSqlSELECT(Source).Distinct;
|
|
if TffSqlSELECT(Source).SelectionList <> nil then begin
|
|
SelectionList := TffSqlSelectionList.Create(Self);
|
|
SelectionList.Assign(TffSqlSELECT(Source).SelectionList);
|
|
end;
|
|
TableRefList := TffSqlTableRefList.Create(Self);
|
|
TableRefList.Assign(TffSqlSELECT(Source).TableRefList);
|
|
if TffSqlSELECT(Source).CondExpWhere <> nil then begin
|
|
CondExpWhere := TffSqlCondExp.Create(Self);
|
|
CondExpWhere.Assign(TffSqlSELECT(Source).CondExpWhere);
|
|
end;
|
|
if TffSqlSELECT(Source).GroupColumnList <> nil then begin
|
|
GroupColumnList := TffSqlGroupColumnList.Create(Self);
|
|
GroupColumnList.Assign(TffSqlSELECT(Source).GroupColumnList);
|
|
end;
|
|
if TffSqlSELECT(Source).CondExpHaving <> nil then begin
|
|
CondExpHaving := TffSqlCondExp.Create(Self);
|
|
CondExpHaving.Assign(TffSqlSELECT(Source).CondExpHaving);
|
|
end;
|
|
if TffSqlSELECT(Source).OrderList <> nil then begin
|
|
OrderList := TffSqlOrderList.Create(Self);
|
|
OrderList.Assign(TffSqlSELECT(Source).OrderList);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
constructor TffSqlSELECT.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
TablesReferencedByOrder := TStringList.Create;
|
|
TableAliases := TStringList.Create;
|
|
TableAliases.Sorted := True;
|
|
TableAliases.Duplicates := dupError;
|
|
AggQueryMode := aqmIdle;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.Clear;
|
|
begin
|
|
ClearTableList;
|
|
|
|
FSelectionList.Free;
|
|
FSelectionList:= nil;
|
|
|
|
FTableRefList.Free;
|
|
FTableRefList:= nil;
|
|
|
|
FCondExpWhere.Free;
|
|
FCondExpWhere:= nil;
|
|
|
|
FGroupColumnList.Free;
|
|
FGroupColumnList:= nil;
|
|
|
|
FCondExpHaving.Free;
|
|
FCondExpHaving:= nil;
|
|
|
|
FOrderList.Free;
|
|
FOrderList:= nil;
|
|
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if not Bound then
|
|
Bind;
|
|
Result :=
|
|
((CondExpWhere <> nil) and CondExpWhere.DependsOn(Table))
|
|
or ((CondExpHaving <> nil) and CondExpHaving.DependsOn(Table));
|
|
|
|
end;
|
|
{--------}
|
|
destructor TffSqlSELECT.Destroy;
|
|
begin
|
|
if FResultTable <> nil then begin
|
|
FResultTable.Owner := nil;
|
|
FResultTable.Free;
|
|
end;
|
|
Clear;
|
|
TableAliases.Free;
|
|
TablesReferencedByOrder.Free;
|
|
Joiner.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.FlagAggregates(Node: TffSqlNode);
|
|
begin
|
|
Node.FlagAggregate(Self);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.EnumAggregates(Node: TffSqlNode);
|
|
begin
|
|
Node.AddAggregate(AggList);
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] = F then begin
|
|
Result := ResultTable.Field(i);
|
|
exit;
|
|
end;
|
|
Result := nil;
|
|
end;
|
|
|
|
{ TAggCounter }
|
|
|
|
function TAggCounter.GetAvg: Variant;
|
|
begin
|
|
if FCount <> 0 then
|
|
Result := FSum / FCount
|
|
else
|
|
Result := Null;
|
|
end;
|
|
|
|
function TAggCounter.GetMax: Variant;
|
|
begin
|
|
if FCount <> 0 then
|
|
Result := FMax
|
|
else
|
|
Result := Null;
|
|
end;
|
|
|
|
function TAggCounter.GetMin: Variant;
|
|
begin
|
|
if FCount <> 0 then
|
|
Result := FMin
|
|
else
|
|
Result := Null;
|
|
end;
|
|
|
|
function TAggCounter.GetSum: Variant;
|
|
begin
|
|
if FCount <> 0 then
|
|
Result := FSum
|
|
else
|
|
Result := Null;
|
|
end;
|
|
|
|
procedure TAggCounter.Reset;
|
|
begin
|
|
FCount := 0;
|
|
end;
|
|
|
|
const
|
|
NumericVarTypes : set of Byte =
|
|
[varSmallint, varInteger, varSingle,
|
|
{$IFDEF DCC6OrLater}
|
|
varShortInt,
|
|
{$ENDIF}
|
|
varDouble, varCurrency, varByte];
|
|
|
|
procedure TAggCounter.Add(const Value: Variant);
|
|
begin
|
|
if FCount = 0 then begin
|
|
FMin := Value;
|
|
FMax := Value;
|
|
if (VarType(Value) and VarTypeMask) in NumericVarTypes then
|
|
FSum := Value;
|
|
end else begin
|
|
if Value < FMin then
|
|
FMin := Value;
|
|
if Value > FMax then
|
|
FMax := Value;
|
|
if (VarType(Value) and VarTypeMask) in NumericVarTypes then
|
|
FSum := FSum + Value;
|
|
end;
|
|
FCount := FCount + 1;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.EnsureResultTable(NeedData: Boolean);
|
|
begin
|
|
Assert(TObject(Self) is TffSqlSELECT);
|
|
if IsDependent or (NeedData and not HaveData) then begin
|
|
if FResultTable <> nil then begin
|
|
Assert(TObject(FResultTable) is TffSqlTableProxy);
|
|
Assert(FResultTable.Owner = Self);
|
|
FResultTable.Owner := nil;
|
|
FResultTable.Free;
|
|
FResultTable := nil;
|
|
end;
|
|
end;
|
|
if FResultTable = nil then begin
|
|
FResultTable := Execute2(NeedData);
|
|
HaveData := NeedData;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.CheckForValue(Value: Variant): Boolean;
|
|
begin
|
|
EnsureResultTable(True);
|
|
if VarIsNull(Value) then
|
|
Result := False
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := ResultTable.First;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.CheckAllValues(RelOp: TffSqlRelOp;
|
|
const Val: Variant): Boolean;
|
|
var
|
|
TestVal: Variant;
|
|
begin
|
|
EnsureResultTable(True);
|
|
Result := False;
|
|
if VarIsNull(Val) then exit;
|
|
if ResultTable.First then begin
|
|
repeat
|
|
TestVal := ResultTable.Field(0).GetValue;
|
|
if VarIsNull(TestVal) then exit;
|
|
case RelOp of
|
|
roEQ :
|
|
if TestVal <> Val then
|
|
exit;
|
|
roLE :
|
|
if Val > TestVal then
|
|
exit;
|
|
roL :
|
|
if Val >= TestVal then
|
|
exit;
|
|
roG :
|
|
if Val <= TestVal then
|
|
exit;
|
|
roGE :
|
|
if Val < TestVal then
|
|
exit;
|
|
roNE :
|
|
if TestVal = Val then
|
|
exit;
|
|
end;
|
|
until not ResultTable.Next;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.CheckAnyValue(RelOp: TffSqlRelOp;
|
|
const Val: Variant): Boolean;
|
|
begin
|
|
EnsureResultTable(True);
|
|
Result := True;
|
|
if ResultTable.First then
|
|
repeat
|
|
case RelOp of
|
|
roEQ :
|
|
if ResultTable.Field(0).GetValue = Val then
|
|
exit;
|
|
roLE :
|
|
if Val <= ResultTable.Field(0).GetValue then
|
|
exit;
|
|
roL :
|
|
if Val < ResultTable.Field(0).GetValue then
|
|
exit;
|
|
roG :
|
|
if Val > ResultTable.Field(0).GetValue then
|
|
exit;
|
|
roGE :
|
|
if Val >= ResultTable.Field(0).GetValue then
|
|
exit;
|
|
roNE :
|
|
if ResultTable.Field(0).GetValue <> Val then
|
|
exit;
|
|
end;
|
|
until not ResultTable.Next;
|
|
Result := False;
|
|
end;
|
|
|
|
function TffSqlSELECT.CheckNonEmpty: Boolean;
|
|
begin
|
|
EnsureResultTable(True);
|
|
Result := FResultTable.First;
|
|
end;
|
|
|
|
function TffSqlSELECT.GetDecimals: Integer;
|
|
begin
|
|
if not TypeKnown then begin
|
|
EnsureResultTable(False);
|
|
FDecimals := FResultTable.Field(0).GetDecimals;
|
|
FType := FResultTable.Field(0).GetType;
|
|
FSize := FResultTable.Field(0).GetSize; {!!.13}
|
|
TypeKnown := True;
|
|
end;
|
|
Result := FDecimals;
|
|
end;
|
|
|
|
{!!.13 new}
|
|
function TffSqlSELECT.GetSize: Integer;
|
|
begin
|
|
if not TypeKnown then begin
|
|
EnsureResultTable(False);
|
|
FDecimals := FResultTable.Field(0).GetDecimals;
|
|
FType := FResultTable.Field(0).GetType;
|
|
FSize := FResultTable.Field(0).GetSize;
|
|
TypeKnown := True;
|
|
end;
|
|
Result := FSize;
|
|
end;
|
|
|
|
function TffSqlSELECT.GetType: TffFieldType;
|
|
begin
|
|
if not TypeKnown then begin
|
|
EnsureResultTable(False);
|
|
FDecimals := FResultTable.Field(0).GetDecimals;
|
|
FType := FResultTable.Field(0).GetType;
|
|
FSize := FResultTable.Field(0).GetSize; {!!.13}
|
|
TypeKnown := True;
|
|
end;
|
|
Result := FType;
|
|
end;
|
|
|
|
function TffSqlSELECT.GetValue: Variant;
|
|
begin
|
|
EnsureResultTable(True);
|
|
if ResultTable.First then
|
|
Result := ResultTable.Field(0).GetValue
|
|
else
|
|
Result := Null;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.BuildSortList(Table: TffSqlTableProxy; var SortList: TffSqlSortArray);
|
|
{-logic extracted from DoOrderBy}
|
|
var
|
|
i, z, k: Integer;
|
|
IX : Integer;
|
|
s: string;
|
|
FR : TffSqlFieldRef;
|
|
AliasName: string;
|
|
begin
|
|
for i := 0 to pred(OrderList.OrderCount) do begin
|
|
if OrderList.OrderItem[i].Column <> nil then begin
|
|
s := OrderList.OrderItem[i].Column.QualColumnName;
|
|
Assert(Assigned(Columns));
|
|
z := Columns.IndexOf(S);
|
|
if z = -1 then begin
|
|
z := PosCh('.', S);
|
|
if z = 0 then begin
|
|
S := '.' + S;
|
|
// may be unqualified field but qualified columns
|
|
z := -1;
|
|
for k := 0 to pred(Columns.Count) do
|
|
if posI(S, Columns[k]) <> 0 then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
if z = -1 then begin
|
|
SQLError('Unknown column specified in ORDER BY clause: ' +
|
|
Copy(S, 2, Length(S) - 1));
|
|
end;
|
|
end else begin
|
|
// Try to find qualified column
|
|
z := -1;
|
|
{S := Uppercase(S);} {!!.10}
|
|
Assert(Assigned(Columns));
|
|
for k := 0 to pred(Columns.Count) do begin
|
|
FR := (Columns.Objects[k] as TffSQLSimpleExpression).Term[0].Factor[0].FieldRef;
|
|
if Assigned(FR) and
|
|
SameText(S, Trim(FR.SQLText)) then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
end;
|
|
if z = -1 then begin
|
|
//Table might be aliased. Replace alias with corresponding name.
|
|
z := PosCh('.', S);
|
|
AliasName := UpperCase(Copy(s, 1, z-1));
|
|
|
|
Assert(Assigned(TableAliases));
|
|
IX := TableAliases.IndexOf(AliasName);
|
|
if IX <> -1 then begin
|
|
IX := Integer(TableAliases.Objects[IX]);
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
S := TablesReferencedByOrder[IX] + '.' +
|
|
UpperCase(Copy(S, Z+1, MaxInt));
|
|
|
|
//Repeat search for field
|
|
z := -1;
|
|
Assert(Assigned(Columns));
|
|
for k := 0 to Pred(Columns.Count) do begin
|
|
FR := (Columns.Objects[K] as TffSQLSimpleExpression).Term[0].Factor[0].FieldRef;
|
|
if Assigned(FR) and
|
|
SameText(S, Trim(FR.SQLText))
|
|
then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
end;
|
|
end else
|
|
z := -1;
|
|
end;
|
|
|
|
if z = -1 then begin
|
|
// may be qualified field but unqualified columns
|
|
z := PosCh('.', S);
|
|
S := copy(S, z + 1, MaxInt);
|
|
z := -1;
|
|
Assert(Assigned(Columns));
|
|
for k := 0 to pred(Columns.Count) do
|
|
if posI(S, Columns[k]) <> 0 then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
if z = -1 then
|
|
SQLError('Unknown column specified in ORDER BY clause:'+S);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Assert(Assigned(Columns));
|
|
SortList[i] := Table.FieldByName(Columns[z]).Index + 1;
|
|
end else begin
|
|
z := StrToInt(OrderList.OrderItem[i].Index);
|
|
SortList[i] := Table.FieldByName(Columns[z - 1]).Index + 1;
|
|
end;
|
|
if OrderList.OrderItem[i].Descending then
|
|
SortList[i] := -SortList[i];
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoOrderBy;
|
|
var
|
|
SortList: TffSqlSortArray;
|
|
Status : TffResult;
|
|
begin
|
|
if (OrderList <> nil) and NeedData then begin
|
|
|
|
BuildSortList(Table, SortList); {!!.11}
|
|
|
|
Status := Table.Sort(OrderList.OrderCount, SortList, False); {!!.13}
|
|
if Status <> DBIERR_NONE then
|
|
raise EffException.CreateNoData(ffStrResServer, Status);
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.NormalQueryResult(NeedData: Boolean): TffSqlTableProxy;
|
|
var
|
|
i : Integer;
|
|
N : TffSqlNode;
|
|
T2 : TffSqlTableProxy;
|
|
F : TffSqlFieldProxy;
|
|
FieldDefList: TffSqlFieldDefList;
|
|
begin
|
|
|
|
{build a normal answer table}
|
|
|
|
{build field definition for answer table}
|
|
FieldDefList := TffSqlFieldDefList.Create;
|
|
try
|
|
Assert(Assigned(Columns));
|
|
for i := 0 to pred(Columns.Count) do begin
|
|
N := TffSqlNode(Columns.Objects[i]);
|
|
FieldDefList.AddField(Columns[i], N.GetType, N.GetSize, N.GetDecimals);
|
|
end;
|
|
|
|
Result := Owner.FDatabase.CreateTemporaryTableWithoutIndex(Self,
|
|
FieldDefList);
|
|
finally
|
|
FieldDefList.Free;
|
|
end;
|
|
|
|
try
|
|
|
|
if Joiner = nil then begin
|
|
Joiner := TffSqlJoiner.Create(Owner, CondExpWhere);
|
|
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
for i := 0 to pred(TablesReferencedByOrder.Count) do
|
|
Joiner.Sources.Add(
|
|
TFFSqlTableProxySubset.Create(
|
|
TFFSqlTableProxy(TablesReferencedByOrder.Objects[i])));
|
|
|
|
end;
|
|
|
|
Joiner.ClearColumnList;
|
|
|
|
Assert(Assigned(Columns));
|
|
for i := 0 to pred(Columns.Count) do begin
|
|
if TffSqlSimpleExpression(Columns.Objects[i]).IsField(F) then begin
|
|
Joiner.AddColumn(
|
|
nil,
|
|
F,
|
|
Result.Field(i));
|
|
end else begin
|
|
Joiner.AddColumn(
|
|
TffSqlSimpleExpression(Columns.Objects[i]),
|
|
nil,
|
|
Result.Field(i));
|
|
end;
|
|
end;
|
|
|
|
if NeedData then begin
|
|
Joiner.Target := Result;
|
|
Owner.FDatabase.StartTransaction([nil]);
|
|
try
|
|
Joiner.Execute(Owner.UseIndex, nil, jmNone);
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
raise;
|
|
end;
|
|
Owner.FDatabase.Commit;
|
|
end;
|
|
|
|
for i := 0 to Result.FieldCount - 1 do
|
|
Result.Field(i).IsTarget := False;
|
|
|
|
{At this point we have a table with all records that meet the
|
|
WHERE criteria.}
|
|
|
|
{if DISTINCT was specifed, we now need to remove any duplicates}
|
|
|
|
if Distinct and NeedData then begin
|
|
T2 := Result.CopyUnique(Self, True); {!!.13}
|
|
Result.Owner := nil;
|
|
Result.Free;
|
|
Result := T2;
|
|
end;
|
|
|
|
if (Parent is TffSqlInClause) or (Parent is TffSqlMatchClause) then begin
|
|
{need an index to allow the IN and MATCH clauses to be evaluated}
|
|
|
|
T2 := Result.CopySortedOnAllFields(Self);
|
|
|
|
Result.Owner := nil;
|
|
Result.Free;
|
|
Result := T2;
|
|
end else begin
|
|
//do ORDER BY
|
|
|
|
DoOrderBy(NeedData, Result);
|
|
|
|
end;
|
|
except
|
|
Result.Owner := nil;
|
|
Result.Free;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.CheckHaving: Boolean;
|
|
begin
|
|
Result := CondExpHaving.AsBoolean;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoAggOrderBy;
|
|
{-utility method for AggregateQueryResult}
|
|
var
|
|
i, j, z, k, IX: Integer;
|
|
S: string;
|
|
FR : TffSQLFieldRef;
|
|
AliasName : string;
|
|
SortList: TffSqlSortArray;
|
|
Status : TffResult;
|
|
begin
|
|
//do ORDER BY
|
|
if OrderList <> nil then begin
|
|
|
|
j := pred(OrderList.OrderCount);
|
|
for i := 0 to j do begin
|
|
if OrderList.OrderItem[i].Column <> nil then begin
|
|
s := OrderList.OrderItem[i].Column.QualColumnName;
|
|
z := Columns.IndexOf(S);
|
|
if z = -1 then begin
|
|
z := PosCh('.', S);
|
|
if z = 0 then begin
|
|
S := '.' + S;
|
|
// may be unqualified field but qualified columns
|
|
z := -1;
|
|
for k := 0 to pred(Columns.Count) do
|
|
if posI(S, Columns[k]) <> 0 then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
if z = -1 then begin
|
|
SQLError('Unknown column specified in ORDER BY clause: ' +
|
|
Copy(S, 2, Length(S) - 1));
|
|
end;
|
|
end else begin
|
|
// This is a qualified column. Try to find qualified column
|
|
z := -1;
|
|
for k := 0 to pred(Columns.Count) do begin
|
|
FR := (Columns.Objects[k] as TffSQLSimpleExpression).
|
|
Term[0].Factor[0].FieldRef;
|
|
if Assigned(FR) and (posI(S, FR.SQLText) <> 0) then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
end;
|
|
if z = -1 then begin
|
|
//Table might be aliased. Replace alias with corresponding table name
|
|
z := PosCh('.', S);
|
|
AliasName := UpperCase(Copy(s, 1, z-1));
|
|
|
|
Assert(Assigned(TableAliases));
|
|
IX := TableAliases.IndexOf(AliasName);
|
|
if IX <> -1 then begin
|
|
IX := Integer(TableAliases.Objects[IX]);
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
S := TablesReferencedByOrder[IX] + '.' +
|
|
UpperCase(Copy(S, Z+1, MaxInt));
|
|
|
|
//Repeat search for field
|
|
z := -1;
|
|
for k := 0 to Pred(Columns.Count) do begin
|
|
FR := (Columns.Objects[K] as TffSQLSimpleExpression).Term[0].Factor[0].FieldRef;
|
|
if Assigned(FR) and (posI(S, FR.SQLText) <> 0) then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
end;
|
|
end else
|
|
z := -1;
|
|
end;
|
|
|
|
if z = -1 then begin
|
|
// may be qualified field but unqualified columns
|
|
Z := PosCh('.', S);
|
|
S := copy(S, z + 1, MaxInt);
|
|
Z := -1;
|
|
for k := 0 to pred(Columns.Count) do
|
|
if posI(S, Columns[k]) <> 0 then begin
|
|
z := k;
|
|
break;
|
|
end;
|
|
if z = -1 then
|
|
SQLError('Unknown column specified in ORDER BY clause:'+S);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
SortList[i] := FGrpTable.Field(z).Index + 1;
|
|
end else begin
|
|
z := StrToInt(OrderList.OrderItem[i].Index);
|
|
SortList[i] := FGrpTable.Field(z - 1).Index + 1;
|
|
end;
|
|
if OrderList.OrderItem[i].Descending then
|
|
SortList[i] := -SortList[i];
|
|
end;
|
|
|
|
Status := FGrpTable.Sort(j + 1, SortList, False); {!!.13}
|
|
if Status <> DBIERR_NONE then
|
|
raise EffException.CreateNoData(ffStrResServer, Status);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoGroupCopy;
|
|
var
|
|
GroupColumnsOut : Integer;
|
|
FieldDefList: TffSqlFieldDefList;
|
|
i: Integer;
|
|
N : TffSqlNode;
|
|
Se : TffSqlSelection;
|
|
T2 : TffSqlTableProxy;
|
|
|
|
procedure CopyGrouped(const Source, Target: TFFSqlTableProxy;
|
|
GroupColumnsIn, GroupColumnsOut, NonGroupColumns: Integer;
|
|
const GroupColumnTargetField,
|
|
AggExpList: TList);
|
|
|
|
var
|
|
i : Integer;
|
|
IsFirst, HaveGroup, NewGroup : Boolean;
|
|
LastValues : TffVariantList;
|
|
|
|
procedure WriteGroup;
|
|
var
|
|
TgtInfo : TffGroupColumnTargetInfo;
|
|
i : Integer;
|
|
begin
|
|
Target.Insert;
|
|
for i := 0 to pred(GroupColumnsOut) do begin
|
|
TgtInfo := TffGroupColumnTargetInfo(GroupColumnTargetField[i]);
|
|
if TgtInfo <> nil then
|
|
Target.Field(TgtInfo.SelFldIndex).SetValue
|
|
(LastValues.GetValue(TgtInfo.LastValueIndex));
|
|
end;
|
|
for i := 0 to pred(NonGroupColumns) do
|
|
Target.Field(GroupColumnsOut + i).SetValue(
|
|
TffSqlSimpleExpression(AggExpList[i]).GetValue);
|
|
for i := 0 to pred(AggList.Count) do
|
|
TffSqlAggregate(AggList[i]).ResetCounters;
|
|
Target.Post;
|
|
end;
|
|
|
|
|
|
begin
|
|
|
|
Owner.FDatabase.StartTransaction([nil]);
|
|
try
|
|
IsFirst := True;
|
|
HaveGroup := False;
|
|
LastValues := TffVariantList.Create(GroupColumnsIn);
|
|
{we know that the source table has grouping columns first}
|
|
for i := 0 to pred(AggList.Count) do
|
|
TffSqlAggregate(AggList[i]).CreateCounter(Source.Field(i + GroupColumnsIn));
|
|
Source.First;
|
|
while not Source.EOF do begin
|
|
if IsFirst then begin
|
|
IsFirst := False;
|
|
NewGroup := True;
|
|
end else begin
|
|
NewGroup := False;
|
|
for i := 0 to pred(GroupColumnsIn) do
|
|
if Source.Field(i).GetValue <> LastValues.GetValue(i) then begin
|
|
NewGroup := True;
|
|
break;
|
|
end;
|
|
end;
|
|
if NewGroup then begin
|
|
if HaveGroup then begin
|
|
Source.Prior;
|
|
WriteGroup;
|
|
Source.Next;
|
|
end;
|
|
for i := 0 to pred(GroupColumnsIn) do
|
|
LastValues.SetValue(i, Source.Field(i).GetValue);
|
|
HaveGroup := True;
|
|
end;
|
|
|
|
for i := 0 to pred(AggList.Count) do
|
|
TffSqlAggregate(AggList[i]).Update;
|
|
Source.Next;
|
|
end;
|
|
{If we happen to have an empty set AND if we don't have grouping
|
|
columns, an 'empty' record should be added to hold the
|
|
count value of zero as well as null for any aggregates}
|
|
if HaveGroup or (GroupColumnsIn = 0) then
|
|
WriteGroup;
|
|
for i := 0 to pred(AggList.Count) do
|
|
with TffSqlAggregate(AggList[i]) do
|
|
DeleteCounter;
|
|
Owner.FDatabase.Commit;
|
|
finally
|
|
LastValues.Free;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
{build a normal answer table}
|
|
|
|
GroupColumnsOut := 0;
|
|
{build field definition for answer table}
|
|
FieldDefList := TffSqlFieldDefList.Create;
|
|
try
|
|
Assert(Assigned(Columns));
|
|
for i := 0 to pred(Columns.Count) do begin
|
|
N := TffSqlNode(Columns.Objects[i]);
|
|
if i < GroupColumnsIn then {!!.11}
|
|
FieldDefList.AddField(Columns[i],
|
|
N.GetType, N.GetSize, N.GetDecimals)
|
|
else {!!.11}
|
|
{Begin !!.12}
|
|
{ Aggregate fields that reference date, time, & currency fields
|
|
should be of the same type in the result set. Other field
|
|
types should be changed to fftDouble in order to avoid clipping
|
|
of the value. }
|
|
case N.GetType of
|
|
fftCurrency..fftDateTime:
|
|
FieldDefList.AddField(Columns[i],
|
|
N.GetType, N.GetSize, N.GetDecimals);
|
|
else
|
|
FieldDefList.AddField(Columns[i],
|
|
fftDouble, 8, N.GetDecimals);
|
|
end;
|
|
{End !!.12}
|
|
Se := SelectionList.Selection[i];
|
|
if (GroupColumnList <> nil) and
|
|
GroupColumnList.Contains(Columns[i], Se) then
|
|
inc(GroupColumnsOut)
|
|
else
|
|
AggExpList.Add(N);
|
|
end;
|
|
|
|
T2 := Owner.FDatabase.CreateTemporaryTableWithoutIndex(Self, FieldDefList);
|
|
finally
|
|
FieldDefList.Free;
|
|
end;
|
|
|
|
AggQueryMode := aqmGrouping;
|
|
try
|
|
CopyGrouped(
|
|
FGrpTable,
|
|
T2,
|
|
GroupColumnsIn,
|
|
GroupColumnsOut,
|
|
AggExpList.Count,
|
|
GroupColumnTargetField,
|
|
AggExpList);
|
|
finally
|
|
AggQueryMode := aqmIdle;
|
|
end;
|
|
|
|
FGrpTable.Owner := nil;
|
|
FGrpTable.Free;
|
|
FGrpTable := T2;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoHaving;
|
|
var
|
|
T2 : TffSqlTableProxy;
|
|
begin
|
|
if CondExpHaving <> nil then begin
|
|
AggQueryMode := aqmHaving;
|
|
try
|
|
HavingTable := FGrpTable;
|
|
CondExpHaving.BindHaving;
|
|
CondExpHaving.EnumNodes(ResetIsConstant, False);
|
|
T2 := FGrpTable.CopyValidated(Self, CheckHaving);
|
|
FGrpTable.Owner := nil;
|
|
FGrpTable.Free;
|
|
FGrpTable := T2;
|
|
finally
|
|
AggQueryMode := aqmIdle;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoSortOnAll;
|
|
var
|
|
T2 : TffSqlTableProxy;
|
|
begin
|
|
T2 := FGrpTable.CopySortedOnAllFields(Self);
|
|
FGrpTable.Owner := nil; {!!.11}
|
|
FGrpTable.Free;
|
|
FGrpTable := T2;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoRemoveDups(NeedData: Boolean);
|
|
var
|
|
i: Integer;
|
|
LDistinct: Boolean;
|
|
T2 : TffSqlTableProxy;
|
|
begin
|
|
if not Distinct then begin
|
|
LDistinct := False;
|
|
for i := 0 to pred(AggList.Count) do
|
|
if TffSqlAggregate(AggList[i]).Distinct then begin
|
|
LDistinct := True;
|
|
break;
|
|
end;
|
|
end else
|
|
LDistinct := True;
|
|
|
|
if LDistinct and NeedData then begin
|
|
T2 := FGrpTable.CopyUnique(Self, True); {!!.13}
|
|
FGrpTable.Owner := nil;
|
|
FGrpTable.Free;
|
|
FGrpTable := T2;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoBuildGroupingTable;
|
|
var
|
|
FieldDefList: TffSqlFieldDefList;
|
|
i: Integer;
|
|
Co : TffSqlGroupColumn;
|
|
Se : TffSqlSelection;
|
|
F : TffSqlFieldProxy;
|
|
GrpTgtInfo : TffGroupColumnTargetInfo;
|
|
Ag : TffSqlAggregate;
|
|
FldType : TffFieldType;
|
|
begin
|
|
FieldDefList := TffSqlFieldDefList.Create;
|
|
try
|
|
{build field definition for grouping table}
|
|
for i := 0 to pred(GroupColumnsIn) do begin
|
|
Co := GroupColumnList.Column[i];
|
|
Se := SelectionList.FindSelection(Co);
|
|
if Se <> nil then begin
|
|
if Se.SimpleExpression.IsField(F) then begin
|
|
FSF.Add(F);
|
|
FSX.Add(nil);
|
|
end else begin
|
|
FSF.Add(nil);
|
|
FSX.Add(Se.SimpleExpression);
|
|
end;
|
|
GrpTgtInfo := TffGroupColumnTargetInfo.Create;
|
|
GrpTgtInfo.SelFldIndex := Se.Index;
|
|
GrpTgtInfo.LastValueIndex := i;
|
|
GroupColumnTargetField.Add(GrpTgtInfo);
|
|
FieldDefList.AddField(
|
|
Co.QualColumnName,
|
|
Se.SimpleExpression.GetType,
|
|
Se.SimpleExpression.GetSize,
|
|
Se.SimpleExpression.GetDecimals);
|
|
|
|
end else begin
|
|
{grouping field is not in selection list}
|
|
{must be plain field in source table}
|
|
F := FindField(Co.QualColumnName);
|
|
FSF.Add(F);
|
|
FSX.Add(nil);
|
|
FieldDefList.AddField(
|
|
Co.QualColumnName,
|
|
F.GetType,
|
|
F.GetSize,
|
|
F.GetDecimals);
|
|
end;
|
|
end;
|
|
|
|
SelectionList.EnumNodes(EnumAggregates, False);
|
|
|
|
for i := 0 to pred(AggList.Count) do begin
|
|
Ag := TffSqlAggregate(AggList[i]);
|
|
if Ag.SimpleExpression <> nil then begin
|
|
FldType := Ag.SimpleExpression.GetType;
|
|
if not Ag.ValidType(FldType) then
|
|
raise Exception.CreateFmt('The %s aggregate function requires a numeric field.',
|
|
[AgString[Ag.AgFunction]]);
|
|
{AVG() needs float field even for integer expressions}
|
|
if Ag.AgFunction = agAvg then
|
|
FieldDefList.AddField(
|
|
Ag.GetTitle(True) + '$' + IntToStr(i), {!!.11}
|
|
fftDouble,
|
|
0,
|
|
2)
|
|
else
|
|
FieldDefList.AddField(
|
|
Ag.GetTitle(True) + '$' + IntToStr(i), {!!.11}
|
|
FldType,
|
|
Ag.SimpleExpression.GetSize,
|
|
Ag.SimpleExpression.GetDecimals)
|
|
end
|
|
else // COUNT(* )
|
|
FieldDefList.AddField(
|
|
Ag.GetTitle(True) + '$' + IntToStr(i), {!!.11}
|
|
fftDouble,
|
|
0,
|
|
0);
|
|
|
|
end;
|
|
|
|
FGrpTable := Owner.FDatabase.CreateTemporaryTableWithoutIndex(Self,
|
|
FieldDefList);
|
|
finally
|
|
FieldDefList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlSELECT.DoCheckAggregates;
|
|
var
|
|
i: Integer;
|
|
Se : TffSqlSelection;
|
|
F : TffSqlFieldProxy;
|
|
LDistinct: Boolean;
|
|
begin
|
|
LDistinct := False;
|
|
{ LDistinct is being used to check for situation where a non-aggregate
|
|
column is listed after an aggregate column. }
|
|
for i := 0 to pred(SelectionList.SelectionCount) do begin
|
|
se := SelectionList.Selection[i];
|
|
if se.IsAggregateExpression then
|
|
LDistinct := True
|
|
else if LDistinct then
|
|
SQLError('Non-aggregate column "' + Trim(se.SQLText) +
|
|
'" must appear before aggregate columns in the selection list.')
|
|
else if se.SimpleExpression.IsField(F) and
|
|
((GroupColumnList = nil) or
|
|
(not GroupColumnList.Contains(Columns[i], se))) then
|
|
SQLError('Non-aggregate column "' + trim(se.SQLText) +
|
|
'" must appear in GROUP BY');
|
|
end;
|
|
end;
|
|
|
|
{!!.11 new}
|
|
function TffSqlSELECT.TableWithCount(const ColumnName: string): TffSqlTableProxy; {!!.12}
|
|
var
|
|
FieldDefList: TffSqlFieldDefList;
|
|
begin
|
|
FieldDefList := TffSqlFieldDefList.Create;
|
|
try
|
|
FieldDefList.AddField(ColumnName, fftDouble, 8, 0); {!!.12}
|
|
Result := Owner.FDatabase.CreateTemporaryTableWithoutIndex(Self, FieldDefList);
|
|
finally
|
|
FieldDefList.Free;
|
|
end;
|
|
Owner.FDatabase.StartTransaction([nil]);
|
|
try
|
|
Result.Insert;
|
|
Result.Field(0).SetValue(TFFSqlTableProxy(TablesReferencedByOrder.Objects[0]).GetRecordCount);
|
|
Result.Post;
|
|
Owner.FDatabase.Commit;
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlSELECT.AggregateQueryResult(NeedData: Boolean): TffSqlTableProxy;
|
|
var
|
|
i : Integer;
|
|
T2 : TffSqlTableProxy;
|
|
GroupColumnsIn : Integer;
|
|
SortList: TffSqlSortArray;
|
|
GroupColumnTargetField,
|
|
AggExpList,
|
|
FSX : TList;
|
|
FSF : TList;
|
|
j : Integer;
|
|
Status : TffResult;
|
|
ColumnName: string; {!!.12}
|
|
begin
|
|
{!!.11 begin}
|
|
if (GroupColumnList = nil)
|
|
and (CondExpWhere = nil) {!!.12}
|
|
and (TablesReferencedByOrder.Count = 1)
|
|
and (CondExpHaving = nil)
|
|
and (SelectionList.SelectionCount = 1)
|
|
and (SelectionList.Selection[0].SimpleExpression <> nil)
|
|
and (SelectionList.Selection[0].SimpleExpression.TermCount = 1)
|
|
and (SelectionList.Selection[0].SimpleExpression.Term[0].FactorCount = 1)
|
|
and (SelectionList.Selection[0].SimpleExpression.Term[0].Factor[0].Aggregate <> nil)
|
|
and (SelectionList.Selection[0].SimpleExpression.Term[0].Factor[0].Aggregate.AgFunction = agCount)
|
|
and (SelectionList.Selection[0].SimpleExpression.Term[0].Factor[0].Aggregate.SimpleExpression = nil) then begin
|
|
{special case, plain "COUNT(*)" - use record count reported by low-level code}
|
|
if SelectionList.Selection[0].Column <> nil then {!!.12}
|
|
ColumnName := SelectionList.Selection[0].Column.ColumnName {!!.12}
|
|
else {!!.12}
|
|
ColumnName := 'COUNT(*)'; {!!.12}
|
|
Result := TableWithCount(ColumnName); {!!.12}
|
|
exit;
|
|
end;
|
|
{!!.11 end}
|
|
|
|
FGrpTable := nil;
|
|
T2 := nil;
|
|
|
|
{Columns contain the columns that will be in the result table.
|
|
However, we may still group on other fields from the selection result -
|
|
in particular if this is a sub-query}
|
|
|
|
{field list for grouping table creation}
|
|
|
|
FSX := nil;
|
|
FSF := nil;
|
|
GroupColumnTargetField := nil;
|
|
AggExpList := nil;
|
|
|
|
try
|
|
{field lists for joiner - one for expressions, another for fields}
|
|
FSX := TList.Create;
|
|
FSF := TList.Create;
|
|
|
|
{where the groups should appear in the final result}
|
|
GroupColumnTargetField := TList.Create;
|
|
AggExpList := TList.Create;
|
|
|
|
if GroupColumnList = nil then
|
|
GroupColumnsIn := 0
|
|
else
|
|
GroupColumnsIn := GroupColumnList.ColumnCount;
|
|
|
|
{make sure all non-grouped columns are aggregate expressions}
|
|
|
|
DoCheckAggregates;
|
|
|
|
AggList := TList.Create;
|
|
try
|
|
DoBuildGroupingTable(GroupColumnsIn, FSF, FSX,
|
|
GroupColumnTargetField);
|
|
|
|
try
|
|
if Joiner = nil then begin
|
|
|
|
Joiner := TffSqlJoiner.Create(Owner, CondExpWhere);
|
|
|
|
Assert(Assigned(TablesReferencedByOrder));
|
|
for i := 0 to pred(TablesReferencedByOrder.Count) do
|
|
Joiner.Sources.Add(
|
|
TFFSqlTableProxySubset.Create(
|
|
TFFSqlTableProxy(TablesReferencedByOrder.Objects[i])));
|
|
end;
|
|
|
|
Joiner.ClearColumnList;
|
|
|
|
if GroupColumnList <> nil then begin
|
|
for i := 0 to pred(GroupColumnsIn) do begin
|
|
Joiner.AddColumn(
|
|
FSX[i],
|
|
FSF[i],
|
|
FGrpTable.Field(i));
|
|
end;
|
|
end;
|
|
|
|
for i := 0 to pred(AggList.Count) do begin
|
|
Joiner.AddColumn(
|
|
TffSqlAggregate(AggList[i]).SimpleExpression,
|
|
nil,
|
|
FGrpTable.Field(i + GroupColumnsIn));
|
|
end;
|
|
|
|
if NeedData then begin
|
|
Joiner.Target := FGrpTable;
|
|
Owner.FDatabase.StartTransaction([nil]);
|
|
try
|
|
Joiner.Execute(Owner.UseIndex, nil, jmNone);
|
|
Owner.FDatabase.Commit;
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
{turn off special aggregation flags so that the table result
|
|
may be queried}
|
|
for i := 0 to FGrpTable.FieldCount - 1 do
|
|
FGrpTable.Field(i).IsTarget := False;
|
|
|
|
{At this point we have a table with all records that meet the
|
|
WHERE criteria.}
|
|
|
|
{if DISTINCT was specifed, we now need to remove any duplicates}
|
|
|
|
DoRemoveDups(NeedData);
|
|
|
|
if GroupColumnList <> nil then begin
|
|
{ we need to group FGrpTable }
|
|
{ First, sort the data on groups }
|
|
for i := 0 to pred(GroupColumnsIn) do
|
|
SortList[i] := FGrpTable.Field(i).Index + 1;
|
|
|
|
Status := FGrpTable.Sort(GroupColumnsIn, SortList, True); {!!.13}
|
|
if Status <> DBIERR_NONE then
|
|
raise EffException.CreateNoData(ffStrResServer, Status);
|
|
|
|
end;
|
|
|
|
{we now have the data sorted on the grouping fields}
|
|
{we then copy to another table with a slightly different
|
|
layout to hold aggregate counters rather than data values
|
|
for the non-grouped columns}
|
|
|
|
DoGroupCopy(GroupColumnsIn, AggExpList,
|
|
GroupColumnTargetField);
|
|
|
|
DoHaving;
|
|
|
|
if (Parent is TffSqlInClause) or (Parent is TffSqlMatchClause) then begin
|
|
{need an index to allow the IN and MATCH clauses to be evaluated}
|
|
|
|
DoSortOnAll;
|
|
end else
|
|
DoAggOrderBy;
|
|
except
|
|
if FGrpTable <> T2 then
|
|
T2.Free;
|
|
FGrpTable.Owner := nil;
|
|
FGrpTable.Free;
|
|
raise;
|
|
end;
|
|
|
|
finally
|
|
AggList.Free;
|
|
end;
|
|
for j := 0 to Pred(GroupColumnTargetField.Count) do
|
|
TffGroupColumnTargetInfo(GroupColumnTargetField[j]).Free;
|
|
|
|
finally
|
|
GroupColumnTargetField.Free;
|
|
FSF.Free;
|
|
FSX.Free;
|
|
AggExpList.Free;
|
|
end;
|
|
Result := FGrpTable;
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.Execute2(NeedData: Boolean): TffSqlTableProxy;
|
|
begin
|
|
{check that all referenced tables and fields exist}
|
|
if not Bound then
|
|
Bind;
|
|
|
|
if HaveAggregates or (GroupColumnList <> nil) then begin
|
|
Result := AggregateQueryResult(NeedData);
|
|
RequestLive := False;
|
|
end else begin
|
|
Result := NormalQueryResult(NeedData);
|
|
RequestLive := False; {!!! for now}
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.Execute(var aLiveResult: Boolean;
|
|
var aCursorID: TffCursorID; var RecordsRead: Integer);
|
|
var
|
|
T : TffSqlTableProxy;
|
|
begin
|
|
Assert(Owner <> nil);
|
|
RequestLive := aLiveResult;
|
|
T := Execute2(True);
|
|
aCursorID := T.CursorID;
|
|
aLiveResult := RequestLive;
|
|
T.LeaveCursorOpen := True;
|
|
if T.Owner = Self then begin
|
|
T.Owner := nil;
|
|
T.Free;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlSELECT)
|
|
and (Distinct = TffSqlSELECT(Other).Distinct)
|
|
and (BothNil(SelectionList, TffSqlSELECT(Other).SelectionList)
|
|
or (BothNonNil(SelectionList, TffSqlSELECT(Other).SelectionList)
|
|
and SelectionList.Equals(TffSqlSELECT(Other).SelectionList))
|
|
or ( ((SelectionList = nil) and TffSqlSELECT(Other).WasStar)
|
|
or (WasStar and (TffSqlSELECT(Other).SelectionList = nil)) )
|
|
)
|
|
and TableRefList.Equals(TffSqlSELECT(Other).TableRefList)
|
|
and (BothNil(CondExpWhere, TffSqlSELECT(Other).CondExpWhere)
|
|
or (BothNonNil(CondExpWhere, TffSqlSELECT(Other).CondExpWhere)
|
|
and CondExpWhere.Equals(TffSqlSELECT(Other).CondExpWhere))
|
|
)
|
|
and (BothNil(GroupColumnList, TffSqlSELECT(Other).GroupColumnList)
|
|
or (BothNonNil(GroupColumnList, TffSqlSELECT(Other).GroupColumnList)
|
|
and GroupColumnList.Equals(TffSqlSELECT(Other).GroupColumnList))
|
|
)
|
|
and (BothNil(CondExpHaving, TffSqlSELECT(Other).CondExpHaving)
|
|
or (BothNonNil(CondExpHaving, TffSqlSELECT(Other).CondExpHaving)
|
|
and CondExpHaving.Equals(TffSqlSELECT(Other).CondExpHaving))
|
|
)
|
|
and
|
|
(BothNil(OrderList, TffSqlSELECT(Other).OrderList)
|
|
or (BothNonNil(OrderList, TffSqlSELECT(Other).OrderList)
|
|
and OrderList.Equals(TffSqlSELECT(Other).OrderList)));
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.GetResultTable: TFFSqlTableProxy;
|
|
begin
|
|
EnsureResultTable(True);
|
|
Result := FResultTable;
|
|
end;
|
|
|
|
function TffSqlSELECT.IsSubQuery: Boolean;
|
|
var
|
|
P: TffSqlNode;
|
|
begin
|
|
P := Parent;
|
|
while P <> nil do begin
|
|
if (P is TffSqlSELECT)
|
|
or (P is TffSqlUPDATE)
|
|
or (P is TffSqlDELETE)
|
|
or (P is TffSqlINSERT) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
P := P.Parent;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlSELECT.Match(Value: Variant; Unique: Boolean;
|
|
MatchOption: TffSqlMatchOption): Boolean;
|
|
|
|
function RangeIsOne(const Table: TffSqlTableProxy): Boolean;
|
|
begin
|
|
Result := Table.First and not Table.Next;
|
|
end;
|
|
|
|
begin
|
|
EnsureResultTable(True);
|
|
if not Unique then
|
|
case MatchOption of
|
|
moUnspec :
|
|
if VarIsNull(Value) then
|
|
Result := True
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := ResultTable.First;
|
|
end;
|
|
moPartial :
|
|
if VarIsNull(Value) then
|
|
Result := True
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := ResultTable.First;
|
|
end;
|
|
else//moFull :
|
|
if VarIsNull(Value) then
|
|
Result := True
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := ResultTable.First;
|
|
end;
|
|
end
|
|
else
|
|
case MatchOption of
|
|
moUnspec :
|
|
if VarIsNull(Value) then
|
|
Result := True
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := RangeIsOne(ResultTable);
|
|
end;
|
|
moPartial :
|
|
if VarIsNull(Value) then
|
|
Result := True
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := RangeIsOne(ResultTable);
|
|
end;
|
|
else//moFull :
|
|
if VarIsNull(Value) then
|
|
Result := True
|
|
else begin
|
|
ResultTable.SetRange([Value], [Value], 1, 1, True, True, True);
|
|
Result := RangeIsOne(ResultTable);
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSELECT.MatchType(ExpectedType: TffFieldType; AllowMultiple: Boolean);
|
|
begin
|
|
//this will only be called when the current SELECT statement
|
|
//functions as a sub-query
|
|
if not AllowMultiple and (SelectionList.SelectionCount <> 1) then
|
|
SQLError('Sub-query was expected to have exactly one column');
|
|
EnsureResultTable(False);
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlFieldRef===================================================}
|
|
procedure TffSqlFieldRef.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlFieldRef then begin
|
|
TableName := TffSqlFieldRef(Source).TableName;
|
|
FieldName := TffSqlFieldRef(Source).FieldName;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlFieldRef.CheckType;
|
|
{ Rewritten !!.06}
|
|
var
|
|
Found : Boolean;
|
|
Inx : Integer;
|
|
Select : TffSQLSelect;
|
|
Selection : TffSQLSelection;
|
|
begin
|
|
Found := False;
|
|
{ The field reference may be an alias or a direct reference to a field. }
|
|
if (TableName = '') then begin
|
|
{ See if it is an alias. }
|
|
Select := OwnerSelect;
|
|
if Select <> nil then begin
|
|
for Inx := 0 to Pred(Select.SelectionList.SelectionCount) do begin
|
|
Selection := Select.SelectionList.Selection[Inx];
|
|
if (not IsAncestor(Selection)) and
|
|
(Selection.Column <> nil) and
|
|
(AnsiCompareText(Selection.Column.ColumnName, FieldName) = 0) then begin
|
|
FType := Selection.SimpleExpression.GetType;
|
|
Found := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
end else begin
|
|
end;
|
|
end;
|
|
|
|
{ If this isn't an alias then see if it is a direct reference. }
|
|
if not Found then begin
|
|
Assert(Field <> nil);
|
|
FType := Field.GetType;
|
|
end;
|
|
TypeKnown := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFieldRef.ClearBinding;
|
|
begin
|
|
FField := nil;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
{!!.12 begin}
|
|
if Field.IsTarget then begin
|
|
Assert(OwnerSelect <> nil);
|
|
if Field.SrcIndex > -1 then
|
|
Result := TffSQLSimpleExpression(OwnerSelect.Joiner.FSX[
|
|
Field.SrcIndex]).DependsOn(Table)
|
|
else
|
|
Result := Field.SrcField.OwnerTable = Table;
|
|
end else
|
|
{!!.12 end}
|
|
Result := Field.OwnerTable = Table;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFieldRef.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' ');
|
|
if WasWildcard then begin
|
|
WriteStr(Stream, TableName);
|
|
WriteStr(Stream, '.*');
|
|
end else
|
|
WriteStr(Stream, GetTitle(True)); {!!.11}
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFieldRef.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlFieldRef)
|
|
and (AnsiCompareText(TableName, TffSqlFieldRef(Other).TableName) = 0)
|
|
and
|
|
( (AnsiCompareText(FieldName, TffSqlFieldRef(Other).FieldName) = 0)
|
|
or (WasWildcard and (TffSqlFieldRef(Other).FieldName = '')
|
|
or (((FieldName = '') and TffSqlFieldRef(Other).WasWildcard))));
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetDecimals: Integer;
|
|
begin
|
|
Result := Field.GetDecimals;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetField: TFFSqlFieldProxy;
|
|
begin
|
|
if FField = nil then
|
|
FField := Parent.BindField(TableName, FieldName);
|
|
Result := FField;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetGroupField: TFFSqlFieldProxy;
|
|
begin
|
|
if OwnerSelect = nil then
|
|
SQLError('Field references may not occur in this context');
|
|
if FGroupField = nil then begin
|
|
FGroupField := OwnerSelect.FGrpTable.FieldByName(QualName);
|
|
if FGroupField = nil then begin
|
|
FGroupField := OwnerSelect.FGrpTable.FieldByName(FieldName);
|
|
if FGroupField = nil then
|
|
SQLError('Unknown field:' + FieldName);
|
|
end;
|
|
end;
|
|
Result := FGroupField;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetSize: Integer;
|
|
begin
|
|
Result := Field.GetSize;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
if Qualified and (TableName <> '') then {!!.11}
|
|
if FieldName <> '' then
|
|
Result := TableName + '.' + FieldName
|
|
else
|
|
Result := TableName + '.*'
|
|
else
|
|
Result := FieldName;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetType: TffFieldType;
|
|
begin
|
|
if not TypeKnown then
|
|
CheckType;
|
|
Result := FType;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.GetValue: Variant;
|
|
begin
|
|
if (OwnerSelect <> nil) and
|
|
(OwnerSelect.AggQueryMode = aqmGrouping) then
|
|
Result := GroupField.GetValue
|
|
else if Field.IsTarget then begin
|
|
Assert(OwnerSelect <> nil);
|
|
if Field.SrcIndex > -1 then
|
|
Result := TffSQLSimpleExpression(OwnerSelect.Joiner.FSX[
|
|
Field.SrcIndex]).GetValue
|
|
else
|
|
Result := Field.SrcField.GetValue;
|
|
end else
|
|
Result := Field.GetValue;
|
|
end;
|
|
{--------}
|
|
function TffSqlFieldRef.IsNull: Boolean;
|
|
begin
|
|
if (OwnerSelect <> nil) and
|
|
(OwnerSelect.AggQueryMode = aqmGrouping) then
|
|
Result := VarIsNull(GroupField.GetValue)
|
|
else if Field.IsTarget then begin
|
|
Assert(OwnerSelect <> nil);
|
|
if Field.SrcIndex > -1 then
|
|
Result := TffSQLSimpleExpression(OwnerSelect.Joiner.
|
|
FSX[Field.SrcIndex]).IsNull
|
|
else
|
|
Result := Field.SrcField.IsNull;
|
|
end else
|
|
Result := Field.IsNull;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFieldRef.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
if GetType <> ExpectedType then
|
|
case GetType of
|
|
fftByte..fftCurrency :
|
|
case ExpectedType of
|
|
fftByte..fftCurrency :
|
|
{ OK };
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime :
|
|
case ExpectedType of
|
|
fftStDate..fftDateTime :
|
|
{ OK };
|
|
else
|
|
TypeMismatch;
|
|
end; { case }
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString..fftWideString :
|
|
case ExpectedType of
|
|
fftChar, fftWideChar, fftShortString..fftWideString :
|
|
{ OK };
|
|
else
|
|
TypeMismatch;
|
|
end; { case }
|
|
{Begin !!.13}
|
|
fftBLOB..fftBLOBTypedBin :
|
|
case ExpectedType of
|
|
fftChar, fftWideChar,
|
|
fftShortString..fftWideString,
|
|
fftBLOB..fftBLOBTypedBin :
|
|
{ OK };
|
|
else
|
|
TypeMismatch;
|
|
end; { case }
|
|
{End !!.13}
|
|
else
|
|
TypeMismatch;
|
|
end; { case }
|
|
end;
|
|
{--------}
|
|
function TffSQLFieldRef.QualName : string;
|
|
var
|
|
Name : string;
|
|
begin
|
|
Result := FFieldName;
|
|
{ If no tablename specified then obtain table name of source table. }
|
|
if FTableName = '' then begin
|
|
if assigned(FField) then
|
|
Result := FField.OwnerTable.Name + '.' + FFieldName
|
|
else
|
|
Result := FFieldName;
|
|
end
|
|
else begin
|
|
if OwnerSelect = nil then
|
|
SQLError('Field references may not occur in this context');
|
|
{ Has a table name. Is it really an alias? }
|
|
Name := OwnerSelect.TableRefList.GetNameForAlias(FTableName);
|
|
if Name <> '' then
|
|
Result := Name + '.' + FFieldName
|
|
else
|
|
Result := TableName + '.' + FFieldName;
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlAggregate==================================================}
|
|
{--------}
|
|
procedure TffSqlAggregate.AddAggregate(Target: TList);
|
|
begin
|
|
Target.Add(Self);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlAggregate then begin
|
|
AgFunction := TffSqlAggregate(Source).AgFunction;
|
|
SimpleExpression.Free;
|
|
SimpleExpression := nil;
|
|
if assigned(TffSqlAggregate(Source).SimpleExpression) then begin
|
|
SimpleExpression := TffSqlSimpleExpression.Create(Self);
|
|
SimpleExpression.Assign(TffSqlAggregate(Source).SimpleExpression);
|
|
end;
|
|
Distinct := TffSqlAggregate(Source).Distinct;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.CreateCounter(SourceField: TFFSqlFieldProxy);
|
|
begin
|
|
FCounter := TAggCounter.Create;
|
|
FSourceField := SourceField;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.DeleteCounter;
|
|
begin
|
|
FCounter.Free;
|
|
FCounter := nil;
|
|
FSourceField := nil;
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SimpleExpression.DependsOn(Table);
|
|
end;
|
|
{--------}
|
|
destructor TffSqlAggregate.Destroy;
|
|
begin
|
|
SimpleExpression.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.ResetCounters;
|
|
begin
|
|
FCounter.Reset;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.Update;
|
|
begin
|
|
case AgFunction of
|
|
agCount :
|
|
if (FSourceField = nil) or not VarIsNull(FSourceField.GetValue) then {!!.13}
|
|
FCounter.Add(1);
|
|
else
|
|
if not VarIsNull(FSourceField.GetValue) then
|
|
FCounter.Add(FSourceField.GetValue);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' ');
|
|
WriteStr(Stream, AgString[AgFunction]);
|
|
WriteStr(Stream,'(');
|
|
if SimpleExpression <> nil then begin
|
|
if Distinct then
|
|
WriteStr(Stream,' DISTINCT')
|
|
else
|
|
WriteStr(Stream,' ALL');
|
|
SimpleExpression.EmitSQL(Stream);
|
|
end else
|
|
WriteStr(Stream, '*');
|
|
WriteStr(Stream,')');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if SimpleExpression <> nil then
|
|
SimpleExpression.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlAggregate)
|
|
and (AgFunction = TffSqlAggregate(Other).AgFunction)
|
|
and (Distinct = TffSqlAggregate(Other).Distinct)
|
|
and (
|
|
BothNil(SimpleExpression, TffSqlAggregate(Other).SimpleExpression)
|
|
or (
|
|
BothNonNil(SimpleExpression, TffSqlAggregate(Other).SimpleExpression)
|
|
and SimpleExpression.Equals(TffSqlAggregate(Other).SimpleExpression)
|
|
)
|
|
);
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.GetAggregateValue: Variant;
|
|
begin
|
|
if FCounter = nil then
|
|
Result := 0
|
|
else begin
|
|
case AgFunction of
|
|
agCount :
|
|
Result := FCounter.Count;
|
|
agMin :
|
|
Result := FCounter.Min;
|
|
agMax :
|
|
Result := FCounter.Max;
|
|
agSum :
|
|
Result := FCounter.Sum;
|
|
else //agAvg :
|
|
Result := FCounter.Avg;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.FlagAggregate(Select: TffSqlSELECT);
|
|
begin
|
|
Select.HaveAggregates := True;
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.GetDecimals: Integer;
|
|
begin
|
|
case AgFunction of
|
|
agCount :
|
|
Result := 0;
|
|
else
|
|
Result := 2;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.GetSize: Integer;
|
|
begin
|
|
if SimpleExpression <> nil then
|
|
Result := SimpleExpression.GetSize
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
Result := AgString[AgFunction] + '(';
|
|
if Distinct then
|
|
Result := Result + 'DISTINCT ';
|
|
if SimpleExpression = nil then
|
|
Result := Result + '*'
|
|
else
|
|
Result := Result + SimpleExpression.GetTitle(Qualified); {!!.11}
|
|
Result := Result + ')';
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.GetType: TffFieldType;
|
|
begin
|
|
if SimpleExpression = nil then
|
|
Result := fftDouble
|
|
else
|
|
case SimpleExpression.GetType of
|
|
fftExtended :
|
|
Result := fftExtended;
|
|
fftCurrency :
|
|
case AgFunction of
|
|
agCount :
|
|
Result := fftDouble;
|
|
else
|
|
Result := fftCurrency;
|
|
end;
|
|
else
|
|
case AgFunction of
|
|
agCount,
|
|
agAvg:
|
|
Result := fftDouble;
|
|
else
|
|
Result := SimpleExpression.GetType;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAggregate.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftByte..fftCurrency :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.Reduce: Boolean;
|
|
begin
|
|
if SimpleExpression <> nil then
|
|
Result := SimpleExpression.Reduce
|
|
else
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlAggregate.ValidType(aType : TffFieldType) : Boolean;
|
|
begin
|
|
case agFunction of
|
|
agSum, agAvg :
|
|
Result := (aType in [fftByte..fftCurrency]);
|
|
else
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlColumn=====================================================}
|
|
procedure TffSqlColumn.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlColumn then begin
|
|
ColumnName := TffSqlColumn(Source).ColumnName;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlColumn.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' ');
|
|
WriteStr(Stream, ColumnName);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlColumn.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlColumn.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlColumn)
|
|
and (AnsiCompareText(ColumnName, TffSqlColumn(Other).ColumnName) = 0);
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlIsTest=====================================================}
|
|
function TffSqlIsTest.AsBoolean(const TestValue: Variant): Boolean;
|
|
begin
|
|
case IsOp of
|
|
ioNull :
|
|
Result := VarIsNull(TestValue) xor UnaryNot;
|
|
ioTrue :
|
|
if UnaryNot then
|
|
Result := not TestValue
|
|
else
|
|
Result := TestValue;
|
|
ioFalse :
|
|
if UnaryNot then
|
|
Result := TestValue
|
|
else
|
|
Result := not TestValue;
|
|
else
|
|
//ioUnknown :
|
|
Result := VarIsNull(TestValue) xor UnaryNot;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIsTest.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlIsTest then begin
|
|
UnaryNot := TffSqlIsTest(Source).UnaryNot;
|
|
IsOp := TffSqlIsTest(Source).IsOp;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlIsTest.EmitSQL(Stream: TStream);
|
|
const
|
|
IsOpStr : array[TffSqlIsOp] of string =
|
|
('NULL', 'TRUE', 'FALSE', 'UNKNOWN');
|
|
begin
|
|
WriteStr(Stream,' IS');
|
|
if UnaryNot then
|
|
WriteStr(Stream,' NOT');
|
|
WriteStr(Stream,' ');
|
|
WriteStr(Stream, IsOpStr[IsOp]);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIsTest.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlIsTest.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlIsTest)
|
|
and (UnaryNot = TffSqlIsTest(Other).UnaryNot)
|
|
and (IsOp = TffSqlIsTest(Other).IsOp);
|
|
end;
|
|
{--------}
|
|
function TffSqlIsTest.Evaluate(
|
|
Expression: TffSqlSimpleExpression): Boolean;
|
|
{- allow check against NULL for non-variant compatible fields}
|
|
begin
|
|
case IsOp of
|
|
ioNull, ioUnknown :
|
|
Result := Expression.IsNull xor UnaryNot;
|
|
else
|
|
Result := AsBoolean(Expression.GetValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlIsTest.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlBetweenClause==============================================}
|
|
function TffSqlBetweenClause.AsBoolean(const TestValue: Variant): Boolean;
|
|
begin
|
|
if VarIsNull(TestValue) then
|
|
Result := False
|
|
else
|
|
Result :=
|
|
(
|
|
(TestValue >= SimpleLow.GetValue)
|
|
and
|
|
(TestValue <= SimpleHigh.GetValue)
|
|
) xor Negated;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlBetweenClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlBetweenClause then begin
|
|
Negated := TffSqlBetweenClause(Source).Negated;
|
|
SimpleLow.Free;
|
|
SimpleLow := TffSqlSimpleExpression.Create(Self);
|
|
SimpleLow.Assign(TffSqlBetweenClause(Source).SimpleLow);
|
|
SimpleHigh.Free;
|
|
SimpleHigh := TffSqlSimpleExpression.Create(Self);
|
|
SimpleHigh.Assign(TffSqlBetweenClause(Source).SimpleHigh);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlBetweenClause.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
FIsConstant :=
|
|
SimpleLow.IsConstant and SimpleHigh.IsConstant;
|
|
end;
|
|
{--------}
|
|
function TffSqlBetweenClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SimpleLow.DependsOn(Table) or SimpleHigh.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlBetweenClause.Destroy;
|
|
begin
|
|
SimpleLow.Free;
|
|
SimpleHigh.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlBetweenClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
if Negated then
|
|
WriteStr(Stream,' NOT');
|
|
WriteStr(Stream, ' BETWEEN ');
|
|
SimpleLow.EmitSQL(Stream);
|
|
WriteStr(Stream,' AND ');
|
|
SimpleHigh.EmitSQL(Stream);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlBetweenClause.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SimpleLow.EnumNodes(EnumMethod, Deep);
|
|
SimpleHigh.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlBetweenClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlBetweenClause)
|
|
and (Negated = TffSqlBetweenClause(Other).Negated)
|
|
and (SimpleLow.Equals(TffSqlBetweenClause(Other).SimpleLow))
|
|
and (SimpleHigh.Equals(TffSqlBetweenClause(Other).SimpleHigh));
|
|
end;
|
|
{--------}
|
|
function TffSqlBetweenClause.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlBetweenClause.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
SimpleLow.MatchType(ExpectedType);
|
|
SimpleHigh.MatchType(ExpectedType);
|
|
end;
|
|
{--------}
|
|
function TffSqlBetweenClause.Reduce: Boolean;
|
|
begin
|
|
Result := SimpleLow.Reduce or SimpleHigh.Reduce;
|
|
end;
|
|
|
|
procedure TffSqlBetweenClause.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{ TffSqlLikePattern }
|
|
|
|
constructor TffsqlLikePattern.Create(SearchPattern: string; const Escape: string);
|
|
var
|
|
i: Integer;
|
|
Mask : string;
|
|
Esc: Char;
|
|
begin
|
|
FloatPatterns := TStringList.Create;
|
|
FloatMasks := TStringList.Create;
|
|
|
|
{
|
|
Search pattern is made up of
|
|
0 or 1 lead pattern
|
|
0-N floating patterns, and
|
|
0 or 1 trail pattern.
|
|
Patterns are separated by '%'.
|
|
If search pattern starts with '%', it does not have a lead pattern.
|
|
If search pattern ends with '%', it does not have a trail pattern.
|
|
|
|
Place holders, '_', are not considered here but in Find.
|
|
|
|
}
|
|
|
|
{build a separate mask string for place holders so that we can use
|
|
the same logic for escaped and non-escaped search patterns}
|
|
|
|
Mask := SearchPattern;
|
|
if Escape <> '' then begin
|
|
i := length(SearchPattern);
|
|
Esc := Escape[1];
|
|
while i >= 2 do begin
|
|
if SearchPattern[i - 1] = Esc then begin
|
|
Mask[i] := ' '; // blank out the mask character
|
|
//remove the escape
|
|
Delete(Mask, i - 1, 1);
|
|
Delete(SearchPattern, i - 1, 1);
|
|
end;
|
|
dec(i);
|
|
end;
|
|
end;
|
|
|
|
if (SearchPattern = '') then
|
|
exit;
|
|
|
|
if Mask[1] <> '%' then begin
|
|
{we have a lead pattern}
|
|
i := PosCh('%', Mask);
|
|
if i = 0 then begin
|
|
{entire search pattern is a lead pattern}
|
|
LeadPattern := SearchPattern;
|
|
LeadMask := Mask;
|
|
exit;
|
|
end;
|
|
|
|
LeadPattern := copy(SearchPattern, 1, i - 1);
|
|
LeadMask := copy(Mask, 1, i - 1);
|
|
|
|
Delete(SearchPattern, 1, i - 1);
|
|
Delete(Mask, 1, i - 1);
|
|
end;
|
|
|
|
if (SearchPattern = '') then
|
|
exit;
|
|
|
|
i := length(Mask);
|
|
|
|
if Mask[i] <> '%' then begin
|
|
{we have a trail pattern}
|
|
while (i > 0) and (Mask[i] <> '%') do
|
|
dec(i);
|
|
if i = 0 then begin
|
|
{entire remaining pattern is a trail pattern}
|
|
TrailPattern := SearchPattern;
|
|
TrailMask := Mask;
|
|
exit;
|
|
end;
|
|
|
|
TrailPattern := copy(SearchPattern, i + 1, MaxInt);
|
|
TrailMask := copy(Mask, i + 1, MaxInt);
|
|
|
|
Delete(SearchPattern, i + 1, MaxInt);
|
|
Delete(Mask, i + 1, MaxInt);
|
|
end;
|
|
|
|
{we now have one or more floating patterns separated by '%'}
|
|
|
|
if Mask = '' then
|
|
exit;
|
|
|
|
if Mask[1] <> '%' then
|
|
exit;
|
|
|
|
Delete(Mask, 1, 1);
|
|
Delete(SearchPattern, 1, 1);
|
|
|
|
repeat
|
|
|
|
i := PosCh('%', Mask);
|
|
|
|
if i = 0 then begin
|
|
{entire remaining search pattern is one pattern}
|
|
FloatPatterns.Add(SearchPattern);
|
|
FloatMasks.Add(Mask);
|
|
exit;
|
|
end;
|
|
|
|
FloatPatterns.Add(copy(SearchPattern, 1, i - 1));
|
|
FloatMasks.Add(copy(Mask, 1, i - 1));
|
|
|
|
Delete(SearchPattern, 1, i);
|
|
Delete(Mask, 1, i);
|
|
|
|
until SearchPattern = '';
|
|
|
|
end;
|
|
|
|
destructor TffSqlLikePattern.Destroy;
|
|
begin
|
|
FloatPatterns.Free;
|
|
FloatMasks.Free;
|
|
inherited;
|
|
end;
|
|
|
|
{!!.13 new}
|
|
function CharsDiffer(IgnoreCase: Boolean; C1, C2: Char): Boolean;
|
|
begin
|
|
if IgnoreCase then
|
|
Result := CharUpper(Pointer(C1)) <> CharUpper(Pointer(C2))
|
|
else
|
|
Result := C1 <> C2;
|
|
end;
|
|
|
|
function Match(const Pattern, Mask : string;
|
|
PatternLength : Integer;
|
|
const PTextToSearch : PAnsiChar;
|
|
const TextLen : Integer;
|
|
StartIndex : Integer;
|
|
IgnoreCase : Boolean {!!.13}
|
|
): Boolean;
|
|
{Modified !!.13}
|
|
{ Look for an exact match of the pattern at StartIndex, disregarding
|
|
locations with '_' in the mask.
|
|
Note: StartIndex is base zero. }
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := True;
|
|
if TextLen < PatternLength then
|
|
Result := False
|
|
else
|
|
for i := 1 to PatternLength do
|
|
if (Mask[i] <> '_') and
|
|
{(PTextToSearch[StartIndex + i - 1] <> Pattern[i]) then begin} {!!.13}
|
|
CharsDiffer(IgnoreCase, PTextToSearch[StartIndex + i - 1], Pattern[i]) then begin {!!.13}
|
|
Result := False;
|
|
Break;
|
|
end; { if }
|
|
end;
|
|
|
|
function Scan(const Pattern, Mask : string;
|
|
PatternLength : Integer;
|
|
const PTextToSearch : PAnsiChar;
|
|
const TextLen : Integer;
|
|
StartIndex: Integer;
|
|
IgnoreCase: Boolean {!!.13}
|
|
) : Integer;
|
|
{Modified !!.13}
|
|
{ Scan for a match of the pattern starting at StartIndex, disregarding
|
|
locations with '_' in the mask. Return -1 if not found, otherwise
|
|
return the position immediately following the matched phrase. }
|
|
var
|
|
L, i : Integer;
|
|
Found : Boolean;
|
|
begin
|
|
L := TextLen - StartIndex;
|
|
repeat
|
|
if L < PatternLength then begin
|
|
Result := -1;
|
|
Exit;
|
|
end;
|
|
Found := True;
|
|
for i := 1 to PatternLength do
|
|
if (i - 1 > L) or (Mask[i] <> '_') and
|
|
{(PTextToSearch[i + StartIndex - 1] <> Pattern[i]) then begin} {!!.13}
|
|
CharsDiffer(IgnoreCase, PTextToSearch[i + StartIndex - 1], Pattern[i]) then begin {!!.13}
|
|
Found := False;
|
|
Break;
|
|
end;
|
|
if Found then begin
|
|
Result := StartIndex + PatternLength;
|
|
Exit;
|
|
end;
|
|
inc(StartIndex);
|
|
dec(L);
|
|
until False;
|
|
end;
|
|
|
|
function TffSqlLikePattern.Find(const TextToSearch: Variant;
|
|
IgnoreCase: Boolean {!!.13}
|
|
): Boolean;
|
|
{Rewritten !!.13}
|
|
{Search the TextToSearch. Return true if the search pattern was found}
|
|
var
|
|
TextLen,
|
|
LeadLen,
|
|
TrailLen,
|
|
i,
|
|
l,
|
|
StartPos,
|
|
EndPos: Integer;
|
|
VStr, P : string;
|
|
VPtr : PAnsiChar;
|
|
begin
|
|
Result := False;
|
|
try
|
|
if TVarData(TextToSearch).VType and VarTypeMask = varByte then begin
|
|
TextLen := VarArrayHighBound(TextToSearch, 1);
|
|
if TextLen = 0 then
|
|
Exit;
|
|
VStr := '';
|
|
VPtr := VarArrayLock(TextToSearch);
|
|
end
|
|
else begin
|
|
TextLen := Length(TextToSearch);
|
|
if TextLen = 0 then
|
|
Exit;
|
|
VStr := VarToStr(TextToSearch);
|
|
VPtr := PAnsiChar(VStr);
|
|
end;
|
|
|
|
LeadLen := Length(LeadPattern);
|
|
TrailLen := Length(TrailPattern);
|
|
if LeadLen > 0 then begin
|
|
{ If there is a lead pattern then see if there is a match. }
|
|
if not Match(LeadPattern, LeadMask, LeadLen, VPtr, TextLen, 0,
|
|
IgnoreCase) then begin {!!.13}
|
|
{ No match so exit. }
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
{ There was a match so set the starting position for the next match. }
|
|
StartPos := LeadLen;
|
|
end else
|
|
{ No lead pattern. Next match starts at beginning of string. }
|
|
StartPos := 0;
|
|
|
|
if TrailLen > 0 then begin
|
|
{ There is a trail pattern. Does it overlap with the lead pattern? }
|
|
i := TextLen - TrailLen;
|
|
if i < StartPos then begin
|
|
{ Yes it overlaps. A match is not possible so exit. }
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
if not Match(TrailPattern, TrailMask, TrailLen, VPtr, TextLen, i,
|
|
IgnoreCase) then begin {!!.13}
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
EndPos := i - 1;
|
|
end else
|
|
EndPos := TextLen - 1;
|
|
|
|
if FloatPatterns.Count = 0 then
|
|
if TextLen <> LeadLen + TrailLen then begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
for i := 0 to pred(FloatPatterns.Count) do begin
|
|
P := FloatPatterns[i];
|
|
l := Length(P);
|
|
{ If the length of the float pattern is greater than the number of
|
|
characters left in the string then a match is not possible. }
|
|
if l > EndPos - StartPos + 1 then begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
StartPos := Scan(P, FloatMasks[i], l, VPtr, TextLen, StartPos, IgnoreCase); {!!.13}
|
|
if StartPos = -1 then begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := True;
|
|
finally
|
|
if VStr = '' then
|
|
VarArrayUnlock(TextToSearch);
|
|
end;
|
|
end;
|
|
{===TffSqlLikeClause=================================================}
|
|
function TffSqlLikeClause.AsBoolean(const TestValue: Variant): Boolean;
|
|
begin
|
|
if VarIsNull(TestValue) then begin
|
|
Result := Negated;
|
|
exit;
|
|
end;
|
|
if LikePattern = nil then
|
|
if EscapeExp <> nil then
|
|
LikePattern := TffSqlLikePattern.Create(SimpleExp.GetValue, EscapeExp.GetValue)
|
|
else
|
|
LikePattern := TffSqlLikePattern.Create(SimpleExp.GetValue, '');
|
|
Result := LikePattern.Find(TestValue, IgnoreCase) xor Negated; {!!.13}
|
|
if not IsConstant then begin
|
|
LikePattern.Free;
|
|
LikePattern := nil;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlLikeClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlLikeClause then begin
|
|
if SimpleExp = nil then
|
|
SimpleExp := TffSqlSimpleExpression.Create(Self);
|
|
SimpleExp.Assign(TffSqlLikeClause(Source).SimpleExp);
|
|
if (EscapeExp = nil) and (TffSqlLikeClause(Source).EscapeExp <> nil) then begin
|
|
EscapeExp := TffSqlSimpleExpression.Create(Self);
|
|
EscapeExp.Assign(TffSqlLikeClause(Source).EscapeExp);
|
|
end;
|
|
Negated := TffSqlLikeClause(Source).Negated;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlLikeClause.CanLimit: Boolean;
|
|
var
|
|
S: string;
|
|
begin
|
|
Result := False;
|
|
if not Limited
|
|
and not IgnoreCase {!!.13}
|
|
and SimpleExp.IsConstant
|
|
and ((EscapeExp = nil) {or EscapeExp.IsConstant}) then begin {!!.11}
|
|
S := SimpleExp.GetValue;
|
|
if not (S[1] in ['%', '_']) then
|
|
Result := (GetHighLimit <> '');
|
|
end;
|
|
end;
|
|
|
|
function TffSqlLikeClause.CanReplaceWithCompare: Boolean;
|
|
var
|
|
S: string;
|
|
begin
|
|
Result := False;
|
|
if not Limited
|
|
and not IgnoreCase {!!.13}
|
|
and SimpleExp.IsConstant
|
|
and ((EscapeExp = nil) {or EscapeExp.IsConstant}) then begin {!!.11}
|
|
S := SimpleExp.GetValue;
|
|
Result := (PosCh('_', S) = 0)
|
|
and (length(S) > 1)
|
|
and (PosCh('%', S) = length(S));
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlLikeClause.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
FIsConstant := SimpleExp.IsConstant and ((EscapeExp = nil) or EscapeExp.IsConstant);
|
|
end;
|
|
{--------}
|
|
function TffSqlLikeClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SimpleExp.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlLikeClause.Destroy;
|
|
begin
|
|
SimpleExp.Free;
|
|
EscapeExp.Free;
|
|
LikePattern.Free;
|
|
if FBmTable <> nil then {!!.11}
|
|
Dispose(FBmTable); {!!.11}
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlLikeClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
if Negated then
|
|
WriteStr(Stream,' NOT');
|
|
WriteStr(Stream, ' LIKE ');
|
|
SimpleExp.EmitSQL(Stream);
|
|
if EscapeExp <> nil then begin
|
|
WriteStr(Stream,' ESCAPE');
|
|
EscapeExp.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlLikeClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SimpleExp.EnumNodes(EnumMethod, Deep);
|
|
if EscapeExp <> nil then
|
|
EscapeExp.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlLikeClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlLikeClause)
|
|
and (Negated = TffSqlLikeClause(Other).Negated)
|
|
and (SimpleExp.Equals(TffSqlLikeClause(Other).SimpleExp))
|
|
and (BothNil(EscapeExp, TffSqlLikeClause(Other).EscapeExp)
|
|
or (BothNonNil(EscapeExp, TffSqlLikeClause(Other).EscapeExp)
|
|
and EscapeExp.Equals(TffSqlLikeClause(Other).EscapeExp)));
|
|
end;
|
|
{--------}
|
|
|
|
{!!.11 new}
|
|
function TffSqlLikeClause.GetBmTable: PBTable;
|
|
var
|
|
S: string;
|
|
begin
|
|
if FBmTable = nil then begin
|
|
Assert(IsBMCompatible);
|
|
if IgnoreCase then {!!.13}
|
|
S := AnsiUpperCase(SimpleExp.GetValue) {!!.13}
|
|
else {!!.13}
|
|
S := SimpleExp.GetValue;
|
|
New(FBmTable);
|
|
FBMPhrase := copy(S, 2, length(S) - 2);
|
|
BMMakeTableS(FBmPhrase, FBmTable^);
|
|
end;
|
|
Result := FBmTable;
|
|
end;
|
|
|
|
function TffSqlLikeClause.GetHighLimit: string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := GetLowLimit;
|
|
i := length(Result);
|
|
if Result[i] in [' '..'~'] then
|
|
inc(Result[i])
|
|
else
|
|
Result := '';
|
|
end;
|
|
|
|
function TffSqlLikeClause.GetLowLimit: string;
|
|
var
|
|
P : Integer;
|
|
begin
|
|
Result := SimpleExp.GetValue;
|
|
P := 1;
|
|
while (P <= length(Result))
|
|
and not (Result[P] in ['%', '_']) do
|
|
inc(P);
|
|
dec(P);
|
|
if P < length(Result) then
|
|
Result := copy(Result, 1 , P);
|
|
end;
|
|
|
|
{!!.11 new}
|
|
procedure TffSqlLikeClause.CheckBMCompat;
|
|
var
|
|
S: string;
|
|
Len,
|
|
Inx : Integer;
|
|
begin
|
|
FBMCompat := False;
|
|
if SimpleExp.IsConstant and (EscapeExp = nil) then begin
|
|
S := SimpleExp.GetValue;
|
|
Len := Length(S);
|
|
FBMCompat := (Len >= 3) and
|
|
(S[1] = '%') and
|
|
(S[Len] = '%');
|
|
{ Verify there is not another wildcard character in the middle of the
|
|
string. }
|
|
for Inx := 2 to Pred(Len) do
|
|
if S[Inx] = '%' then begin
|
|
FBMCompat := False;
|
|
Break;
|
|
end;
|
|
end;
|
|
BMCompatChecked := True;
|
|
end;
|
|
|
|
{!!.11 new}
|
|
function TffSqlLikeClause.IsBMCompatible: Boolean;
|
|
begin
|
|
if not BMCompatChecked then
|
|
CheckBMCompat;
|
|
Result := FBMCompat;
|
|
end;
|
|
|
|
function TffSqlLikeClause.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlLikeClause.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftChar, fftWideChar,
|
|
fftShortString..fftWideString :
|
|
SimpleExp.MatchType(ExpectedType);
|
|
fftBLOB..fftBLOBTypedBin : {!!.11}
|
|
SimpleExp.MatchType(fftNullAnsiStr); {!!.11}
|
|
else
|
|
SQLError(Format('The LIKE operator may not be applied to %s fields', {!!.11}
|
|
[FieldDataTypes[ExpectedType]])); {!!.11}
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlLikeClause.Reduce: Boolean;
|
|
begin
|
|
Result := SimpleExp.Reduce or ((EscapeExp <> nil) and EscapeExp.Reduce);
|
|
end;
|
|
|
|
procedure TffSqlLikeClause.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlInClause===================================================}
|
|
function TffSqlInClause.AsBoolean(const TestValue: Variant): Boolean;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.CheckForValue(TestValue)
|
|
else
|
|
Result := SimpleExpList.Contains(TestValue);
|
|
Result := Result xor Negated;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlInClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlInClause then begin
|
|
SimpleExpList.Free;
|
|
SimpleExpList := nil; {!!.12}
|
|
SubQuery.Free;
|
|
SubQuery := nil; {!!.12}
|
|
if TffSqlInClause(Source).SubQuery <> nil then begin
|
|
SubQuery := TffSqlSELECT.Create(Self);
|
|
SubQuery.Assign(TffSqlInClause(Source).SubQuery);
|
|
end else begin
|
|
SimpleExpList := TffSqlSimpleExpressionList.Create(Self);
|
|
SimpleExpList.Assign(TffSqlInClause(Source).SimpleExpList);
|
|
end;
|
|
Negated := TffSqlInClause(Source).Negated;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlInClause.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
if SubQuery <> nil then
|
|
FIsConstant := False
|
|
else
|
|
FIsConstant := SimpleExpList.IsConstant;
|
|
end;
|
|
{--------}
|
|
function TffSqlInClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.DependsOn(Table)
|
|
else
|
|
Result := SimpleExpList.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlInClause.Destroy;
|
|
begin
|
|
SubQuery.Free;
|
|
SimpleExpList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlInClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
if Negated then
|
|
WriteStr(Stream,' NOT');
|
|
WriteStr(Stream, ' IN (');
|
|
if SubQuery <> nil then
|
|
SubQuery.EmitSQL(Stream)
|
|
else
|
|
SimpleExpList.EmitSQL(Stream);
|
|
WriteStr(Stream, ') ');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlInClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if SubQuery <> nil then
|
|
SubQuery.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
SimpleExpList.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlInClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlInClause)
|
|
and (Negated = TffSqlInClause(Other).Negated);
|
|
if Result then
|
|
if SubQuery <> nil then
|
|
if TffSqlInClause(Other).SubQuery = nil then
|
|
Result := False
|
|
else
|
|
Result := SubQuery.Equals(TffSqlInClause(Other).SubQuery)
|
|
else
|
|
if TffSqlInClause(Other).SimpleExpList = nil then
|
|
Result := False
|
|
else
|
|
Result := SimpleExpList.Equals(TffSqlInClause(Other).SimpleExpList);
|
|
end;
|
|
{--------}
|
|
function TffSqlInClause.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
procedure TffSqlInClause.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
if SubQuery <> nil then
|
|
SubQuery.MatchType(ExpectedType, True)
|
|
else
|
|
SimpleExpList.MatchType(ExpectedType);
|
|
end;
|
|
{--------}
|
|
function TffSqlInClause.Reduce: Boolean;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.Reduce
|
|
else
|
|
Result := SimpleExpList.Reduce;
|
|
end;
|
|
|
|
procedure TffSqlInClause.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{====================================================================}
|
|
|
|
function SimpleCompare(RelOp: TffSqlRelOp; const Val1, Val2: Variant): Boolean;
|
|
const
|
|
ValIsBLOBArray : array[boolean, boolean] of Byte =
|
|
( (1, { false, false }
|
|
2), { false, true }
|
|
(3, { true, false }
|
|
4) { true, true }
|
|
);
|
|
var
|
|
VStr : string;
|
|
VPtr1, VPtr2 : PAnsiChar;
|
|
Inx, VPtr1Len, VPtr2Len : Integer;
|
|
VPtr1Locked, VPtr2Locked : Boolean;
|
|
ValIsBLOBCase : Byte;
|
|
begin
|
|
if VarIsNull(Val1) or
|
|
VarIsNull(Val2) then begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
Assert(RelOp <> roNone);
|
|
|
|
ValIsBLOBCase := ValIsBLOBArray[VarIsArray(Val1) and
|
|
(TVarData(Val1).VType and VarTypeMask = varByte),
|
|
VarIsArray(Val2) and
|
|
(TVarData(Val2).VType and VarTypeMask = varByte)];
|
|
if ValIsBLOBCase = 1 then
|
|
case RelOp of
|
|
roEQ :
|
|
if (VarType(Val1) and VarTypeMask = VarDate)
|
|
and (VarType(Val2) and VarTypeMask = VarDate) then
|
|
Result := abs(double(Val1) - double(Val2)) < TimeDelta
|
|
else
|
|
Result := Val1 = Val2;
|
|
roLE :
|
|
Result := Val1 <= Val2;
|
|
roL :
|
|
Result := Val1 < Val2;
|
|
roG :
|
|
Result := Val1 > Val2;
|
|
roGE :
|
|
Result := Val1 >= Val2;
|
|
else//roNE :
|
|
if (VarType(Val1) and VarTypeMask = VarDate)
|
|
and (VarType(Val2) and VarTypeMask = VarDate) then
|
|
Result := abs(double(Val1) - double(Val2)) >= TimeDelta
|
|
else
|
|
Result := Val1 <> Val2;
|
|
end { case }
|
|
else begin
|
|
{ One of the parameters is a BLOB. It must be converted to a string.
|
|
This code is kind of flaky in that it is a duplicate of the preceding
|
|
section. However, this approach should give us optimal performance for
|
|
cases where neither parameter is a BLOB. }
|
|
VPtr1 := nil;
|
|
VPtr2 := nil;
|
|
VPtr1Locked := False;
|
|
VPtr2Locked := False;
|
|
try
|
|
case ValIsBLOBCase of
|
|
2 : begin
|
|
VStr := VarToStr(Val1);
|
|
VPtr1 := PAnsiChar(VStr);
|
|
VPtr1Len := Length(VStr);
|
|
VPtr2 := VarArrayLock(Val2);
|
|
VPtr2Locked := True;
|
|
VPtr2Len := VarArrayHighBound(Val2, 1);
|
|
end;
|
|
3 : begin
|
|
VPtr1 := VarArrayLock(Val1);
|
|
VPtr1Locked := True;
|
|
VPtr1Len := VarArrayHighBound(Val1, 1);
|
|
VStr := VarToStr(Val2);
|
|
VPtr2 := PAnsiChar(VStr);
|
|
VPtr2Len := Length(VStr);
|
|
end;
|
|
4 : begin
|
|
VPtr1 := VarArrayLock(Val1);
|
|
VPtr1Locked := True;
|
|
VPtr1Len := VarArrayHighBound(Val1, 1);
|
|
VPtr2 := VarArrayLock(Val2);
|
|
VPtr2Locked := True;
|
|
VPtr2Len := VarArrayHighBound(Val2, 1);
|
|
end;
|
|
else begin
|
|
VPtr1Len := 0;
|
|
VPtr2Len := 0;
|
|
end;
|
|
end; { case }
|
|
Inx := Windows.CompareStringA(LOCALE_USER_DEFAULT, 0,
|
|
VPtr1, VPtr1Len, VPtr2, VPtr2Len) - 2;
|
|
case RelOp of
|
|
roEQ : Result := (Inx = 0);
|
|
roLE : Result := (Inx <= 0);
|
|
roL : Result := (Inx < 0);
|
|
roG : Result := (Inx > 0);
|
|
roGE : Result := (Inx >= 0);
|
|
else
|
|
{ roNE }
|
|
Result := (Inx <> 0);
|
|
end; { case }
|
|
finally
|
|
if VPtr1Locked then
|
|
VarArrayUnlock(Val1);
|
|
if VPtr2Locked then
|
|
VarArrayUnlock(Val2);
|
|
end;
|
|
end; { if..else }
|
|
end;
|
|
|
|
{===TffSqlCondPrimary================================================}
|
|
function TffSqlCondPrimary.AsBoolean: Boolean;
|
|
var
|
|
F: TffSqlFieldProxy; {!!.11}
|
|
BMTable: PBTable; {!!.13}
|
|
begin
|
|
Result := False;
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
if not TypeChecked then
|
|
CheckType;
|
|
|
|
if RelOp = roNone then
|
|
if BetweenClause <> nil then
|
|
Result := BetweenClause.AsBoolean(SimpleExp1.GetValue)
|
|
else
|
|
if LikeClause <> nil then
|
|
if SimpleExp1.IsField(F) and LikeClause.IsBMCompatible then begin {!!.11}{!!.13}
|
|
{Need to call BMTable before method call - otherwise BMPhrase doesn't get initialized in time}
|
|
BMTable := LikeClause.BMTable;
|
|
Result := F.BMMatch(BMTable^, LikeClause.BMPhrase, LikeClause.IgnoreCase) {!!.11}{!!.13}
|
|
end else {!!.11}{!!.13}
|
|
Result := LikeClause.AsBoolean(SimpleExp1.GetValue)
|
|
else
|
|
if InClause <> nil then
|
|
Result := InClause.AsBoolean(SimpleExp1.GetValue)
|
|
else
|
|
if IsTest <> nil then
|
|
Result := IsTest.Evaluate(SimpleExp1)
|
|
else
|
|
if ExistsClause <> nil then
|
|
Result := ExistsClause.AsBoolean
|
|
else
|
|
if UniqueClause <> nil then
|
|
Result := UniqueClause.AsBoolean
|
|
else
|
|
if MatchClause <> nil then
|
|
Result := MatchClause.AsBoolean(SimpleExp1.GetValue)
|
|
else
|
|
Result := SimpleExp1.GetValue
|
|
else
|
|
if SimpleExp2 <> nil then
|
|
Result := SimpleCompare(RelOp, SimpleExp1.GetValue, SimpleExp2.GetValue)
|
|
else
|
|
if AllOrAnyClause <> nil then
|
|
Result := AllOrAnyClause.Compare(RelOp, SimpleExp1.GetValue)
|
|
else
|
|
SQLError('Simple expression or ANY/ALL clause expected');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondPrimary.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlCondPrimary then begin
|
|
|
|
Clear;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).SimpleExp1) then begin
|
|
SimpleExp1 := TffSqlSimpleExpression.Create(Self);
|
|
SimpleExp1.Assign(TffSqlCondPrimary(Source).SimpleExp1);
|
|
end;
|
|
|
|
RelOp := TffSqlCondPrimary(Source).RelOp;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).SimpleExp2) then begin
|
|
SimpleExp2 := TffSqlSimpleExpression.Create(Self);
|
|
SimpleExp2.Assign(TffSqlCondPrimary(Source).SimpleExp2);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).BetweenClause) then begin
|
|
BetweenClause := TffSqlBetweenClause.Create(Self);
|
|
BetweenClause.Assign(TffSqlCondPrimary(Source).BetweenClause);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).LikeClause) then begin
|
|
LikeClause := TffSqlLikeClause.Create(Self);
|
|
LikeClause.Assign(TffSqlCondPrimary(Source).LikeClause);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).InClause) then begin
|
|
InClause := TffSqlInClause.Create(Self);
|
|
InClause.Assign(TffSqlCondPrimary(Source).InClause);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).IsTest) then begin
|
|
IsTest := TffSqlIsTest.Create(Self);
|
|
IsTest.Assign(TffSqlCondPrimary(Source).IsTest);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).AllOrAnyClause) then begin
|
|
AllOrAnyClause := TffSqlAllOrAnyClause.Create(Self);
|
|
AllOrAnyClause.Assign(TffSqlCondPrimary(Source).AllOrAnyClause);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).ExistsClause) then begin
|
|
ExistsClause := TffSqlExistsClause.Create(Self);
|
|
ExistsClause.Assign(TffSqlCondPrimary(Source).ExistsClause);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).UniqueClause) then begin
|
|
UniqueClause := TffSqlUniqueClause.Create(Self);
|
|
UniqueClause.Assign(TffSqlCondPrimary(Source).UniqueClause);
|
|
end;
|
|
|
|
if assigned(TffSqlCondPrimary(Source).MatchClause) then begin
|
|
MatchClause := TffSqlMatchClause.Create(Self);
|
|
MatchClause.Assign(TffSqlCondPrimary(Source).MatchClause);
|
|
end;
|
|
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondPrimary.BindHaving;
|
|
begin
|
|
if SimpleExp1 <> nil then
|
|
SimpleExp1.BindHaving;
|
|
case RelOp of
|
|
roNone :
|
|
if BetweenClause <> nil then
|
|
SQLError('BETWEEN not supported in a HAVING clause')
|
|
else
|
|
if LikeClause <> nil then
|
|
SQLError('LIKE not supported in a HAVING clause')
|
|
else
|
|
if InClause <> nil then
|
|
SQLError('IN not supported in a HAVING clause')
|
|
else
|
|
{if IsTest <> nil then
|
|
SQLError('IS not supported in a HAVING clause')
|
|
else} {!!.11}
|
|
if ExistsClause <> nil then
|
|
SQLError('EXISTS not supported in a HAVING clause')
|
|
else
|
|
if UniqueClause <> nil then
|
|
SQLError('UNIQUE not supported in a HAVING clause')
|
|
else
|
|
if MatchClause <> nil then
|
|
SQLError('MATCH not supported in a HAVING clause');
|
|
else
|
|
if AllOrAnyClause <> nil then
|
|
//SQLError('ANY or ALL conditions not supported in a HAVING clause')
|
|
else begin
|
|
Assert(SimpleExp2 <> nil);
|
|
SimpleExp2.BindHaving;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlCondPrimary.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
FIsConstant := False;
|
|
if SimpleExp1 <> nil then
|
|
if not SimpleExp1.IsConstant then
|
|
exit;
|
|
case RelOp of
|
|
roNone :
|
|
if BetweenClause <> nil then
|
|
if not BetweenClause.IsConstant then
|
|
exit
|
|
else
|
|
else
|
|
if LikeClause <> nil then
|
|
if not LikeClause.IsConstant then
|
|
exit
|
|
else
|
|
else
|
|
if InClause <> nil then
|
|
if not InClause.IsConstant then
|
|
exit
|
|
else
|
|
else
|
|
if IsTest <> nil then
|
|
// constant by definition
|
|
else
|
|
if ExistsClause <> nil then
|
|
exit
|
|
else
|
|
if UniqueClause <> nil then
|
|
exit
|
|
else
|
|
if MatchClause <> nil then
|
|
exit;
|
|
else
|
|
if AllOrAnyClause <> nil then
|
|
exit
|
|
else begin
|
|
Assert(SimpleExp2 <> nil);
|
|
if not SimpleExp2.IsConstant then
|
|
exit;
|
|
end;
|
|
end;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
|
|
procedure TffSqlCondPrimary.CheckType;
|
|
var
|
|
T1 : TffFieldType;
|
|
begin
|
|
if SimpleExp1 <> nil then
|
|
T1 := SimpleExp1.GetType
|
|
else
|
|
T1 := fftBLOB; {anything that doesn't match a valid SQL type}
|
|
case RelOp of
|
|
roNone :
|
|
if BetweenClause <> nil then
|
|
BetweenClause.MatchType(T1)
|
|
else
|
|
if LikeClause <> nil then
|
|
LikeClause.MatchType(T1)
|
|
else
|
|
if InClause <> nil then
|
|
InClause.MatchType(T1)
|
|
else
|
|
if IsTest <> nil then
|
|
IsTest.MatchType(T1)
|
|
else
|
|
if ExistsClause <> nil then
|
|
//T1 := ExistsClause.GetType
|
|
else
|
|
if UniqueClause <> nil then
|
|
//T1 := UniqueClause.GetType
|
|
else
|
|
if MatchClause <> nil then
|
|
MatchClause.MatchType(T1);
|
|
//else
|
|
// if T1 <> fftBoolean then
|
|
// TypeMismatch;
|
|
else
|
|
if AllOrAnyClause <> nil then
|
|
AllOrAnyClause.MatchType(T1)
|
|
else begin
|
|
Assert(SimpleExp2 <> nil);
|
|
SimpleExp2.MatchType(T1);
|
|
end;
|
|
end;
|
|
TypeChecked := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondPrimary.Clear;
|
|
begin
|
|
SimpleExp1.Free;
|
|
SimpleExp1 := nil;
|
|
BetweenClause.Free;
|
|
BetweenClause := nil;
|
|
LikeClause.Free;
|
|
LikeClause := nil;
|
|
InClause.Free;
|
|
InClause := nil;
|
|
IsTest.Free;
|
|
IsTest := nil;
|
|
ExistsClause.Free;
|
|
ExistsClause := nil;
|
|
UniqueClause.Free;
|
|
UniqueClause := nil;
|
|
MatchClause.Free;
|
|
MatchClause := nil;
|
|
AllOrAnyClause.Free;
|
|
AllOrAnyClause := nil;
|
|
SimpleExp2.Free;
|
|
SimpleExp2 := nil;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := False;
|
|
case RelOp of
|
|
roNone :
|
|
if BetweenClause <> nil then
|
|
Result := SimpleExp1.DependsOn(Table) or BetweenClause.DependsOn(Table)
|
|
else
|
|
if LikeClause <> nil then
|
|
Result := SimpleExp1.DependsOn(Table) or LikeClause.DependsOn(Table)
|
|
else
|
|
if InClause <> nil then
|
|
Result := SimpleExp1.DependsOn(Table) or InClause.DependsOn(Table)
|
|
else
|
|
if IsTest <> nil then
|
|
Result := SimpleExp1.DependsOn(Table)
|
|
else
|
|
if ExistsClause <> nil then
|
|
Result := ExistsClause.DependsOn(Table)
|
|
else
|
|
if UniqueClause <> nil then
|
|
Result := UniqueClause.DependsOn(Table)
|
|
else
|
|
if MatchClause <> nil then
|
|
Result := SimpleExp1.DependsOn(Table) or MatchClause.DependsOn(Table)
|
|
else
|
|
Result := SimpleExp1.DependsOn(Table);
|
|
else //roEQ, roLE, roL, roG, roGE, roNE :
|
|
if SimpleExp2 <> nil then
|
|
Result := SimpleExp1.DependsOn(Table) or SimpleExp2.DependsOn(Table)
|
|
else
|
|
if AllOrAnyClause <> nil then
|
|
Result := SimpleExp1.DependsOn(Table) or AllOrAnyClause.DependsOn(Table)
|
|
else
|
|
SQLError('Simple expression or ANY/ALL clause expected');
|
|
end;
|
|
if AllOrAnyClause <> nil then
|
|
Result := Result or AllOrAnyClause.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlCondPrimary.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlCondPrimary.EmitSQL(Stream: TStream);
|
|
begin
|
|
if SimpleExp1 <> nil then
|
|
SimpleExp1.EmitSQL(Stream);
|
|
case RelOp of
|
|
roNone :
|
|
if BetweenClause <> nil then
|
|
BetweenClause.EmitSQL(Stream)
|
|
else
|
|
if LikeClause <> nil then
|
|
LikeClause.EmitSQL(Stream)
|
|
else
|
|
if InClause <> nil then
|
|
InClause.EmitSQL(Stream)
|
|
else
|
|
if IsTest <> nil then
|
|
IsTest.EmitSQL(Stream)
|
|
else
|
|
if ExistsClause <> nil then
|
|
ExistsClause.EmitSQL(Stream)
|
|
else
|
|
if UniqueClause <> nil then
|
|
UniqueClause.EmitSQL(Stream)
|
|
else
|
|
if MatchClause <> nil then
|
|
MatchClause.EmitSQL(Stream);
|
|
else
|
|
WriteStr(Stream,' ');
|
|
WriteStr(Stream, RelOpStr[RelOp]);
|
|
WriteStr(Stream,' ');
|
|
if AllOrAnyClause <> nil then
|
|
AllOrAnyClause.EmitSQL(Stream)
|
|
else
|
|
SimpleExp2.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondPrimary.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if SimpleExp1 <> nil then
|
|
SimpleExp1.EnumNodes(EnumMethod, Deep);
|
|
case RelOp of
|
|
roNone :
|
|
if BetweenClause <> nil then
|
|
BetweenClause.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if LikeClause <> nil then
|
|
LikeClause.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if InClause <> nil then
|
|
InClause.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if IsTest <> nil then
|
|
IsTest.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if MatchClause <> nil then
|
|
MatchClause.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if ExistsClause <> nil then
|
|
ExistsClause.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if UniqueClause <> nil then
|
|
UniqueClause.EnumNodes(EnumMethod, Deep);
|
|
else
|
|
if SimpleExp2 <> nil then
|
|
SimpleExp2.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if AllOrAnyClause <> nil then
|
|
AllOrAnyClause.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlCondPrimary)
|
|
and (RelOp = TffSqlCondPrimary(Other).RelOp)
|
|
and (
|
|
BothNil(SimpleExp1, TffSqlCondPrimary(Other).SimpleExp1)
|
|
or (
|
|
BothNonNil(SimpleExp1, TffSqlCondPrimary(Other).SimpleExp1)
|
|
and SimpleExp1.Equals(TffSqlCondPrimary(Other).SimpleExp1)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(SimpleExp2, TffSqlCondPrimary(Other).SimpleExp2)
|
|
or (
|
|
BothNonNil(SimpleExp2, TffSqlCondPrimary(Other).SimpleExp2)
|
|
and SimpleExp2.Equals(TffSqlCondPrimary(Other).SimpleExp2)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(BetweenClause, TffSqlCondPrimary(Other).BetweenClause)
|
|
or (
|
|
BothNonNil(BetweenClause, TffSqlCondPrimary(Other).BetweenClause)
|
|
and BetweenClause.Equals(TffSqlCondPrimary(Other).BetweenClause)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(LikeClause, TffSqlCondPrimary(Other).LikeClause)
|
|
or (
|
|
BothNonNil(LikeClause, TffSqlCondPrimary(Other).LikeClause)
|
|
and LikeClause.Equals(TffSqlCondPrimary(Other).LikeClause)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(InClause, TffSqlCondPrimary(Other).InClause)
|
|
or (
|
|
BothNonNil(InClause, TffSqlCondPrimary(Other).InClause)
|
|
and InClause.Equals(TffSqlCondPrimary(Other).InClause)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(IsTest, TffSqlCondPrimary(Other).IsTest)
|
|
or (
|
|
BothNonNil(IsTest, TffSqlCondPrimary(Other).IsTest)
|
|
and IsTest.Equals(TffSqlCondPrimary(Other).IsTest)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(AllOrAnyClause, TffSqlCondPrimary(Other).AllOrAnyClause)
|
|
or (
|
|
BothNonNil(AllOrAnyClause, TffSqlCondPrimary(Other).AllOrAnyClause)
|
|
and AllOrAnyClause.Equals(TffSqlCondPrimary(Other).AllOrAnyClause)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(ExistsClause, TffSqlCondPrimary(Other).ExistsClause)
|
|
or (
|
|
BothNonNil(ExistsClause, TffSqlCondPrimary(Other).ExistsClause)
|
|
and ExistsClause.Equals(TffSqlCondPrimary(Other).ExistsClause)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(MatchClause, TffSqlCondPrimary(Other).MatchClause)
|
|
or (
|
|
BothNonNil(MatchClause, TffSqlCondPrimary(Other).MatchClause)
|
|
and MatchClause.Equals(TffSqlCondPrimary(Other).MatchClause)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(UniqueClause, TffSqlCondPrimary(Other).UniqueClause)
|
|
or (
|
|
BothNonNil(UniqueClause, TffSqlCondPrimary(Other).UniqueClause)
|
|
and UniqueClause.Equals(TffSqlCondPrimary(Other).UniqueClause)
|
|
)
|
|
);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.GetDecimals: Integer;
|
|
begin
|
|
if SimpleExp1 <> nil then
|
|
Result := SimpleExp1.GetDecimals
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.GetSize: Integer;
|
|
begin
|
|
case RelOp of
|
|
roNone :
|
|
Result := SimpleExp1.GetSize
|
|
else
|
|
Result := 1;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
case GetType of
|
|
fftBoolean:
|
|
Result := 'COND'
|
|
else
|
|
Result := SimpleExp1.GetTitle(Qualified); {!!.11}
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.GetType: TffFieldType;
|
|
begin
|
|
if SimpleExp1 <> nil then
|
|
Result := SimpleExp1.GetType
|
|
else
|
|
Result := fftBoolean; {should never happen}
|
|
case RelOp of
|
|
roNone :
|
|
if (BetweenClause <> nil)
|
|
or (LikeClause <> nil)
|
|
or (InClause <> nil)
|
|
or (IsTest <> nil)
|
|
or (MatchClause <> nil) then
|
|
Result := fftBoolean;
|
|
else
|
|
if SimpleExp2 <> nil then
|
|
SimpleExp2.MatchType(Result);
|
|
Result := fftBoolean;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.GetValue: Variant;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
case GetType of
|
|
fftBoolean:
|
|
Result := AsBoolean
|
|
else
|
|
Result := SimpleExp1.GetValue;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.IsRelationTo(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy;
|
|
var Operator: TffSqlRelOp;
|
|
var ArgExpression: TffSqlSimpleExpression;
|
|
var SameCase: Boolean): Boolean; {!!.10}
|
|
begin
|
|
ArgExpression := nil;
|
|
case RelOp of
|
|
roEQ, roLE, roL, roG, roGE, roNE :
|
|
begin
|
|
if SimpleExp2 <> nil then
|
|
if SimpleExp1.IsFieldFrom(Table, FieldReferenced, SameCase) then begin
|
|
Result := True;
|
|
ArgExpression := SimpleExp2;
|
|
end else
|
|
if SimpleExp2.IsFieldFrom(Table, FieldReferenced, SameCase) then begin
|
|
Result := True;
|
|
ArgExpression := SimpleExp1;
|
|
end else
|
|
Result := False
|
|
else {typically ANY or ALL relation}
|
|
Result := False;
|
|
end;
|
|
else
|
|
Result := False;
|
|
end;
|
|
if AllOrAnyClause <> nil then
|
|
Result := False;
|
|
Operator := RelOp;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondPrimary.JustSimpleExpression: Boolean;
|
|
begin
|
|
Result := (RelOp = roNone)
|
|
and (BetweenClause = nil)
|
|
and (LikeClause = nil)
|
|
and (InClause = nil)
|
|
and (IsTest = nil)
|
|
and (ExistsClause = nil)
|
|
and (UniqueClause = nil)
|
|
and (MatchClause = nil);
|
|
end;
|
|
|
|
{!!.11 new}
|
|
procedure TffSqlCondPrimary.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case RelOp of
|
|
roNone :
|
|
if (BetweenClause <> nil)
|
|
or (LikeClause <> nil)
|
|
or (InClause <> nil)
|
|
or (IsTest <> nil)
|
|
or (ExistsClause <> nil) {!!.11}
|
|
or (MatchClause <> nil) then
|
|
if ExpectedType <> fftBoolean then
|
|
TypeMismatch
|
|
else
|
|
else
|
|
SimpleExp1.MatchType(ExpectedType);
|
|
else
|
|
if SimpleExp2 <> nil then begin
|
|
SimpleExp2.MatchType(SimpleExp1.GetType);
|
|
if ExpectedType <> fftBoolean then
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlCondPrimary.Reduce: Boolean;
|
|
begin
|
|
Result := True;
|
|
if (SimpleExp1 <> nil) and SimpleExp1.Reduce then
|
|
exit;
|
|
if (SimpleExp2 <> nil) and SimpleExp2.Reduce then
|
|
exit;
|
|
if (BetweenClause <> nil) and BetweenClause.Reduce then
|
|
exit;
|
|
if (LikeClause <> nil) and LikeClause.Reduce then
|
|
exit;
|
|
if (InClause <> nil) and InClause.Reduce then
|
|
exit;
|
|
if (ExistsClause <> nil) and ExistsClause.Reduce then
|
|
exit;
|
|
if (UniqueClause <> nil) and UniqueClause.Reduce then
|
|
exit;
|
|
if (MatchClause <> nil) and MatchClause.Reduce then
|
|
exit;
|
|
if (AllOrAnyClause <> nil) and AllOrAnyClause.Reduce then
|
|
exit;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlCondPrimary.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlCondFactor=================================================}
|
|
function TffSqlCondFactor.AsBoolean: Boolean;
|
|
begin
|
|
if TmpKnown then begin
|
|
Result := TmpValue;
|
|
exit;
|
|
end;
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
Result := CondPrimary.AsBoolean;
|
|
if UnaryNot then
|
|
Result := not Result;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondFactor.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlCondFactor then begin
|
|
if CondPrimary = nil then
|
|
CondPrimary := TffSqlCondPrimary.Create(Self);
|
|
CondPrimary.Assign(TffSqlCondFactor(Source).CondPrimary);
|
|
UnaryNot := TffSqlCondFactor(Source).UnaryNot;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlCondFactor.BindHaving;
|
|
begin
|
|
CondPrimary.BindHaving;
|
|
end;
|
|
|
|
procedure TffSqlCondFactor.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
if CondPrimary.IsConstant then begin
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlCondFactor.Clear;
|
|
begin
|
|
if CondPrimary <> nil then
|
|
CondPrimary.Clear;
|
|
end;
|
|
|
|
function TffSqlCondFactor.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := CondPrimary.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlCondFactor.Destroy;
|
|
begin
|
|
CondPrimary.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlCondFactor.EmitSQL(Stream: TStream);
|
|
begin
|
|
if UnaryNot then
|
|
WriteStr(Stream,' NOT');
|
|
CondPrimary.EmitSQL(Stream);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondFactor.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
CondPrimary.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlCondFactor)
|
|
and (UnaryNot = TffSqlCondFactor(Other).UnaryNot)
|
|
and (CondPrimary.Equals(TffSqlCondFactor(Other).CondPrimary));
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.GetDecimals: Integer;
|
|
begin
|
|
Result := CondPrimary.GetDecimals;
|
|
end;
|
|
{--------}
|
|
{!!.10}
|
|
function TffSqlCondFactor.GetSize: Integer;
|
|
begin
|
|
if UnaryNot then
|
|
Result := 1
|
|
else
|
|
Result := CondPrimary.GetSize;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
Result := CondPrimary.GetTitle(Qualified); {!!.11}
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.GetType: TffFieldType;
|
|
begin
|
|
if UnaryNot then
|
|
Result := fftBoolean
|
|
else
|
|
Result := CondPrimary.GetType;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.GetValue: Variant;
|
|
begin
|
|
if TmpKnown then begin
|
|
Result := TmpValue;
|
|
exit;
|
|
end;
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
if UnaryNot then
|
|
Result := AsBoolean
|
|
else
|
|
Result := CondPrimary.GetValue;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.IsRelationTo(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy;
|
|
var Operator: TffSqlRelOp;
|
|
var ArgExpression: TffSqlSimpleExpression;
|
|
var SameCase: Boolean): Boolean;
|
|
begin
|
|
ArgExpression := nil;
|
|
Result := CondPrimary.IsRelationTo(Table, FieldReferenced,
|
|
Operator, ArgExpression, SameCase)
|
|
and not ArgExpression.DependsOn(Table);
|
|
if Result and UnaryNot then
|
|
case Operator of
|
|
roNone : ;
|
|
roEQ : Operator := roNE;
|
|
roLE : Operator := roG;
|
|
roL : Operator := roGE;
|
|
roG : Operator := roLE;
|
|
roGE : Operator := roL;
|
|
roNE : Operator := roEQ;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondFactor.MarkTrue;
|
|
begin
|
|
TmpKnown := True;
|
|
TmpValue := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondFactor.MarkUnknown;
|
|
begin
|
|
TmpKnown := False;
|
|
end;
|
|
{--------}
|
|
{!!.11 - new}
|
|
procedure TffSqlCondFactor.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
if UnaryNot then
|
|
if ExpectedType <> fftBoolean then
|
|
TypeMismatch
|
|
else
|
|
else
|
|
CondPrimary.MatchType(ExpectedType);
|
|
end;
|
|
{--------}
|
|
function TffSqlCondFactor.Reduce: Boolean;
|
|
var
|
|
LiftPrimary : TffSqlCondPrimary;
|
|
NewExp: TffSqlSimpleExpression;
|
|
NewTerm: TffSqlTerm;
|
|
NewFactor: TffSqlFactor;
|
|
NewCondExp : TffSqlCondExp;
|
|
NewCondTerm: TffSqlCondTerm;
|
|
NewCondFactor : TffSqlCondFactor;
|
|
NewCondPrimary : TffSqlCondPrimary;
|
|
begin
|
|
{look for a conditional primary nested inside redundant parens}
|
|
{eliminate parens when found}
|
|
Result := False;
|
|
LiftPrimary := nil;
|
|
if (CondPrimary.RelOp = roNone) then
|
|
with CondPrimary do begin
|
|
//if SimpleExp1 <> nil then begin
|
|
if JustSimpleExpression then begin
|
|
with SimpleExp1 do
|
|
if TermCount = 1 then
|
|
with Term[0] do
|
|
if FactorCount = 1 then
|
|
with Factor[0] do
|
|
if CondExp <> nil then
|
|
with CondExp do
|
|
if CondTermCount = 1 then
|
|
with CondTerm[0] do
|
|
if CondFactorCount = 1 then
|
|
with CondFactor[0] do begin
|
|
LiftPrimary := TffSqlCondPrimary.Create(Self);
|
|
LiftPrimary.Assign(CondPrimary);
|
|
end;
|
|
if LiftPrimary <> nil then begin
|
|
Clear;
|
|
Assign(LiftPrimary);
|
|
LiftPrimary.Free;
|
|
Result := True;
|
|
end else
|
|
if Reduce then begin
|
|
{expression itself was reduced}
|
|
Result := True;
|
|
end;
|
|
end;
|
|
if not Result then
|
|
Result := Reduce;
|
|
end;
|
|
if not Result then begin {otherwise we'll be called again}
|
|
{see if this a negated simple expression which can be reversed}
|
|
if UnaryNot and (CondPrimary.RelOp <> roNone) then begin
|
|
{it is, reverse condition and remove NOT}
|
|
case CondPrimary.RelOp of
|
|
roEQ : CondPrimary.RelOp := roNE;
|
|
roLE : CondPrimary.RelOp := roG;
|
|
roL : CondPrimary.RelOp := roGE;
|
|
roG : CondPrimary.RelOp := roLE;
|
|
roGE: CondPrimary.RelOp := roL;
|
|
roNE : CondPrimary.RelOp := roEQ;
|
|
end;
|
|
UnaryNot := False;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
if not Result then {otherwise we'll be called again}
|
|
if (CondPrimary.RelOp = roNE) { "<>" operator }
|
|
{can't optimize ALL/ANY clauses this way}
|
|
and (CondPrimary.AllOrAnyClause = nil) then {!!.11}
|
|
if CondPrimary.SimpleExp1.HasFieldRef
|
|
or CondPrimary.SimpleExp2.HasFieldRef then begin
|
|
{convert expressions of the form
|
|
Simple Exp1 <> Simple Exp2
|
|
where at least one expression contains a field reference
|
|
to
|
|
(Simple Exp1 < Simple Exp2 OR Simple Exp1 > Simple Exp2)
|
|
to allow for index optimization later on}
|
|
NewExp := TffSqlSimpleExpression.Create(CondPrimary);
|
|
NewTerm := TffSqlTerm.Create(NewExp);
|
|
NewFactor := TffSqlFactor.Create(NewTerm);
|
|
NewCondExp := TffSqlCondExp.Create(NewFactor);
|
|
|
|
NewCondTerm := TffSqlCondTerm.Create(NewCondExp);
|
|
NewCondFactor := TffSqlCondFactor.Create(NewCondTerm);
|
|
NewCondPrimary := TffSqlCondPrimary.Create(NewCondFactor);
|
|
NewCondPrimary.Assign(CondPrimary);
|
|
NewCondPrimary.RelOp := roL;
|
|
NewCondFactor.CondPrimary := NewCondPrimary;
|
|
NewCondTerm.AddCondFactor(NewCondFactor);
|
|
NewCondExp.AddCondTerm(NewCondTerm);
|
|
|
|
NewCondTerm := TffSqlCondTerm.Create(NewCondExp);
|
|
NewCondFactor := TffSqlCondFactor.Create(NewCondTerm);
|
|
NewCondPrimary := TffSqlCondPrimary.Create(NewCondFactor);
|
|
NewCondPrimary.Assign(CondPrimary);
|
|
NewCondPrimary.RelOp := roG;
|
|
NewCondFactor.CondPrimary := NewCondPrimary;
|
|
NewCondTerm.AddCondFactor(NewCondFactor);
|
|
NewCondExp.AddCondTerm(NewCondTerm);
|
|
|
|
NewFactor.CondExp := NewCondExp;
|
|
NewTerm.AddFactor(NewFactor);
|
|
NewExp.AddTerm(NewTerm);
|
|
|
|
CondPrimary.SimpleExp2.Free;
|
|
CondPrimary.SimpleExp2 := nil;
|
|
CondPrimary.RelOp := roNone;
|
|
CondPrimary.SimpleExp1.Assign(NewExp);
|
|
NewExp.Free;
|
|
Result := True;
|
|
end;
|
|
if not Result then {!!.11}
|
|
Result := CondPrimary.Reduce; {!!.11}
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCondFactor.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlFloatLiteral===============================================}
|
|
procedure TffSqlFloatLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlFloatLiteral then begin
|
|
Value := TffSqlFloatLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlFloatLiteral.ConvertToNative;
|
|
var
|
|
Code : Integer;
|
|
begin
|
|
case GetType of
|
|
fftSingle :
|
|
Val(Value, SingleValue, Code);
|
|
fftDouble :
|
|
Val(Value, DoubleValue, Code);
|
|
fftExtended :
|
|
Val(Value, ExtendedValue, Code);
|
|
fftComp :
|
|
Val(Value, Comp(CompValue), Code);
|
|
fftCurrency :
|
|
begin
|
|
FFValCurr(Value, CurrencyValue, Code);
|
|
end;
|
|
end;
|
|
Converted := Code = 0;
|
|
end;
|
|
|
|
procedure TffSqlFloatLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, Value);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFloatLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlFloatLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlFloatLiteral)
|
|
and (AnsiCompareText(Value, TffSqlFloatLiteral(Other).Value) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlFloatLiteral.GetDecimals: Integer;
|
|
begin
|
|
Result := 2;
|
|
end;
|
|
{--------}
|
|
function TffSqlFloatLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftDouble;
|
|
end;
|
|
{--------}
|
|
function TffSqlFloatLiteral.GetValue: Variant;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
case GetType of
|
|
fftSingle :
|
|
Result := SingleValue;
|
|
fftDouble :
|
|
Result := DoubleValue;
|
|
fftExtended :
|
|
Result := ExtendedValue;
|
|
fftComp :
|
|
Result := Comp(CompValue);
|
|
fftCurrency :
|
|
Result := CurrencyValue;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFloatLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftByte..fftAutoInc :
|
|
;
|
|
fftSingle..fftCurrency :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlIntegerLiteral=============================================}
|
|
procedure TffSqlIntegerLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlIntegerLiteral then begin
|
|
Value := TffSqlIntegerLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlIntegerLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, Value);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIntegerLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlIntegerLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlIntegerLiteral)
|
|
and (AnsiCompareText(Value, TffSqlFloatLiteral(Other).Value) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlIntegerLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftInt32;
|
|
end;
|
|
|
|
procedure TffSqlIntegerLiteral.ConvertToNative;
|
|
begin
|
|
Int32Value := StrToInt(Value);
|
|
Converted := True;
|
|
end;
|
|
|
|
function TffSqlIntegerLiteral.GetValue: Variant;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
Result := Int32Value;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIntegerLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftByte..fftCurrency :
|
|
;
|
|
fftShortString..fftWideString :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlStringLiteral==============================================}
|
|
procedure TffSqlStringLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlStringLiteral then begin
|
|
Value := TffSqlStringLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlStringLiteral.ConvertToNative;
|
|
var
|
|
S : string;
|
|
P : Integer;
|
|
begin
|
|
S := copy(Value, 2, length(Value) - 2); //strip quotes
|
|
{convert internal double-quotes to single quotes}
|
|
P := pos('''''', S);
|
|
while P <> 0 do begin
|
|
Delete(S, P, 1);
|
|
P := pos('''''', S);
|
|
end;
|
|
Assert(GetType in [fftChar, fftWideChar,
|
|
fftShortString..fftWideString]);
|
|
case GetType of
|
|
fftChar :
|
|
CharValue := S[1];
|
|
fftWideChar :
|
|
WideCharValue := WideChar(S[1]);
|
|
fftShortString :
|
|
ShortStringValue := S;
|
|
fftShortAnsiStr :
|
|
ShortAnsiStringValue := S;
|
|
fftNullString :
|
|
NullStringValue := PChar(S);
|
|
fftNullAnsiStr :
|
|
NullAnsiStrValue := PChar(S);
|
|
fftWideString :
|
|
WideStringValue := S;
|
|
end;
|
|
Converted := True;
|
|
end;
|
|
{--------}
|
|
constructor TffSqlStringLiteral.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FType := fftNullAnsiStr; {!!.11}
|
|
end;
|
|
{--------}
|
|
procedure TffSqlStringLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, Value);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlStringLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlStringLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlStringLiteral)
|
|
and (AnsiCompareText(Value, TffSqlStringLiteral(Other).Value) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlStringLiteral.GetSize: Integer;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
Assert(GetType in [fftChar..fftWideString]);
|
|
case GetType of
|
|
fftChar :
|
|
Result := 1;
|
|
fftWideChar :
|
|
Result := 2;
|
|
fftShortString :
|
|
Result := length(ShortStringValue);
|
|
fftShortAnsiStr :
|
|
Result := length(ShortAnsiStringValue);
|
|
fftNullString :
|
|
Result := length(NullStringValue{^});
|
|
fftNullAnsiStr :
|
|
Result := length(NullAnsiStrValue);
|
|
else //fftWideString :
|
|
Result := length(WideStringValue);
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlStringLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := FType;
|
|
end;
|
|
{--------}
|
|
function TffSqlStringLiteral.GetValue: Variant;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
Assert(GetType in [fftChar..fftWideString]);
|
|
case GetType of
|
|
fftChar :
|
|
Result := CharValue;
|
|
fftWideChar :
|
|
Result := WideCharValue;
|
|
fftShortString :
|
|
Result := ShortStringValue;
|
|
fftShortAnsiStr :
|
|
Result := ShortAnsiStringValue;
|
|
fftNullString :
|
|
Result := NullStringValue{^};
|
|
fftNullAnsiStr :
|
|
Result := NullAnsiStrValue;
|
|
fftWideString :
|
|
Result := WideStringValue;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlStringLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString..fftWideString :
|
|
begin
|
|
FType := ExpectedType;
|
|
Converted := False;
|
|
end;
|
|
{Begin !!.11}
|
|
fftBLOB..fftBLOBTypedBin :
|
|
begin
|
|
FType := fftNullAnsiStr;
|
|
Converted := False;
|
|
end;
|
|
{End !!.11}
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlLiteral====================================================}
|
|
procedure TffSqlLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlLiteral then begin
|
|
Clear;
|
|
|
|
if assigned(TffSqlLiteral(Source).FloatLiteral) then begin
|
|
FloatLiteral := TffSqlFloatLiteral.Create(Self);
|
|
FloatLiteral.Assign(TffSqlLiteral(Source).FloatLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).IntegerLiteral) then begin
|
|
IntegerLiteral := TffSqlIntegerLiteral.Create(Self);
|
|
IntegerLiteral.Assign(TffSqlLiteral(Source).IntegerLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).StringLiteral) then begin
|
|
StringLiteral := TffSqlStringLiteral.Create(Self);
|
|
StringLiteral.Assign(TffSqlLiteral(Source).StringLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).DateLiteral) then begin
|
|
DateLiteral := TffSqlDateLiteral.Create(Self);
|
|
DateLiteral.Assign(TffSqlLiteral(Source).DateLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).TimeLiteral) then begin
|
|
TimeLiteral := TffSqlTimeLiteral.Create(Self);
|
|
TimeLiteral.Assign(TffSqlLiteral(Source).TimeLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).TimeStampLiteral) then begin
|
|
TimeStampLiteral := TffSqlTimeStampLiteral.Create(Self);
|
|
TimeStampLiteral.Assign(TffSqlLiteral(Source).TimeStampLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).IntervalLiteral) then begin
|
|
IntervalLiteral := TffSqlIntervalLiteral.Create(Self);
|
|
IntervalLiteral.Assign(TffSqlLiteral(Source).IntervalLiteral);
|
|
end;
|
|
|
|
if assigned(TffSqlLiteral(Source).BooleanLiteral) then begin
|
|
BooleanLiteral := TffSqlBooleanLiteral.Create(Self);
|
|
BooleanLiteral.Assign(TffSqlLiteral(Source).BooleanLiteral);
|
|
end;
|
|
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlLiteral.Clear;
|
|
begin
|
|
FloatLiteral.Free;
|
|
IntegerLiteral.Free;
|
|
StringLiteral.Free;
|
|
DateLiteral.Free;
|
|
TimeLiteral.Free;
|
|
TimeStampLiteral.Free;
|
|
IntervalLiteral.Free;
|
|
BooleanLiteral.Free;
|
|
FloatLiteral:= nil;
|
|
IntegerLiteral:= nil;
|
|
StringLiteral:= nil;
|
|
DateLiteral:= nil;
|
|
TimeLiteral:= nil;
|
|
TimeStampLiteral:= nil;
|
|
IntervalLiteral:= nil;
|
|
BooleanLiteral := nil;
|
|
end;
|
|
|
|
destructor TffSqlLiteral.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
if FloatLiteral <> nil then
|
|
FloatLiteral.EmitSQL(Stream)
|
|
else
|
|
if IntegerLiteral <> nil then
|
|
IntegerLiteral.EmitSQL(Stream)
|
|
else
|
|
if StringLiteral <> nil then
|
|
StringLiteral.EmitSQL(Stream)
|
|
else
|
|
if DateLiteral <> nil then
|
|
DateLiteral.EmitSQL(Stream)
|
|
else
|
|
if TimeLiteral <> nil then
|
|
TimeLiteral.EmitSQL(Stream)
|
|
else
|
|
if TimestampLiteral <> nil then
|
|
TimestampLiteral.EmitSQL(Stream)
|
|
else
|
|
if IntervalLiteral <> nil then
|
|
IntervalLiteral.EmitSQL(Stream)
|
|
else
|
|
if BooleanLiteral <> nil then
|
|
BooleanLiteral.EmitSQL(Stream)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.AddIntervalTo(Target: TDateTime): TDateTime;
|
|
begin
|
|
if IntervalLiteral <> nil then
|
|
Result := IntervalLiteral.AddIntervalTo(Target)
|
|
else begin
|
|
SQLError('Internal error: Type Mismatch');
|
|
Result := Null;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
begin
|
|
if IntervalLiteral <> nil then
|
|
Result := IntervalLiteral.SubtractIntervalFrom(Target)
|
|
else begin
|
|
SQLError('Internal error: Type Mismatch');
|
|
Result := Null;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if FloatLiteral <> nil then
|
|
FloatLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if IntegerLiteral <> nil then
|
|
IntegerLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if StringLiteral <> nil then
|
|
StringLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if DateLiteral <> nil then
|
|
DateLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if TimeLiteral <> nil then
|
|
TimeLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if TimestampLiteral <> nil then
|
|
TimestampLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if IntervalLiteral <> nil then
|
|
IntervalLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if BooleanLiteral <> nil then
|
|
BooleanLiteral.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.GetValue: Variant;
|
|
begin
|
|
if FloatLiteral <> nil then
|
|
Result := FloatLiteral.GetValue
|
|
else
|
|
if IntegerLiteral <> nil then
|
|
Result := IntegerLiteral.GetValue
|
|
else
|
|
if StringLiteral <> nil then
|
|
Result := StringLiteral.GetValue
|
|
else
|
|
if DateLiteral <> nil then
|
|
Result := DateLiteral.GetValue
|
|
else
|
|
if TimeLiteral <> nil then
|
|
Result := TimeLiteral.GetValue
|
|
else
|
|
if TimestampLiteral <> nil then
|
|
Result := TimestampLiteral.GetValue
|
|
else
|
|
if IntervalLiteral <> nil then
|
|
Result := IntervalLiteral.GetValue
|
|
else
|
|
if BooleanLiteral <> nil then
|
|
Result := BooleanLiteral.GetValue
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlLiteral)
|
|
and
|
|
(BothNil(FloatLiteral, TffSqlLiteral(Other).FloatLiteral)
|
|
or (BothNonNil(FloatLiteral, TffSqlLiteral(Other).FloatLiteral)
|
|
and FloatLiteral.Equals(TffSqlLiteral(Other).FloatLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(IntegerLiteral, TffSqlLiteral(Other).IntegerLiteral)
|
|
or (BothNonNil(IntegerLiteral, TffSqlLiteral(Other).IntegerLiteral)
|
|
and IntegerLiteral.Equals(TffSqlLiteral(Other).IntegerLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(StringLiteral, TffSqlLiteral(Other).StringLiteral)
|
|
or (BothNonNil(StringLiteral, TffSqlLiteral(Other).StringLiteral)
|
|
and StringLiteral.Equals(TffSqlLiteral(Other).StringLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(DateLiteral, TffSqlLiteral(Other).DateLiteral)
|
|
or (BothNonNil(DateLiteral, TffSqlLiteral(Other).DateLiteral)
|
|
and DateLiteral.Equals(TffSqlLiteral(Other).DateLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(TimeLiteral, TffSqlLiteral(Other).TimeLiteral)
|
|
or (BothNonNil(TimeLiteral, TffSqlLiteral(Other).TimeLiteral)
|
|
and TimeLiteral.Equals(TffSqlLiteral(Other).TimeLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(TimestampLiteral, TffSqlLiteral(Other).TimestampLiteral)
|
|
or (BothNonNil(TimestampLiteral, TffSqlLiteral(Other).TimestampLiteral)
|
|
and TimestampLiteral.Equals(TffSqlLiteral(Other).TimestampLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(IntervalLiteral, TffSqlLiteral(Other).IntervalLiteral)
|
|
or (BothNonNil(IntervalLiteral, TffSqlLiteral(Other).IntervalLiteral)
|
|
and IntervalLiteral.Equals(TffSqlLiteral(Other).IntervalLiteral)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(BooleanLiteral, TffSqlLiteral(Other).BooleanLiteral)
|
|
or (BothNonNil(BooleanLiteral, TffSqlLiteral(Other).BooleanLiteral)
|
|
and BooleanLiteral.Equals(TffSqlLiteral(Other).BooleanLiteral)
|
|
)
|
|
);
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.GetDecimals: Integer;
|
|
begin
|
|
if FloatLiteral <> nil then
|
|
Result := FloatLiteral.GetDecimals
|
|
else
|
|
Result := 0;
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.GetSize: Integer;
|
|
begin
|
|
Result := 0;
|
|
if FloatLiteral <> nil then
|
|
Result := FloatLiteral.GetSize
|
|
else
|
|
if IntegerLiteral <> nil then
|
|
Result := IntegerLiteral.GetSize
|
|
else
|
|
if StringLiteral <> nil then
|
|
Result := StringLiteral.GetSize
|
|
else
|
|
if DateLiteral <> nil then
|
|
Result := DateLiteral.GetSize
|
|
else
|
|
if TimeLiteral <> nil then
|
|
Result := TimeLiteral.GetSize
|
|
else
|
|
if TimestampLiteral <> nil then
|
|
Result := TimestampLiteral.GetSize
|
|
else
|
|
if IntervalLiteral <> nil then
|
|
Result := IntervalLiteral.GetSize
|
|
else
|
|
if BooleanLiteral <> nil then
|
|
Result := BooleanLiteral.GetSize
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftInterval; {dummy to suppress compiler warning}
|
|
if FloatLiteral <> nil then
|
|
Result := FloatLiteral.GetType
|
|
else
|
|
if IntegerLiteral <> nil then
|
|
Result := IntegerLiteral.GetType
|
|
else
|
|
if StringLiteral <> nil then
|
|
Result := StringLiteral.GetType
|
|
else
|
|
if DateLiteral <> nil then
|
|
Result := DateLiteral.GetType
|
|
else
|
|
if TimeLiteral <> nil then
|
|
Result := TimeLiteral.GetType
|
|
else
|
|
if TimestampLiteral <> nil then
|
|
Result := TimestampLiteral.GetType
|
|
else
|
|
if IntervalLiteral <> nil then
|
|
Result := IntervalLiteral.GetType
|
|
else
|
|
if BooleanLiteral <> nil then
|
|
Result := BooleanLiteral.GetType
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
|
|
function IsValidDate(const S: ShortString): Boolean;
|
|
begin
|
|
if (length(S) <> 12)
|
|
or (S[6] <> '-')
|
|
or (S[9] <> '-') then
|
|
Result := False
|
|
else
|
|
try
|
|
EncodeDate(
|
|
StrToInt(copy(S, 2, 4)),
|
|
StrToInt(copy(S, 7, 2)),
|
|
StrToInt(copy(S, 10, 2)));
|
|
Result := True;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function IsValidTime(const S: ShortString): Boolean;
|
|
begin
|
|
if (length(S) <> 10)
|
|
or (S[4] <> ':')
|
|
or (S[7] <> ':') then
|
|
Result := False
|
|
else
|
|
try
|
|
EncodeTime(
|
|
StrToInt(copy(S, 2, 2)),
|
|
StrToInt(copy(S, 5, 2)),
|
|
StrToInt(copy(S, 8, 2)),
|
|
0);
|
|
Result := True;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function IsValidTimestamp(const S: ShortString): Boolean;
|
|
begin
|
|
if (length(S) < 21)
|
|
or (S[6] <> '-')
|
|
or (S[9] <> '-')
|
|
or (S[12] <> ' ')
|
|
or (S[15] <> ':')
|
|
or (S[18] <> ':') then
|
|
Result := False
|
|
else
|
|
try
|
|
EncodeDate(
|
|
StrToInt(copy(S, 2, 4)),
|
|
StrToInt(copy(S, 7, 2)),
|
|
StrToInt(copy(S, 10, 2)));
|
|
EncodeTime(
|
|
StrToInt(copy(S, 13, 2)),
|
|
StrToInt(copy(S, 16, 2)),
|
|
StrToInt(copy(S, 19, 2)),
|
|
0);
|
|
Result := True;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
if FloatLiteral <> nil then
|
|
FloatLiteral.MatchType(ExpectedType)
|
|
else
|
|
if IntegerLiteral <> nil then
|
|
IntegerLiteral.MatchType(ExpectedType)
|
|
else
|
|
if StringLiteral <> nil then
|
|
case ExpectedType of
|
|
fftStDate, fftStTime, fftDateTime:
|
|
begin
|
|
{String literal, but caller was expecting a Date-type.}
|
|
{See if the string literal represents a valid date.}
|
|
{If it does, convert.}
|
|
if IsValidDate(StringLiteral.Value) then begin
|
|
DateLiteral := TffSqlDateLiteral.Create(Self);
|
|
DateLiteral.Value := StringLiteral.Value;
|
|
StringLiteral.Free;
|
|
StringLiteral := nil;
|
|
end else
|
|
{See if the string literal represents a valid time.}
|
|
{If it does, convert.}
|
|
if IsValidTime(StringLiteral.Value) then begin
|
|
TimeLiteral := TffSqlTimeLiteral.Create(Self);
|
|
TimeLiteral.Value := StringLiteral.Value;
|
|
StringLiteral.Free;
|
|
StringLiteral := nil;
|
|
end else
|
|
{See if the string literal represents a valid time stamp}
|
|
{If it does, convert.}
|
|
if IsValidTimestamp(StringLiteral.Value) then begin
|
|
TimeStampLiteral := TffSqlTimestampLiteral.Create(Self);
|
|
TimeStampLiteral.Value := StringLiteral.Value;
|
|
StringLiteral.Free;
|
|
StringLiteral := nil;
|
|
end else
|
|
TypeMismatch;
|
|
end;
|
|
else
|
|
StringLiteral.MatchType(ExpectedType);
|
|
end
|
|
else
|
|
if DateLiteral <> nil then
|
|
DateLiteral.MatchType(ExpectedType)
|
|
else
|
|
if TimeLiteral <> nil then
|
|
TimeLiteral.MatchType(ExpectedType)
|
|
else
|
|
if TimestampLiteral <> nil then
|
|
TimestampLiteral.MatchType(ExpectedType)
|
|
else
|
|
if IntervalLiteral <> nil then
|
|
IntervalLiteral.MatchType(ExpectedType)
|
|
else
|
|
if BooleanLiteral <> nil then
|
|
BooleanLiteral.MatchType(ExpectedType)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlParam======================================================}
|
|
procedure TffSqlParam.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlParam then begin
|
|
FParmIndex := TffSqlParam(Source).FParmIndex;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
constructor TffSqlParam.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FParmIndex := Owner.ParmCount;
|
|
inc(Owner.ParmCount);
|
|
end;
|
|
{--------}
|
|
function TffSqlParam.GetDecimals: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
{--------}
|
|
function TffSqlParam.GetSize: Integer;
|
|
begin
|
|
case GetType of
|
|
fftWideString : Result := length(GetValue);
|
|
fftShortAnsiStr : Result := length(GetValue);
|
|
fftBLOB : Result := VarArrayHighBound(GetValue, 1); {!!.13}
|
|
else
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlParam.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
Result := '?';
|
|
end;
|
|
{--------}
|
|
function TffSqlParam.GetType: TffFieldType;
|
|
var
|
|
V : Variant;
|
|
begin
|
|
Result := fftInterval; {dummy to suppress compiler warning}
|
|
V := Owner.ParmList.GetValue(ParmIndex);
|
|
case VarType(V) and VarTypeMask of
|
|
varSmallint : Result := fftInt32;
|
|
varInteger : Result := fftInt32;
|
|
varSingle : Result := fftSingle;
|
|
varDouble : Result := fftDouble;
|
|
varCurrency : Result := fftCurrency;
|
|
varDate : Result := fftDateTime;
|
|
varOleStr : Result := fftWideString;
|
|
varBoolean : Result := fftBoolean;
|
|
varString : Result := fftShortAnsiStr;
|
|
varByte : Result := fftBLOB; {!!.13}
|
|
else
|
|
SQLError('Unsupported parameter type:'+IntToHex(VarType(V),0));
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlParam.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' ?');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlParam.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlParam.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlParam) {
|
|
and (AnsiCompareText(Name, TffSqlParam(Other).Name) = 0)};
|
|
end;
|
|
{--------}
|
|
function TffSqlParam.GetValue: Variant;
|
|
begin
|
|
if Owner.ParmList = nil then
|
|
raise Exception.Create('No parameter values specified for query. ' +
|
|
'Verify the parameters listed in the ' +
|
|
'TffQuery.Params property matches the ' +
|
|
'parameters specified in the TffQuery.SQL ' +
|
|
'property.');
|
|
Result := Owner.ParmList.GetValue(ParmIndex);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlParam.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlFactor=====================================================}
|
|
function TffSqlFactor.AddIntervalTo(Target: TDateTime): TDateTime;
|
|
begin
|
|
if Literal <> nil then
|
|
Result := Literal.AddIntervalTo(Target)
|
|
else begin
|
|
SQLError('Not implemented');
|
|
Result := Null;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
begin
|
|
if Literal <> nil then
|
|
Result := Literal.SubtractIntervalFrom(Target)
|
|
else begin
|
|
SQLError('Not implemented');
|
|
Result := Null;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
if SubQuery <> nil then
|
|
FIsConstant := False
|
|
else
|
|
if CondExp <> nil then
|
|
FIsConstant := CondExp.IsConstant
|
|
else
|
|
if FieldRef <> nil then
|
|
FIsConstant := False
|
|
else
|
|
if Literal <> nil then
|
|
FIsConstant := {True} Literal.IntervalLiteral = nil
|
|
{can't store interval values, so we can't handle those
|
|
as constant values even if they are in fact constant}
|
|
else
|
|
if Param <> nil then
|
|
FIsConstant := False
|
|
else
|
|
if Aggregate <> nil then
|
|
FIsConstant := False
|
|
else
|
|
if ScalarFunc <> nil then
|
|
FIsConstant := ScalarFunc.IsConstant
|
|
else
|
|
Assert(False);
|
|
if FIsConstant then begin
|
|
FIsConstant := False;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.CheckType;
|
|
begin
|
|
if SubQuery <> nil then
|
|
FType := SubQuery.GetType
|
|
else
|
|
if CondExp <> nil then
|
|
FType:= CondExp.GetType
|
|
else
|
|
if FieldRef <> nil then
|
|
FType := FieldRef.GetType
|
|
else
|
|
if Literal <> nil then
|
|
FType := Literal.GetType
|
|
else
|
|
if Param <> nil then
|
|
FType := Param.GetType
|
|
else
|
|
if Aggregate <> nil then
|
|
FType := Aggregate.GetType
|
|
else
|
|
if ScalarFunc <> nil then
|
|
FType := ScalarFunc.GetType
|
|
else
|
|
Assert(False);
|
|
if UnaryMinus then
|
|
case FType of
|
|
fftByte,
|
|
fftWord16,
|
|
fftWord32,
|
|
fftInt8,
|
|
fftInt16,
|
|
fftInt32,
|
|
fftAutoInc,
|
|
fftSingle,
|
|
fftDouble,
|
|
fftExtended,
|
|
fftComp,
|
|
fftCurrency :
|
|
;
|
|
else
|
|
SQLError('Operator/operand mismatch');
|
|
end;
|
|
TypeKnown := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.Clear;
|
|
begin
|
|
SubQuery.Free;
|
|
CondExp.Free;
|
|
FieldRef.Free;
|
|
Literal.Free;
|
|
Param.Free;
|
|
Aggregate.Free;
|
|
ScalarFunc.Free;
|
|
SubQuery:= nil;
|
|
CondExp:= nil;
|
|
FieldRef:= nil;
|
|
Literal:= nil;
|
|
Param:= nil;
|
|
Aggregate:= nil;
|
|
ScalarFunc:= nil;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.DependsOn(Table)
|
|
else
|
|
if CondExp <> nil then
|
|
Result := CondExp.DependsOn(Table)
|
|
else
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.DependsOn(Table)
|
|
else
|
|
if Literal <> nil then
|
|
Result := False
|
|
else
|
|
if Param <> nil then
|
|
Result := False
|
|
else
|
|
if Aggregate <> nil then
|
|
Result := Aggregate.DependsOn(Table)
|
|
else
|
|
if ScalarFunc <> nil then
|
|
Result := ScalarFunc.DependsOn(Table)
|
|
else begin
|
|
Assert(False);
|
|
Result := False;
|
|
end;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlFactor.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.EmitSQL(Stream: TStream);
|
|
begin
|
|
if UnaryMinus then
|
|
WriteStr(Stream,' - ');
|
|
if SubQuery <> nil then begin
|
|
WriteStr(Stream,' (');
|
|
SubQuery.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end else
|
|
if CondExp <> nil then begin
|
|
WriteStr(Stream,' (');
|
|
CondExp.EmitSQL(Stream);
|
|
WRiteStr(Stream,')');
|
|
end else
|
|
if FieldRef <> nil then
|
|
FieldRef.EmitSQL(Stream)
|
|
else
|
|
if Literal <> nil then
|
|
Literal.EmitSQL(Stream)
|
|
else
|
|
if Param <> nil then
|
|
Param.EmitSQL(Stream)
|
|
else
|
|
if Aggregate <> nil then
|
|
Aggregate.EmitSQL(Stream)
|
|
else
|
|
if ScalarFunc <> nil then
|
|
ScalarFunc.EmitSQL(Stream)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if SubQuery <> nil then
|
|
SubQuery.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if CondExp <> nil then
|
|
CondExp.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if FieldRef <> nil then
|
|
FieldRef.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if Literal <> nil then
|
|
Literal.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if Param <> nil then
|
|
Param.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if ScalarFunc <> nil then
|
|
ScalarFunc.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
if Aggregate <> nil then
|
|
Aggregate.EnumNodes(EnumMethod, Deep)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlFactor)
|
|
and (MulOp = TffSqlFactor(Other).MulOp)
|
|
and (UnaryMinus = TffSqlFactor(Other).UnaryMinus)
|
|
and
|
|
(BothNil(CondExp, TffSqlFactor(Other).CondExp)
|
|
or (
|
|
BothNonNil(CondExp, TffSqlFactor(Other).CondExp)
|
|
and CondExp.Equals(TffSqlFactor(Other).CondExp)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(FieldRef, TffSqlFactor(Other).FieldRef)
|
|
or (
|
|
BothNonNil(FieldRef, TffSqlFactor(Other).FieldRef)
|
|
and FieldRef.Equals(TffSqlFactor(Other).FieldRef)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(Literal, TffSqlFactor(Other).Literal)
|
|
or (
|
|
BothNonNil(Literal, TffSqlFactor(Other).Literal)
|
|
and Literal.Equals(TffSqlFactor(Other).Literal)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(Param, TffSqlFactor(Other).Param)
|
|
or (
|
|
BothNonNil(Param, TffSqlFactor(Other).Param)
|
|
and Param.Equals(TffSqlFactor(Other).Param)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(Aggregate, TffSqlFactor(Other).Aggregate)
|
|
or (
|
|
BothNonNil(Aggregate, TffSqlFactor(Other).Aggregate)
|
|
and Aggregate.Equals(TffSqlFactor(Other).Aggregate)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(SubQuery, TffSqlFactor(Other).SubQuery)
|
|
or (
|
|
BothNonNil(SubQuery, TffSqlFactor(Other).SubQuery)
|
|
and SubQuery.Equals(TffSqlFactor(Other).SubQuery)
|
|
)
|
|
)
|
|
and
|
|
(BothNil(ScalarFunc, TffSqlFactor(Other).ScalarFunc)
|
|
or (
|
|
BothNonNil(ScalarFunc, TffSqlFactor(Other).ScalarFunc)
|
|
and ScalarFunc.Equals(TffSqlFactor(Other).ScalarFunc)
|
|
)
|
|
);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.GetDecimals: Integer;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.GetDecimals
|
|
else
|
|
if CondExp <> nil then
|
|
Result := CondExp.GetDecimals
|
|
else
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.GetDecimals
|
|
else
|
|
if Literal <> nil then
|
|
Result := Literal.GetDecimals
|
|
else
|
|
if Param <> nil then
|
|
Result := Param.GetDecimals
|
|
else
|
|
if Aggregate <> nil then
|
|
Result := Aggregate.GetDecimals
|
|
else
|
|
if ScalarFunc <> nil then
|
|
Result := ScalarFunc.GetDecimals
|
|
else begin
|
|
Assert(False);
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.GetSize: Integer;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.GetSize
|
|
else
|
|
if CondExp <> nil then
|
|
Result := CondExp.GetSize
|
|
else
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.GetSize
|
|
else
|
|
if Literal <> nil then
|
|
Result := Literal.GetSize
|
|
else
|
|
if Param <> nil then
|
|
Result := Param.GetSize
|
|
else
|
|
if Aggregate <> nil then
|
|
Result := Aggregate.GetSize
|
|
else
|
|
if ScalarFunc <> nil then
|
|
Result := ScalarFunc.GetSize
|
|
else begin
|
|
Assert(False);
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := 'SUB'
|
|
else
|
|
if CondExp <> nil then
|
|
Result := CondExp.GetTitle(Qualified) {!!.11}
|
|
else
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.GetTitle(Qualified) {!!.11}
|
|
else
|
|
if Literal <> nil then
|
|
Result := 'LIT'
|
|
else
|
|
if Param <> nil then
|
|
Result := Param.GetTitle(Qualified) {!!.11}
|
|
else
|
|
if ScalarFunc <> nil then
|
|
Result := ScalarFunc.GetTitle(Qualified) {!!.11}
|
|
else
|
|
if Aggregate <> nil then
|
|
Result := Aggregate.GetTitle(Qualified) {!!.11}
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.GetType: TffFieldType;
|
|
begin
|
|
if not TypeKnown then
|
|
CheckType;
|
|
Result := FType
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlFactor then begin
|
|
Clear;
|
|
|
|
MulOp := TffSqlFactor(Source).MulOp;
|
|
|
|
UnaryMinus := TffSqlFactor(Source).UnaryMinus;
|
|
|
|
if assigned(TffSqlFactor(Source).CondExp) then begin
|
|
CondExp := TffSqlCondExp.Create(Self);
|
|
CondExp.Assign(TffSqlFactor(Source).CondExp);
|
|
end;
|
|
|
|
if assigned(TffSqlFactor(Source).FieldRef) then begin
|
|
FieldRef := TffSqlFieldRef.Create(Self);
|
|
FieldRef.Assign(TffSqlFactor(Source).FieldRef);
|
|
end;
|
|
|
|
if assigned(TffSqlFactor(Source).Literal) then begin
|
|
Literal := TffSqlLiteral.Create(Self);
|
|
Literal.Assign(TffSqlFactor(Source).Literal);
|
|
end;
|
|
|
|
if assigned(TffSqlFactor(Source).Param) then begin
|
|
Param := TffSqlParam.Create(Self);
|
|
Param.Assign(TffSqlFactor(Source).Param);
|
|
end;
|
|
|
|
if assigned(TffSqlFactor(Source).Aggregate) then begin
|
|
Aggregate := TffSqlAggregate.Create(Self);
|
|
Aggregate.Assign(TffSqlFactor(Source).Aggregate);
|
|
end;
|
|
|
|
if assigned(TffSqlFactor(Source).SubQuery) then begin
|
|
SubQuery := TffSqlSELECT.Create(Self);
|
|
SubQuery.Assign(TffSqlFactor(Source).SubQuery);
|
|
end;
|
|
|
|
if assigned(TffSqlFactor(Source).ScalarFunc) then begin
|
|
ScalarFunc := TffSqlScalarFunc.Create(Self);
|
|
ScalarFunc.Assign(TffSqlFactor(Source).ScalarFunc);
|
|
end;
|
|
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.GetValue: Variant;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.GetValue
|
|
else
|
|
if CondExp <> nil then
|
|
Result := CondExp.GetValue
|
|
else
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.GetValue
|
|
else
|
|
if Literal <> nil then
|
|
Result := Literal.GetValue
|
|
else
|
|
if Param <> nil then
|
|
Result := Param.GetValue
|
|
else
|
|
if Aggregate <> nil then
|
|
Result := Aggregate.GetAggregateValue
|
|
else
|
|
if ScalarFunc <> nil then
|
|
Result := ScalarFunc.GetValue
|
|
else
|
|
Assert(False);
|
|
if UnaryMinus then
|
|
if not VarIsNull(Result) then
|
|
Result := - Result;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.HasFieldRef: Boolean;
|
|
begin
|
|
Result := (FieldRef <> nil);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.IsField(var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
begin
|
|
Result := (FieldRef <> nil) and not UnaryMinus;
|
|
if Result then
|
|
FieldReferenced := FieldRef.Field;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy; var SameCase: Boolean): Boolean;
|
|
begin
|
|
Result := (FieldRef <> nil) and
|
|
(FieldRef.Field <> nil) and
|
|
(FieldRef.Field.OwnerTable = Table);
|
|
if Result then begin
|
|
FieldReferenced := FieldRef.Field;
|
|
SameCase := True;
|
|
end else
|
|
if ScalarFunc <> nil then begin
|
|
Result := ScalarFunc.IsFieldFrom(Table, FieldReferenced);
|
|
SameCase := False;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.IsNull: Boolean;
|
|
begin
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.IsNull
|
|
else
|
|
Result := VarIsNull(GetValue);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.IsAggregate: Boolean;
|
|
begin
|
|
Result := Aggregate <> nil;
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
if SubQuery <> nil then
|
|
SubQuery.MatchType(ExpectedType, True)
|
|
else
|
|
if CondExp <> nil then
|
|
CondExp.MatchType(ExpectedType)
|
|
else
|
|
if FieldRef <> nil then
|
|
FieldRef.MatchType(ExpectedType)
|
|
else
|
|
if Literal <> nil then
|
|
Literal.MatchType(ExpectedType)
|
|
else
|
|
if Param <> nil then
|
|
Param.MatchType(ExpectedType)
|
|
else
|
|
if Aggregate <> nil then
|
|
Aggregate.MatchType(ExpectedType)
|
|
else
|
|
if ScalarFunc <> nil then
|
|
ScalarFunc.MatchType(ExpectedType)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
{--------}
|
|
function TffSqlFactor.Reduce: Boolean;
|
|
var
|
|
LiftFactor: TffSqlFactor;
|
|
begin
|
|
if SubQuery <> nil then
|
|
Result := SubQuery.Reduce
|
|
else
|
|
if CondExp <> nil then begin
|
|
{!!.11 begin}
|
|
{if conditional expression is nothing but a parenthesized factor,
|
|
lift it to this level}
|
|
LiftFactor := nil;
|
|
if CondExp.CondTermCount = 1 then
|
|
with CondExp.CondTerm[0] do
|
|
if CondFactorCount = 1 then
|
|
with CondFactor[0] do
|
|
if not UnaryNot then
|
|
with CondPrimary do
|
|
if (RelOp = roNone) and (SimpleExp2 = nil) then
|
|
with SimpleExp1 do
|
|
if TermCount = 1 then
|
|
with Term[0] do
|
|
if FactorCount = 1 then begin
|
|
LiftFactor := TffSqlFactor.Create(Parent);
|
|
LiftFactor.Assign(Factor[0]);
|
|
LiftFactor.MulOp := MulOp; {!!.13}
|
|
end;
|
|
if LiftFactor <> nil then begin
|
|
CondExp.Free;
|
|
CondExp := nil;
|
|
Assign(LiftFactor);
|
|
LiftFactor.Free;
|
|
Result := True;
|
|
end else
|
|
{!!.11 end}
|
|
Result := CondExp.Reduce
|
|
end else
|
|
if FieldRef <> nil then
|
|
Result := False
|
|
else
|
|
if Literal <> nil then
|
|
Result := False
|
|
else
|
|
if Param <> nil then
|
|
Result := False
|
|
else
|
|
if Aggregate <> nil then
|
|
Result := Aggregate.Reduce
|
|
else
|
|
if ScalarFunc <> nil then
|
|
Result := ScalarFunc.Reduce
|
|
else
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlFactor.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
{Begin !!.11}
|
|
{--------}
|
|
function TffSqlFactor.WasWildcard : Boolean;
|
|
begin
|
|
if FieldRef <> nil then
|
|
Result := FieldRef.WasWildcard
|
|
else
|
|
Result := False;
|
|
end;
|
|
{End !!.11}
|
|
{====================================================================}
|
|
|
|
{===TffSqlSelection==================================================}
|
|
procedure TffSqlSelection.AddColumnDef(Target: TffSqlColumnListOwner);
|
|
{Rewritten !!.11}
|
|
var
|
|
S, SQual : string;
|
|
F : TffSqlNode;
|
|
i : Integer;
|
|
begin
|
|
if Column <> nil then
|
|
S := Column.ColumnName
|
|
else
|
|
S := '';
|
|
F := SimpleExpression;
|
|
if S = '' then
|
|
S := SimpleExpression.GetTitle(False);
|
|
|
|
if Target.Columns.IndexOf(S) <> -1 then begin
|
|
{ See if we can use the qualified column name. This is done for the sake
|
|
of backwards compatibility with existing SQL statements in FF clients. }
|
|
SQual := SimpleExpression.GetTitle(True);
|
|
if Target.Columns.IndexOf(SQual) = -1 then
|
|
Target.Columns.AddObject(SQual, F)
|
|
else begin
|
|
i := 1;
|
|
repeat
|
|
inc(i);
|
|
until Target.Columns.IndexOf(S + '_' + IntToStr(i)) = -1;
|
|
Target.Columns.AddObject(S + '_' + IntToStr(i), F);
|
|
end;
|
|
end else
|
|
Target.Columns.AddObject(S, F);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelection.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlSelection then begin
|
|
SimpleExpression.Free;
|
|
SimpleExpression := TffSqlSimpleExpression.Create(Self);
|
|
SimpleExpression.Assign(TffSqlSelection(Source).SimpleExpression);
|
|
Column.Free;
|
|
Column := nil;
|
|
if assigned(TffSqlSelection(Source).Column) then begin
|
|
Column := TffSqlColumn.Create(Self);
|
|
Column.Assign(TffSqlSelection(Source).Column);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
destructor TffSqlSelection.Destroy;
|
|
begin
|
|
SimpleExpression.Free;
|
|
Column.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlSelection.EmitSQL(Stream: TStream);
|
|
begin
|
|
SimpleExpression.EmitSQL(Stream);
|
|
if Column <> nil then begin
|
|
WriteStr(Stream,' AS');
|
|
Column.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSelection.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SimpleExpression.EnumNodes(EnumMethod, Deep);
|
|
if Column <> nil then
|
|
Column.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlSelection.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlSelection)
|
|
and (
|
|
BothNil(SimpleExpression, TffSqlSelection(Other).SimpleExpression)
|
|
or (BothNonNil(SimpleExpression, TffSqlSelection(Other).SimpleExpression)
|
|
and SimpleExpression.Equals(TffSqlSelection(Other).SimpleExpression)
|
|
)
|
|
)
|
|
and (
|
|
BothNil(Column, TffSqlSelection(Other).Column)
|
|
or (BothNonNil(Column, TffSqlSelection(Other).Column)
|
|
and Column.Equals(TffSqlSelection(Other).Column)
|
|
)
|
|
);
|
|
end;
|
|
{--------}
|
|
function TffSqlSelection.GetIndex: Integer;
|
|
begin
|
|
Result := TffSqlSelectionList(Parent).FSelections.IndexOf(Self);
|
|
end;
|
|
|
|
{--------}
|
|
function TffSqlSelection.IsAggregateExpression: Boolean;
|
|
begin
|
|
Result := SimpleExpression.IsAggregateExpression;
|
|
end;
|
|
|
|
function TffSqlSelection.Reduce: Boolean;
|
|
begin
|
|
Result := SimpleExpression.Reduce;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlTableRef===================================================}
|
|
procedure TffSqlTableRef.AddTableReference(Select: TffSqlSELECT);
|
|
var
|
|
IX, I : Integer;
|
|
begin
|
|
IX := -1;
|
|
Assert(Assigned(Select.TablesReferencedByOrder));
|
|
if TableName <> '' then begin
|
|
if DatabaseName <> '' then
|
|
if not SameText(DatabaseName, Owner.FDatabase.Alias) then
|
|
SQLError(format('The referenced database name %s does not '+
|
|
'match the current database, %s.',
|
|
[DatabaseName, Owner.FDatabase.Alias]));
|
|
IX := Select.TablesReferencedByOrder.Add(TableName)
|
|
end else begin
|
|
Assert(Assigned(TableExp));
|
|
TableExp.EnsureResultTable(True);
|
|
if Select.TablesReferencedByOrder.IndexOf('$$UNNAMED') = -1 then
|
|
IX := Select.TablesReferencedByOrder.AddObject('$$UNNAMED',
|
|
TableExp.ResultTable)
|
|
else begin
|
|
I := 2;
|
|
repeat
|
|
if Select.TablesReferencedByOrder.IndexOf('$$UNNAMED_' + IntToStr(I)) =
|
|
-1 then begin
|
|
IX := Select.TablesReferencedByOrder.AddObject('$$UNNAMED_' +
|
|
IntToStr(I), TableExp.ResultTable);
|
|
break;
|
|
end;
|
|
inc(I);
|
|
until False;
|
|
end;
|
|
end;
|
|
if Alias <> '' then begin
|
|
Assert(Assigned(Select.TableAliases));
|
|
if Select.TableAliases.IndexOf(Alias) <> -1 then
|
|
SQLError('Duplicate alias definition:' + Alias);
|
|
Select.TableAliases.AddObject(Alias, TObject(IX));
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTableRef.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlTableRef then begin
|
|
Clear;
|
|
TableName := TffSqlTableRef(Source).TableName;
|
|
Alias := TffSqlTableRef(Source).Alias;
|
|
if TffSqlTableRef(Source).TableExp <> nil then begin
|
|
TableExp := TffSqlTableExp.Create(Self);
|
|
TableExp.Assign(TffSqlTableRef(Source).TableExp);
|
|
end;
|
|
if TffSqlTableRef(Source).ColumnList <> nil then begin
|
|
ColumnList := TFFSqlInsertColumnList.Create(Self);
|
|
ColumnList.Assign(TffSqlTableRef(Source).ColumnList);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlTableRef.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
{- not used for binding directly from SELECT - only for
|
|
binding to contained sub-expressions}
|
|
begin
|
|
if TableExp <> nil then
|
|
Result := TableExp.BindFieldDown(TableName, FieldName)
|
|
else
|
|
if SameText(TableName, Self.TableName)
|
|
and (Alias = '') {can't bind to table name if alias present} {!!.12}
|
|
or SameText(TableName, Alias) then
|
|
Result := ResultTable.FieldByName(FieldName)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TffSqlTableRef.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
if SameText(TableName, Alias) or SameText(TableName, Self.TableName) then
|
|
Result := GetTable(AOwner, False)
|
|
else
|
|
if TableExp <> nil then
|
|
Result := TableExp.BindTable(AOwner, TableName)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TffSqlTableRef.Clear;
|
|
begin
|
|
TableName := '';
|
|
Alias := '';
|
|
TableExp.Free;
|
|
TableExp := nil;
|
|
ColumnList.Free;
|
|
ColumnList := nil;
|
|
end;
|
|
|
|
function TffSqlTableRef.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if TableExp <> nil then
|
|
Result := TableExp.DependsOn(Table)
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
destructor TffSqlTableRef.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlTableRef.EmitSQL(Stream: TStream);
|
|
begin
|
|
if TableName <> '' then begin
|
|
WriteStr(Stream, ' ');
|
|
WriteStr(Stream, TableName);
|
|
if Alias <> '' then begin
|
|
WriteStr(Stream,' AS ');
|
|
WriteStr(Stream, Alias);
|
|
end;
|
|
end else
|
|
if TableExp <> nil then begin
|
|
WriteStr(Stream, ' (');
|
|
TableExp.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
if Alias <> '' then begin
|
|
WriteStr(Stream,' AS ');
|
|
WriteStr(Stream, Alias);
|
|
end;
|
|
if ColumnList <> nil then begin
|
|
WriteStr(Stream, ' (');
|
|
ColumnList.EmitSQL(Stream);
|
|
WriteStr(Stream, ')');
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTableRef.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if Deep and assigned(TableExp) then
|
|
TableExp.EnumNodes(EnumMethod, Deep);
|
|
if assigned(ColumnList) then
|
|
ColumnList.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
|
|
function TffSqlTableRef.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlTableRef)
|
|
and (AnsiCompareText(TableName, TffSqlTableRef(Other).TableName) = 0)
|
|
and (AnsiCompareText(Alias, TffSqlTableRef(Other).Alias) = 0)
|
|
and (BothNil(TableExp, TffSqlTableRef(Other).TableExp)
|
|
or (BothNonNil(TableExp, TffSqlTableRef(Other).TableExp)
|
|
and TableExp.Equals(TffSqlTableRef(Other).TableExp)
|
|
))
|
|
and (BothNil(ColumnList, TffSqlTableRef(Other).ColumnList)
|
|
or (BothNonNil(ColumnList, TffSqlTableRef(Other).ColumnList)
|
|
and ColumnList.Equals(TffSqlTableRef(Other).ColumnList)
|
|
));
|
|
end;
|
|
|
|
procedure TffSqlTableRef.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
var
|
|
T : TffSqlTableProxy;
|
|
begin
|
|
Assert(Owner <> nil);
|
|
T := GetTable(Self, False);
|
|
aCursorID := T.CursorID;
|
|
T.LeaveCursorOpen := True;
|
|
if T.Owner = Self then begin
|
|
T.Owner := nil;
|
|
T.Free;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlTableRef.GetResultTable: TFFSqlTableProxy;
|
|
begin
|
|
Result := GetTable(Self, False);
|
|
end;
|
|
|
|
function TffSqlTableRef.GetSQLName: string;
|
|
begin
|
|
if Alias <> '' then
|
|
Result := Alias
|
|
else
|
|
if TableName <> '' then
|
|
Result := TableName
|
|
else
|
|
Result := 'UNNAMED';
|
|
end;
|
|
|
|
function TffSqlTableRef.GetTable(AOwner: TObject;
|
|
const ExclContLock : Boolean): TffSqlTableProxy;
|
|
begin
|
|
if DatabaseName <> '' then
|
|
if not SameText(DatabaseName, Owner.FDatabase.Alias) then
|
|
SQLError(format('The referenced database name %s does not '+
|
|
'match the current database, %s.',
|
|
[DatabaseName, Owner.FDatabase.Alias]));
|
|
if TableName <> '' then begin
|
|
if FTable = nil then begin
|
|
FTable := Owner.FDatabase.TableByName(AOwner, TableName,
|
|
ExclContLock, Alias); {!!.11}
|
|
if FTable = nil then
|
|
SQLError('Unable to open table: ' + TableName +
|
|
'. Ensure the table exists and is not in use by ' +
|
|
'another process.');
|
|
FTable.SetIndex(-1);
|
|
end;
|
|
Result := FTable;
|
|
end else
|
|
Result := TableExp.ResultTable;
|
|
end;
|
|
|
|
{!!.11 new}
|
|
function TffSqlTableRef.Reduce: Boolean;
|
|
begin
|
|
if TableExp <> nil then
|
|
if TableExp.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function TffSqlTableRef.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
begin
|
|
if TableExp <> nil then
|
|
Result := TableExp.TargetFieldFromSourceField(F)
|
|
else
|
|
Result := nil; {!!.13}
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlSimpleExpressionList=======================================}
|
|
function TffSqlSimpleExpressionList.AddExpression(
|
|
Expression: TffSqlSimpleExpression): TffSqlSimpleExpression;
|
|
begin
|
|
FExpressionList.Add(Expression);
|
|
Result := Expression;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpressionList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Source is TffSqlSimpleExpressionList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlSimpleExpressionList(Source).ExpressionCount) do
|
|
AddExpression(TffSqlSimpleExpression.Create(Self)).Assign(
|
|
TffSqlSimpleExpressionList(Source).Expression[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlSimpleExpressionList.CheckIsConstant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
for i := 0 to pred(ExpressionCount) do
|
|
if not Expression[i].IsConstant then begin
|
|
FIsConstant := False;
|
|
exit;
|
|
end;
|
|
FIsConstant := True;
|
|
end;
|
|
|
|
function TffSqlSimpleExpressionList.Contains(const TestValue: Variant): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ExpressionCount) do
|
|
if Expression[i].GetValue = TestValue then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
constructor TffSqlSimpleExpressionList.Create(
|
|
AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FExpressionList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpressionList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ExpressionCount) do
|
|
Expression[i].Free;
|
|
FExpressionList.Clear;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpressionList.DependsOn(
|
|
Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ExpressionCount) do
|
|
if Expression[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlSimpleExpressionList.Destroy;
|
|
begin
|
|
Clear;
|
|
FExpressionList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpressionList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Expression[0].EmitSQL(Stream);
|
|
for i := 1 to pred(ExpressionCount) do begin
|
|
WriteStr(Stream,', ');
|
|
Expression[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpressionList.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(ExpressionCount) do
|
|
Expression[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpressionList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlSimpleExpressionList then begin
|
|
if ExpressionCount <> TffSqlSimpleExpressionList(Other).ExpressionCount then
|
|
exit;
|
|
for i := 0 to pred(ExpressionCount) do
|
|
if not Expression[i].Equals(TffSqlSimpleExpressionList(Other).Expression[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpressionList.GetExpression(
|
|
Index: Integer): TffSqlSimpleExpression;
|
|
begin
|
|
Result := TffSqlSimpleExpression(FExpressionList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpressionList.GetExpressionCount: Integer;
|
|
begin
|
|
Result := FExpressionList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpressionList.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
procedure TffSqlSimpleExpressionList.MatchType(ExpectedType: TffFieldType);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ExpressionCount) do
|
|
Expression[i].MatchType(ExpectedType);
|
|
end;
|
|
{--------}
|
|
function TffSqlSimpleExpressionList.Reduce: Boolean;
|
|
var
|
|
I : integer;
|
|
begin
|
|
for i := 0 to pred(ExpressionCount) do
|
|
if Expression[i].Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlSimpleExpressionList.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
procedure TffSqlSimpleExpressionList.SetExpression(Index: Integer;
|
|
const Value: TffSqlSimpleExpression);
|
|
begin
|
|
FExpressionList[Index] := Value;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlOrderColumn================================================}
|
|
procedure TffSqlOrderColumn.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlOrderColumn then begin
|
|
TableName := TffSqlOrderColumn(Source).TableName;
|
|
FieldName := TffSqlOrderColumn(Source).FieldName;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderColumn.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ' ');
|
|
if TableName <> '' then begin
|
|
WriteStr(Stream, TableName);
|
|
WriteStr(Stream, '.');
|
|
end;
|
|
WriteStr(Stream, FieldName);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderColumn.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderColumn.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result := Other is TffSqlOrderColumn
|
|
and (AnsiCompareText(TableName, TffSqlOrderColumn(Other).TableName) = 0)
|
|
and (AnsiCompareText(FieldName, TffSqlOrderColumn(Other).FieldName) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderColumn.QualColumnName : string;
|
|
begin
|
|
if TableName <> '' then
|
|
Result := TableName + '.' + FieldName
|
|
else
|
|
Result := FieldName;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlGroupColumn================================================}
|
|
procedure TffSqlGroupColumn.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlGroupColumn then begin
|
|
TableName := TffSqlGroupColumn(Source).TableName;
|
|
FieldName := TffSqlGroupColumn(Source).FieldName;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlGroupColumn.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ' ');
|
|
if TableName <> '' then begin
|
|
WriteStr(Stream, TableName);
|
|
WriteStr(Stream, '.');
|
|
end;
|
|
WriteStr(Stream, FieldName);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlGroupColumn.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumn.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result := Other is TffSqlGroupColumn
|
|
and (AnsiCompareText(TableName, TffSqlGroupColumn(Other).TableName) = 0)
|
|
and (AnsiCompareText(FieldName, TffSqlGroupColumn(Other).FieldName) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlGroupColumn.QualColumnName: string;
|
|
var
|
|
F : TffSqlFieldProxy;
|
|
Name : string;
|
|
begin
|
|
if OwnerSelect = nil then
|
|
SQLError('Field references may not occur in this context');
|
|
if TableName <> '' then begin
|
|
Name := OwnerSelect.TableRefList.GetNameForAlias(FTableName);
|
|
if Name <> '' then
|
|
Result := Name + '.' + FFieldName
|
|
else
|
|
Result := TableName + '.' + FFieldName;
|
|
end
|
|
else begin
|
|
{ If this is an alias for a field in the selection list then return
|
|
the name. }
|
|
if OwnerSelect.Columns.IndexOf(FieldName) > -1 then
|
|
Result := FieldName
|
|
else begin
|
|
{ Find the proxy for this field. }
|
|
F := OwnerSelect.FindField(FFieldName);
|
|
if F = nil then
|
|
Result := FFieldName
|
|
else
|
|
Result := F.OwnerTable.Name + '.' + FFieldName;
|
|
end;
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlOrderItem==================================================}
|
|
procedure TffSqlOrderItem.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlOrderItem then begin
|
|
if TffSqlOrderItem(Source).Column <> nil then begin
|
|
if Column = nil then
|
|
Column := TffSqlOrderColumn.Create(Self);
|
|
Column.Assign(TffSqlOrderItem(Source).Column);
|
|
end;
|
|
Index := TffSqlOrderItem(Source).Index;
|
|
Descending := TffSqlOrderItem(Source).Descending;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
constructor TffSqlOrderItem.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
end;
|
|
|
|
destructor TffSqlOrderItem.Destroy;
|
|
begin
|
|
Column.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlOrderItem.EmitSQL(Stream: TStream);
|
|
begin
|
|
if Column <> nil then
|
|
Column.EmitSQL(Stream)
|
|
else begin
|
|
WriteStr(Stream, ' ');
|
|
WriteStr(Stream, Index);
|
|
end;
|
|
if Descending then
|
|
WriteStr(Stream,' DESC')
|
|
else
|
|
Writestr(Stream,' ASC');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderItem.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if Column <> nil then
|
|
Column.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderItem.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlOrderItem)
|
|
and (Descending = TffSqlOrderItem(Other).Descending)
|
|
and (Index = TffSqlOrderItem(Other).Index)
|
|
and (BothNil(Column, TffSqlOrderItem(Other).Column)
|
|
or (BothNonNil(Column, TffSqlOrderItem(Other).Column)
|
|
and Column.Equals(TffSqlOrderItem(Other).Column)
|
|
));
|
|
end;
|
|
{--------}
|
|
{====================================================================}
|
|
|
|
{===TffSqlOrderList==================================================}
|
|
function TffSqlOrderList.AddOrderItem(NewOrder: TffSqlOrderItem): TffSqlOrderItem;
|
|
begin
|
|
FOrderItemList.Add(NewOrder);
|
|
Result := NewOrder;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Source is TffSqlOrderList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlOrderList(Source).OrderCount) do
|
|
AddOrderItem(TffSqlOrderItem.Create(Self)).Assign(
|
|
TffSqlOrderList(Source).OrderItem[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
constructor TffSqlOrderList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FOrderItemList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FOrderItemList.Count) do
|
|
OrderItem[i].Free;
|
|
FOrderItemList.Clear;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlOrderList.Destroy;
|
|
begin
|
|
Clear;
|
|
FOrderItemList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
WriteStr(Stream,' ORDER BY');
|
|
OrderItem[0].EmitSQL(Stream);
|
|
for i := 1 to pred(OrderCount) do begin
|
|
WriteStr(Stream,', ');
|
|
OrderItem[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlOrderList.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(OrderCount) do
|
|
OrderItem[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlOrderList then begin
|
|
if OrderCount <> TffSqlOrderList(Other).OrderCount then
|
|
exit;
|
|
for i := 0 to pred(OrderCount) do
|
|
if not OrderItem[i].Equals(TffSqlOrderList(Other).OrderItem[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderList.GetOrderCount: Integer;
|
|
begin
|
|
Result := FOrderItemList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderList.GetOrderItem(
|
|
Index: Integer): TffSqlOrderItem;
|
|
begin
|
|
Result := TffSqlOrderItem(FOrderItemList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlOrderList.Reduce: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlOrderList.SetOrderItem(Index: Integer;
|
|
const Value: TffSqlOrderItem);
|
|
begin
|
|
FOrderItemList[Index] := Value;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlAllOrAnyClause=============================================}
|
|
procedure TffSqlAllOrAnyClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlAllOrAnyClause then begin
|
|
All := TffSqlAllOrAnyClause(Source).All;
|
|
SubQuery.Free;
|
|
SubQuery := TffSqlSELECT.Create(Self);
|
|
SubQuery.Assign(TffSqlAllOrAnyClause(Source).SubQuery);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlAllOrAnyClause.Compare(RelOp: TffSqlRelOp;
|
|
const Val: Variant): Boolean;
|
|
begin
|
|
if All then
|
|
Result := SubQuery.CheckAllValues(RelOp, Val)
|
|
else
|
|
Result := SubQuery.CheckAnyValue(RelOp, Val);
|
|
end;
|
|
|
|
function TffSqlAllOrAnyClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SubQuery.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlAllOrAnyClause.Destroy;
|
|
begin
|
|
SubQuery.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAllOrAnyClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
if All then
|
|
WriteStr(Stream,' ALL ')
|
|
else
|
|
WriteStr(Stream,' ANY ');
|
|
WriteStr(Stream,'(');
|
|
SubQuery.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAllOrAnyClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SubQuery.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlAllOrAnyClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlAllOrAnyClause)
|
|
and (All = TffSqlAllOrAnyClause(Other).All)
|
|
and (SubQuery.Equals(TffSqlAllOrAnyClause(Other).SubQuery));
|
|
end;
|
|
{--------}
|
|
procedure TffSqlAllOrAnyClause.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
SubQuery.MatchType(ExpectedType, True);
|
|
end;
|
|
|
|
function TffSqlAllOrAnyClause.Reduce: Boolean;
|
|
begin
|
|
Result := SubQuery.Reduce;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlExistsClause===============================================}
|
|
function TffSqlExistsClause.AsBoolean: Boolean;
|
|
begin
|
|
Result := SubQuery.CheckNonEmpty;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlExistsClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlExistsClause then begin
|
|
SubQuery.Free;
|
|
SubQuery := TffSqlSELECT.Create(Self);
|
|
SubQuery.Assign(TffSqlExistsClause(Source).SubQuery);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlExistsClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SubQuery.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlExistsClause.Destroy;
|
|
begin
|
|
SubQuery.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlExistsClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' EXISTS (');
|
|
SubQuery.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlExistsClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SubQuery.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlExistsClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlExistsClause)
|
|
and (SubQuery.Equals(TffSqlExistsClause(Other).SubQuery));
|
|
end;
|
|
{--------}
|
|
function TffSqlExistsClause.Reduce: Boolean;
|
|
begin
|
|
Result := SubQuery.Reduce;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlUniqueClause===============================================}
|
|
function TffSqlUniqueClause.AsBoolean: Boolean;
|
|
begin
|
|
Result := SubQuery.CheckNoDups;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUniqueClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlUniqueClause then begin
|
|
SubQuery.Free;
|
|
SubQuery := TffSqlTableExp.Create(Self);
|
|
SubQuery.Assign(TffSqlUniqueClause(Source).SubQuery);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlUniqueClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SubQuery.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlUniqueClause.Destroy;
|
|
begin
|
|
SubQuery.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUniqueClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' UNIQUE (');
|
|
SubQuery.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUniqueClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SubQuery.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlUniqueClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlUniqueClause)
|
|
and (SubQuery.Equals(TffSqlUniqueClause(Other).SubQuery));
|
|
end;
|
|
{--------}
|
|
function TffSqlUniqueClause.Reduce: Boolean;
|
|
begin
|
|
Result := SubQuery.Reduce;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
function OffsetTime(const DateTime: TDateTime; DeltaH, DeltaM, DeltaS: Integer): TDateTime;
|
|
var
|
|
Mi, H, S, MSec : Word;
|
|
Hs, Mis, Ss : Integer;
|
|
DeltaD : Integer;
|
|
begin
|
|
DecodeTime(DateTime, H, Mi, S, MSec);
|
|
Hs := H;
|
|
Mis := Mi;
|
|
Ss := S;
|
|
Ss := Ss + (DeltaS mod 60);
|
|
Mis := Mis + (DeltaS div 60);
|
|
if Ss < 0 then begin
|
|
dec(Mis);
|
|
inc(Ss, 60);
|
|
end else
|
|
if Ss >= 60 then begin
|
|
inc(Mis);
|
|
dec(Ss, 60);
|
|
end;
|
|
Mis := Mis + (DeltaM mod 60);
|
|
Hs := Hs + (DeltaM div 60);
|
|
if Mis < 0 then begin
|
|
dec(Hs);
|
|
inc(Mis, 60);
|
|
end else
|
|
if Mis >= 60 then begin
|
|
inc(Hs);
|
|
dec(Mis, 60);
|
|
end;
|
|
Hs := Hs + (DeltaH mod 24);
|
|
DeltaD := (DeltaH div 24);
|
|
if Hs < 0 then begin
|
|
dec(DeltaD);
|
|
inc(Hs, 24);
|
|
end else
|
|
if Hs >= 24 then begin
|
|
inc(DeltaD);
|
|
dec(Hs, 24);
|
|
end;
|
|
Result := Round(DateTime) + EncodeTime(Hs, Mis, Ss, MSec) + DeltaD;
|
|
end;
|
|
|
|
{===TffSqlIntervalLiteral============================================}
|
|
function TffSqlIntervalLiteral.AddIntervalTo(Target: TDateTime): TDateTime;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
case StartDef of
|
|
iYear :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := IncMonth(Target, Y1 * 12);
|
|
else //iMonth :
|
|
Result := IncMonth(Target, Y1 * 12 + M1);
|
|
end;
|
|
iMonth :
|
|
Result := IncMonth(Target, M1);
|
|
iDay :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := Target + D1;
|
|
iHour :
|
|
Result := OffsetTime(Target, H1, 0, 0) + D1;
|
|
iMinute :
|
|
Result := OffsetTime(Target, H1, M1, 0) + D1;
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, H1, M1, S1) + D1;
|
|
end;
|
|
iHour :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := OffsetTime(Target, H1, 0, 0);
|
|
iMinute :
|
|
Result := OffsetTime(Target, H1, M1, 0);
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, H1, M1, S1);
|
|
end;
|
|
iMinute :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := OffsetTime(Target, 0, M1, 0);
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, 0, M1, S1);
|
|
end;
|
|
else //iSecond :
|
|
Result := OffsetTime(Target, 0, 0, S1);
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlIntervalLiteral.SubtractIntervalFrom(Target: TDateTime): TDateTime;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
case StartDef of
|
|
iYear :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := IncMonth(Target, -Y1 * 12);
|
|
else//iMonth :
|
|
Result := IncMonth(Target, -(Y1 * 12 + M1));
|
|
end;
|
|
iMonth :
|
|
Result := IncMonth(Target, -M1);
|
|
iDay :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := Target - D1;
|
|
iHour :
|
|
Result := OffsetTime(Target, -H1, 0, 0) - D1;
|
|
iMinute :
|
|
Result := OffsetTime(Target, -H1, -M1, 0) - D1;
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, -H1, -M1, -S1) - D1;
|
|
end;
|
|
iHour :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := OffsetTime(Target, -H1, 0, 0);
|
|
iMinute :
|
|
Result := OffsetTime(Target, -H1, -M1, 0);
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, -H1, -M1, -S1);
|
|
end;
|
|
iMinute :
|
|
case EndDef of
|
|
iUnspec :
|
|
Result := OffsetTime(Target, 0, -M1, 0);
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, 0, -M1, -S1);
|
|
end;
|
|
else//iSecond :
|
|
Result := OffsetTime(Target, 0, 0, -S1);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIntervalLiteral.ConvertToNative;
|
|
var
|
|
S : string;
|
|
P : Integer;
|
|
begin
|
|
S := Value;
|
|
case StartDef of
|
|
iUnspec :
|
|
SQLError('Internal error in date/time interval literal');
|
|
iYear :
|
|
case EndDef of
|
|
iUnspec :
|
|
Y1 := StrToInt(copy(S, 2, length(S) - 2));
|
|
iYear :
|
|
SQLError('Syntax error in year-month interval literal');
|
|
iMonth :
|
|
begin
|
|
P := PosCh('-', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in year-month interval literal: "-" expected');
|
|
Y1 := StrToInt(copy(S, 2, P - 2));
|
|
M1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
else
|
|
SQLError('Syntax error in year-month interval literal');
|
|
end;
|
|
iMonth :
|
|
case EndDef of
|
|
iUnspec :
|
|
M1 := StrToInt(copy(S, 2, length(S) - 2));
|
|
else
|
|
SQLError('Syntax error in year-month interval literal');
|
|
end;
|
|
iDay :
|
|
case EndDef of
|
|
iUnspec :
|
|
D1 := StrToInt(copy(S, 2, length(S) - 2));
|
|
iHour :
|
|
begin
|
|
P := PosCh(' ', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: " " expected');
|
|
D1 := StrToInt(copy(S, 2, P - 2));
|
|
H1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
iMinute :
|
|
begin
|
|
P := PosCh(' ', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: " " expected');
|
|
D1 := StrToInt(copy(S, 2, P - 2));
|
|
Delete(S, 2, P - 2);
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
H1 := StrToInt(copy(S, 2, P - 2));
|
|
M1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
iSecond :
|
|
begin
|
|
P := PosCh(' ', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: " " expected');
|
|
D1 := StrToInt(copy(S, 2, P - 2));
|
|
Delete(S, 2, P - 1);
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
H1 := StrToInt(copy(S, 2, P - 2));
|
|
Delete(S, 2, P - 1);
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
M1 := StrToInt(copy(S, 2, P - 2));
|
|
S1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
else
|
|
SQLError('Syntax error in date-time interval literal');
|
|
end;
|
|
iHour :
|
|
case EndDef of
|
|
iUnspec :
|
|
H1 := StrToInt(copy(S, 2, length(S) - 2));
|
|
iMinute :
|
|
begin
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
H1 := StrToInt(copy(S, 2, P - 2));
|
|
M1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
iSecond :
|
|
begin
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
H1 := StrToInt(copy(S, 2, P - 2));
|
|
Delete(S, 2, P - 1);
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
M1 := StrToInt(copy(S, 2, P - 2));
|
|
S1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
else
|
|
SQLError('Syntax error in date-time interval literal');
|
|
end;
|
|
iMinute :
|
|
case EndDef of
|
|
iUnspec :
|
|
M1 := StrToInt(copy(S, 2, length(S) - 2));
|
|
iSecond :
|
|
begin;
|
|
P := PosCh(':', S);
|
|
if P = 0 then
|
|
SQLError('Syntax error in date-time interval literal: ":" expected');
|
|
M1 := StrToInt(copy(S, 2, P - 2));
|
|
S1 := StrToInt(copy(S, P + 1, length(S) - P - 1));
|
|
end;
|
|
else
|
|
SQLError('Syntax error in date-time interval literal');
|
|
end;
|
|
iSecond :
|
|
case EndDef of
|
|
iUnspec :
|
|
S1 := StrToInt(copy(S, 2, length(S) - 2));
|
|
else
|
|
SQLError('Syntax error in date-time interval literal');
|
|
end;
|
|
else
|
|
SQLError('Syntax error in date-time interval literal');
|
|
end;
|
|
Converted := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIntervalLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlIntervalLiteral then begin
|
|
Value := TffSqlIntervalLiteral(Source).Value;
|
|
StartDef := TffSqlIntervalLiteral(Source).StartDef;
|
|
EndDef := TffSqlIntervalLiteral(Source).EndDef;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlIntervalLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' INTERVAL ');
|
|
WriteStr(Stream, Value);
|
|
WriteStr(Stream,' ');
|
|
WriteStr(Stream, DefStr[StartDef]);
|
|
if EndDef <> iUnspec then begin
|
|
WriteStr(Stream,' TO ');
|
|
WriteStr(Stream, DefStr[EndDef]);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIntervalLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlIntervalLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlIntervalLiteral)
|
|
and (AnsiCompareText(Value, TffSqlIntervalLiteral(Other).Value) = 0)
|
|
and (StartDef = TffSqlIntervalLiteral(Other).StartDef)
|
|
and (EndDef = TffSqlIntervalLiteral(Other).EndDef);
|
|
end;
|
|
{--------}
|
|
function TffSqlIntervalLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftInterval;
|
|
end;
|
|
{--------}
|
|
function TffSqlIntervalLiteral.GetValue: Variant;
|
|
begin
|
|
Result := '';
|
|
{This value returned to allow tests for NULL to pass}
|
|
end;
|
|
{--------}
|
|
procedure TffSqlIntervalLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftStDate,
|
|
fftDateTime :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
if not Converted then
|
|
ConvertToNative;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlTimestampLiteral===========================================}
|
|
procedure TffSqlTimestampLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlTimeStampLiteral then begin
|
|
Value := TffSqlTimeStampLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlTimeStampLiteral.ConvertToNative;
|
|
begin
|
|
if (length(Value) < 21)
|
|
or not (Value[6] in ['-', '.', '/'])
|
|
or (Value[9] <> Value[6])
|
|
or (Value[12] <> ' ')
|
|
or (Value[15] <> ':')
|
|
or (Value[18] <> ':') then
|
|
SQLError('Syntax error in time stamp literal');
|
|
DateTimeValue :=
|
|
EncodeDate(
|
|
StrToInt(copy(Value, 2, 4)),
|
|
StrToInt(copy(Value, 7, 2)),
|
|
StrToInt(copy(Value, 10, 2)))
|
|
+
|
|
EncodeTime(
|
|
StrToInt(copy(Value, 13, 2)),
|
|
StrToInt(copy(Value, 16, 2)),
|
|
StrToInt(copy(Value, 19, 2)),
|
|
0);
|
|
Converted := True;
|
|
end;
|
|
|
|
procedure TffSqlTimestampLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' TIMESTAMP ');
|
|
WriteStr(Stream, Value);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTimestampLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlTimestampLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlTimestampLiteral)
|
|
and (AnsiCompareText(Value, TffSqlTimestampLiteral(Other).Value) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlTimestampLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftDateTime;
|
|
end;
|
|
|
|
function TffSqlTimestampLiteral.GetValue: Variant;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
Result := DateTimeValue;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTimestampLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftStTime,
|
|
fftDateTime :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
if not Converted then
|
|
ConvertToNative;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlTimeLiteral================================================}
|
|
procedure TffSqlTimeLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlTimeLiteral then begin
|
|
Value := TffSqlTimeLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlTimeLiteral.ConvertToNative;
|
|
begin
|
|
if (length(Value) <> 10)
|
|
or (Value[4] <> ':')
|
|
or (Value[7] <> ':') then
|
|
SQLError('Syntax error in time literal');
|
|
TimeValue := EncodeTime(
|
|
StrToInt(copy(Value, 2, 2)),
|
|
StrToInt(copy(Value, 5, 2)),
|
|
StrToInt(copy(Value, 8, 2)),
|
|
0);
|
|
Converted := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTimeLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' TIME ');
|
|
WriteStr(Stream, Value);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTimeLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlTimeLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlTimeLiteral)
|
|
and (AnsiCompareText(Value, TffSqlTimeLiteral(Other).Value) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlTimeLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftStTime;
|
|
end;
|
|
|
|
function TffSqlTimeLiteral.GetValue: Variant;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
Result := TimeValue;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlTimeLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftStTime,
|
|
fftDateTime :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
if not Converted then
|
|
ConvertToNative;
|
|
end;
|
|
{====================================================================}
|
|
|
|
{===TffSqlDateLiteral================================================}
|
|
{--------}
|
|
procedure TffSqlDateLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlDateLiteral then begin
|
|
Value := TffSqlDateLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlDateLiteral.ConvertToNative;
|
|
begin
|
|
if (length(Value) <> 12)
|
|
or not (Value[6] in ['-', '.', '/'])
|
|
or (Value[9] <> Value[6]) then
|
|
SQLError('Syntax error in date literal');
|
|
DateValue := EncodeDate(
|
|
StrToInt(copy(Value, 2, 4)),
|
|
StrToInt(copy(Value, 7, 2)),
|
|
StrToInt(copy(Value, 10, 2)));
|
|
Converted := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlDateLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' DATE ');
|
|
WriteStr(Stream, Value);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlDateLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlDateLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlDateLiteral)
|
|
and (AnsiCompareText(Value, TffSqlDateLiteral(Other).Value) = 0);
|
|
end;
|
|
{--------}
|
|
function TffSqlDateLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftStDate;
|
|
end;
|
|
|
|
function TffSqlDateLiteral.GetValue: Variant;
|
|
begin
|
|
if not Converted then
|
|
ConvertToNative;
|
|
Result := DateValue;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlDateLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftStDate,
|
|
fftDateTime :
|
|
;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
if not Converted then
|
|
ConvertToNative;
|
|
end;
|
|
{===TffSqlBooleanLiteral================================================}
|
|
{--------}
|
|
procedure TffSqlBooleanLiteral.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlBooleanLiteral then begin
|
|
Value := TffSqlBooleanLiteral(Source).Value;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
{--------}
|
|
procedure TffSqlBooleanLiteral.EmitSQL(Stream: TStream);
|
|
begin
|
|
if Value then
|
|
WriteStr(Stream, ' TRUE')
|
|
else
|
|
WriteStr(Stream, ' FALSE');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlBooleanLiteral.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
{--------}
|
|
function TffSqlBooleanLiteral.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlBooleanLiteral)
|
|
and (Value = TffSqlBooleanLiteral(Other).Value);
|
|
end;
|
|
{--------}
|
|
function TffSqlBooleanLiteral.GetType: TffFieldType;
|
|
begin
|
|
Result := fftBoolean;
|
|
end;
|
|
|
|
function TffSqlBooleanLiteral.GetValue: Boolean;
|
|
begin
|
|
Result := Value;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlBooleanLiteral.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
fftBoolean : ;
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
{====================================================================}
|
|
|
|
const
|
|
FuncStr : array[TffSqlScalarFunction] of string = (
|
|
'CASE', 'CHARACTER_LENGTH','COALESCE', 'CURRENT_DATE','CURRENT_TIME','CURRENT_TIMESTAMP',
|
|
'CURRENT_USER','LOWER','UPPER','POSITION','SESSION_USER','SUBSTRING',
|
|
'SYSTEM_USER','TRIM','EXTRACT', 'NULLIF',
|
|
'ABS', 'CEIL', 'FLOOR', 'EXP', 'LOG', 'POWER', 'RAND', 'ROUND'); {!!.11}
|
|
LeadStr : array[TffSqlLTB] of string = ('BOTH', 'LEADING', 'TRAILING');
|
|
{===TffSqlScalarFunc=================================================}
|
|
procedure TffSqlScalarFunc.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
case SQLFunction of
|
|
sfCase :
|
|
FIsConstant := CaseExp.IsConstant;
|
|
sfCharlen :
|
|
FIsConstant := Arg1.IsConstant;
|
|
sfCoalesce :
|
|
FIsConstant := False;
|
|
sfCurrentDate :
|
|
FIsConstant := True;
|
|
sfCurrentTime :
|
|
FIsConstant := True;
|
|
sfCurrentTimestamp :
|
|
FIsConstant := True;
|
|
sfCurrentUser :
|
|
FIsConstant := True;
|
|
sfLower :
|
|
FIsConstant := Arg1.IsConstant;
|
|
sfUpper :
|
|
FIsConstant := Arg1.IsConstant;
|
|
sfPosition :
|
|
FIsConstant := Arg2.IsConstant and Arg1.IsConstant;
|
|
sfSessionUser :
|
|
FIsConstant := True;
|
|
sfSubstring :
|
|
FIsConstant :=
|
|
Arg1.IsConstant and Arg2.IsConstant and
|
|
((Arg3 = nil) or (Arg3.IsConstant));
|
|
sfSystemUser :
|
|
FIsConstant := True;
|
|
sfTrim :
|
|
FIsConstant :=
|
|
((Arg1 = nil) or (Arg1.IsConstant))
|
|
and ((Arg2 = nil) or (Arg2.IsConstant));
|
|
sfExtract :
|
|
FIsConstant := Arg1.IsConstant;
|
|
sfNullIf :
|
|
FIsConstant := Arg2.IsConstant and Arg1.IsConstant;
|
|
{!!.11 begin}
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfRound :
|
|
FIsConstant := Arg1.IsConstant;
|
|
sfRand :
|
|
FIsConstant := False;
|
|
sfPower :
|
|
FIsConstant := Arg2.IsConstant and Arg1.IsConstant;
|
|
{!!.11 end}
|
|
else
|
|
Assert(False);
|
|
end;
|
|
if FIsConstant then begin
|
|
FIsConstant := False;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlScalarFunc.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlScalarFunc then begin
|
|
Clear;
|
|
SQLFunction := TffSqlScalarFunc(Source).SQLFunction;
|
|
if assigned(TffSqlScalarFunc(Source).Arg1) then begin
|
|
Arg1 := TffSqlSimpleExpression.Create(Self);
|
|
Arg1.Assign(TffSqlScalarFunc(Source).Arg1);
|
|
end;
|
|
if assigned(TffSqlScalarFunc(Source).Arg2) then begin
|
|
Arg2 := TffSqlSimpleExpression.Create(Self);
|
|
Arg2.Assign(TffSqlScalarFunc(Source).Arg2);
|
|
end;
|
|
if assigned(TffSqlScalarFunc(Source).Arg3) then begin
|
|
Arg3 := TffSqlSimpleExpression.Create(Self);
|
|
Arg3.Assign(TffSqlScalarFunc(Source).Arg3);
|
|
end;
|
|
LTB := TffSqlScalarFunc(Source).LTB;
|
|
XDef := TffSqlScalarFunc(Source).XDef;
|
|
if assigned(TffSqlScalarFunc(Source).CaseExp) then begin
|
|
CaseExp := TffSqlCaseExpression.Create(Self);
|
|
CaseExp.Assign(TffSqlScalarFunc(Source).CaseExp);
|
|
end;
|
|
if assigned(TffSqlScalarFunc(Source).CoalesceExp) then begin
|
|
CoalesceExp := TffSqlCoalesceExpression.Create(Self);
|
|
CoalesceExp.Assign(TffSqlScalarFunc(Source).CoalesceExp);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlScalarFunc.Clear;
|
|
begin
|
|
CaseExp.Free;
|
|
CoalesceExp.Free;
|
|
Arg1.Free;
|
|
Arg2.Free;
|
|
Arg3.Free;
|
|
CaseExp:= nil;
|
|
CoalesceExp:= nil;
|
|
Arg1:= nil;
|
|
Arg2:= nil;
|
|
Arg3:= nil;
|
|
end;
|
|
|
|
function TffSqlScalarFunc.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
case SQLFunction of
|
|
sfCase :
|
|
Result := CaseExp.DependsOn(Table);
|
|
sfCharlen,
|
|
sfLower,
|
|
sfUpper,
|
|
sfExtract :
|
|
Result := Arg1.DependsOn(Table);
|
|
sfCoalesce :
|
|
Result := CoalesceExp.DependsOn(Table);
|
|
sfSystemUser,
|
|
sfCurrentDate,
|
|
sfCurrentTime,
|
|
sfCurrentTimestamp,
|
|
sfCurrentUser,
|
|
sfSessionUser :
|
|
Result := False;
|
|
sfPosition :
|
|
Result := Arg2.DependsOn(Table) or Arg1.DependsOn(Table);
|
|
sfSubstring :
|
|
begin
|
|
Result := Arg1.DependsOn(Table) or Arg2.DependsOn(Table);
|
|
if not Result and (Arg3 <> nil) then
|
|
Result := Arg3.DependsOn(Table);
|
|
end;
|
|
sfTrim :
|
|
begin
|
|
if Arg2 = nil then
|
|
Result := Arg1.DependsOn(Table)
|
|
else
|
|
Result := Arg1.DependsOn(Table) or Arg2.DependsOn(Table)
|
|
end;
|
|
sfNullIf :
|
|
begin
|
|
Result := Arg1.DependsOn(Table) or Arg2.DependsOn(Table);
|
|
end;
|
|
{!!.11 begin}
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfRound :
|
|
Result := Arg1.DependsOn(Table) ;
|
|
sfRand :
|
|
Result := False;
|
|
sfPower :
|
|
Result := Arg1.DependsOn(Table) or Arg2.DependsOn(Table);
|
|
{!!.11 end}
|
|
else
|
|
Assert(False);
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
destructor TffSqlScalarFunc.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlScalarFunc.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ' ');
|
|
case SQLFunction of
|
|
sfCase :
|
|
CaseExp.EmitSQL(Stream);
|
|
sfCoalesce :
|
|
CoalesceExp.EmitSQL(Stream);
|
|
sfCurrentDate,
|
|
sfCurrentTime,
|
|
sfCurrentTimestamp,
|
|
sfCurrentUser,
|
|
sfSessionUser,
|
|
sfSystemUser,
|
|
sfRand : {!!.11}
|
|
WriteStr(Stream, FuncStr[SQLFunction]);
|
|
else
|
|
WriteStr(Stream, FuncStr[SQLFunction]);
|
|
WriteStr(Stream,'(');
|
|
case SQLFunction of
|
|
sfCharlen,
|
|
sfLower,
|
|
sfUpper,
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfRound : {!!.11}
|
|
begin
|
|
Arg1.EmitSQL(Stream);
|
|
end;
|
|
sfNullIf,
|
|
sfPosition,
|
|
sfPower : {!!.11}
|
|
begin
|
|
Arg1.EmitSQL(Stream);
|
|
WriteStr(Stream,' , ');
|
|
Arg2.EmitSQL(Stream);
|
|
end;
|
|
sfSubstring :
|
|
begin
|
|
Arg1.EmitSQL(Stream);
|
|
WriteStr(Stream,' FROM ');
|
|
Arg2.EmitSQL(Stream);
|
|
if Arg3 <> nil then begin
|
|
WriteStr(Stream,' FOR ');
|
|
Arg3.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
sfTrim :
|
|
begin
|
|
WriteStr(Stream, LeadStr[LTB]);
|
|
WriteStr(Stream, ' ');
|
|
if Arg1 <> nil then
|
|
Arg1.EmitSQL(Stream);
|
|
if Arg2 <> nil then begin
|
|
WriteStr(Stream,' FROM ');
|
|
Arg2.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
sfExtract :
|
|
begin
|
|
WriteStr(Stream, DefStr[XDef]);
|
|
WriteStr(Stream,' FROM ');
|
|
Arg1.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
WriteStr(Stream,')');
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlScalarFunc.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
case SQLFunction of
|
|
sfCase :
|
|
CaseExp.EnumNodes(EnumMethod, Deep);
|
|
sfCoalesce :
|
|
CoalesceExp.EnumNodes(EnumMethod, Deep);
|
|
sfCurrentDate,
|
|
sfCurrentTime,
|
|
sfCurrentTimestamp,
|
|
sfCurrentUser,
|
|
sfSessionUser,
|
|
sfSystemUser,
|
|
sfRand : {!!.11}
|
|
;
|
|
else
|
|
case SQLFunction of
|
|
sfCharlen,
|
|
sfLower,
|
|
sfUpper,
|
|
sfExtract,
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfRound : {!!.11}
|
|
Arg1.EnumNodes(EnumMethod, Deep);
|
|
sfNullIf,
|
|
sfPosition,
|
|
sfPower : {!!.11}
|
|
begin
|
|
Arg1.EnumNodes(EnumMethod, Deep);
|
|
Arg2.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
sfSubstring :
|
|
begin
|
|
Arg1.EnumNodes(EnumMethod, Deep);
|
|
Arg2.EnumNodes(EnumMethod, Deep);
|
|
if Arg3 <> nil then
|
|
Arg3.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
sfTrim :
|
|
begin
|
|
if Arg1 <> nil then
|
|
Arg1.EnumNodes(EnumMethod, Deep);
|
|
if Arg2 <> nil then
|
|
Arg2.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlScalarFunc.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlScalarFunc then begin
|
|
if SQLFunction <> TffSqlScalarFunc(Other).SQLFunction then
|
|
exit;
|
|
case SQLFunction of
|
|
sfCase :
|
|
if not CaseExp.Equals(TffSqlScalarFunc(Other).CaseExp) then
|
|
exit;
|
|
sfCoalesce :
|
|
if not CoalesceExp.Equals(TffSqlScalarFunc(Other).CoalesceExp) then
|
|
exit;
|
|
sfCharlen,
|
|
sfLower,
|
|
sfUpper,
|
|
sfExtract,
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfRound : {!!.11}
|
|
if not Arg1.Equals(TffSqlScalarFunc(Other).Arg1) then
|
|
exit;
|
|
sfNullIf,
|
|
sfPosition,
|
|
sfPower : {!!.11}
|
|
begin
|
|
if not Arg1.Equals(TffSqlScalarFunc(Other).Arg1) then
|
|
exit;
|
|
if not Arg2.Equals(TffSqlScalarFunc(Other).Arg2) then
|
|
exit;
|
|
end;
|
|
sfSubstring :
|
|
begin
|
|
if not Arg1.Equals(TffSqlScalarFunc(Other).Arg1) then
|
|
exit;
|
|
if not Arg2.Equals(TffSqlScalarFunc(Other).Arg2) then
|
|
exit;
|
|
if not (
|
|
BothNil(Arg3, TffSqlScalarFunc(Other).Arg3)
|
|
or (BothNonNil(Arg3, TffSqlScalarFunc(Other).Arg3)
|
|
and Arg3.Equals(TffSqlScalarFunc(Other).Arg3))) then
|
|
exit;
|
|
end;
|
|
sfTrim :
|
|
begin
|
|
if not (
|
|
BothNil(Arg1, TffSqlScalarFunc(Other).Arg1)
|
|
or (BothNonNil(Arg1, TffSqlScalarFunc(Other).Arg1)
|
|
and Arg1.Equals(TffSqlScalarFunc(Other).Arg1))) then
|
|
exit;
|
|
if not (
|
|
BothNil(Arg2, TffSqlScalarFunc(Other).Arg2)
|
|
or (BothNonNil(Arg2, TffSqlScalarFunc(Other).Arg2)
|
|
and Arg2.Equals(TffSqlScalarFunc(Other).Arg2))) then
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlScalarFunc.GetDecimals: Integer;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
{--------}
|
|
function TffSqlScalarFunc.GetSize: Integer;
|
|
var
|
|
S : string;
|
|
begin
|
|
{should only be called on text functions}
|
|
case SQLFunction of
|
|
sfCase :
|
|
Result := CaseExp.GetSize;
|
|
sfLower,
|
|
sfUpper,
|
|
sfSubstring :
|
|
Result := Arg1.GetSize;
|
|
sfTrim :
|
|
if Arg2 = nil then
|
|
Result := Arg1.GetSize
|
|
else
|
|
Result := Arg2.GetSize;
|
|
sfCoalesce :
|
|
Result := CoalesceExp.GetSize;
|
|
sfCurrentUser,
|
|
sfSystemUser,
|
|
sfSessionUser :
|
|
begin
|
|
S := GetValue;
|
|
Result := length(S);
|
|
end;
|
|
sfNullIf :
|
|
Result := Arg1.GetSize;
|
|
else
|
|
Result := 0;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlScalarFunc.GetTitle(const Qualified : Boolean): string; {!!.11}
|
|
begin
|
|
Result := FuncStr[SQLFunction];
|
|
end;
|
|
{--------}
|
|
procedure TffSqlScalarFunc.CheckType;
|
|
begin
|
|
case SQLFunction of
|
|
sfCase :
|
|
FType := CaseExp.GetType;
|
|
sfCharlen :
|
|
begin
|
|
Arg1.MatchType(fftShortString);
|
|
FType := fftInt32;
|
|
end;
|
|
sfCoalesce :
|
|
FType := CoalesceExp.GetType;
|
|
sfCurrentDate :
|
|
FType := fftStDate;
|
|
sfCurrentTime :
|
|
FType := fftStTime;
|
|
sfCurrentTimestamp :
|
|
FType := fftDateTime;
|
|
sfCurrentUser :
|
|
FType := fftShortAnsiStr;
|
|
sfLower :
|
|
begin
|
|
Arg1.MatchType(fftShortString);
|
|
FType := fftShortAnsiStr;
|
|
end;
|
|
sfUpper :
|
|
begin
|
|
Arg1.MatchType(fftShortString);
|
|
FType := fftShortAnsiStr;
|
|
end;
|
|
sfPosition :
|
|
begin
|
|
Arg1.MatchType(fftShortString);
|
|
Arg2.MatchType(fftShortString);
|
|
FType := fftInt32;
|
|
end;
|
|
sfSessionUser :
|
|
FType := fftShortAnsiStr;
|
|
sfSubstring :
|
|
begin
|
|
Arg1.MatchType(fftShortString);
|
|
Arg2.MatchType(fftInt32);
|
|
if Arg3 <> nil then
|
|
Arg3.MatchType(fftInt32);
|
|
FType := fftShortAnsiStr;
|
|
end;
|
|
sfSystemUser :
|
|
FType := fftShortAnsiStr;
|
|
sfTrim :
|
|
begin
|
|
if Arg1 <> nil then
|
|
Arg1.MatchType(fftShortString);
|
|
if Arg2 <> nil then
|
|
Arg2.MatchType(fftShortString);
|
|
FType := fftShortAnsiStr;
|
|
end;
|
|
sfExtract :
|
|
begin
|
|
Arg1.MatchType(fftDateTime);
|
|
FType := fftInt32;
|
|
end;
|
|
sfNullIf :
|
|
FType := Arg1.GetType;
|
|
{!!.11 begin}
|
|
sfAbs, {sfCeil, sfFloor, }sfExp, sfLog, sfRound, sfRand, sfPower : {!!.12}
|
|
FType := fftDouble;
|
|
{!!.11 end}
|
|
sfCeil, sfFloor: {!!.12}
|
|
case Arg1.GetType of {!!.12}
|
|
fftStDate..fftDateTime : {!!.12}
|
|
FType := Arg1.GetType; {!!.12}
|
|
else {!!.12}
|
|
FType := fftDouble; {!!.12}
|
|
end; {!!.12}
|
|
else
|
|
Assert(False);
|
|
end;
|
|
TypeKnown := True;
|
|
end;
|
|
{--------}
|
|
function TffSqlScalarFunc.GetType: TffFieldType;
|
|
begin
|
|
if not TypeKnown then
|
|
CheckType;
|
|
Result := FType;
|
|
end;
|
|
{Begin !!.13}
|
|
{--------}
|
|
function ConvertBLOBToString(const Value : Variant) : string;
|
|
{ Converts a BLOB value to a string value.
|
|
Assumption: Value is always a var array of byte }
|
|
var
|
|
ResultLen : Longint;
|
|
VPtr : PAnsiChar;
|
|
begin
|
|
ResultLen := VarArrayHighBound(Value, 1);
|
|
SetLength(Result, ResultLen);
|
|
VPtr := VarArrayLock(Value);
|
|
try
|
|
Move(VPtr^, Result[1], ResultLen);
|
|
finally
|
|
VarArrayUnlock(Value);
|
|
end;
|
|
end;
|
|
{End !!.13}
|
|
{--------}
|
|
function TffSqlScalarFunc.GetValue: Variant;
|
|
{Revised !!.13 - Scalar functions updated to recognize BLOB fields as
|
|
arrays of bytes instead of as strings. }
|
|
var
|
|
S : string;
|
|
WS, WS2 : widestring; {!!.11}
|
|
I1, I2 : Integer;
|
|
Y, M, D : Word;
|
|
Hour, Min, Sec, MSec : Word;
|
|
Ch : Char;
|
|
DT : TDateTime;
|
|
V, V2 : Variant; {!!.11}
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
case SQLFunction of
|
|
sfCase :
|
|
Result := CaseExp.GetValue;
|
|
sfCharlen :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else if (VarType(V) and VarTypeMask = varByte) then
|
|
Result := VarArrayHighBound(V, 1)
|
|
else
|
|
Result := length(V);
|
|
end;
|
|
sfCoalesce :
|
|
Result := CoalesceExp.GetValue;
|
|
sfCurrentDate :
|
|
Result := Owner.StartDate;
|
|
sfCurrentTime :
|
|
Result := Owner.StartTime;
|
|
sfCurrentTimestamp :
|
|
Result := Owner.StartDateTime;
|
|
sfCurrentUser :
|
|
Result := IntToStr(Owner.FClientID);
|
|
sfLower :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else if (VarType(V) and VarTypeMask = varByte) then
|
|
Result := AnsiLowerCase(ConvertBLOBToString(V))
|
|
else
|
|
Result := AnsiLowerCase(V);
|
|
end;
|
|
sfUpper :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else if (VarType(V) and VarTypeMask = varByte) then
|
|
Result := AnsiUpperCase(ConvertBLOBToString(V))
|
|
else
|
|
Result := AnsiUpperCase(V);
|
|
end;
|
|
sfPosition :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
V2 := Arg2.GetValue;
|
|
if VarIsNull(V) or VarIsNull(V2) then
|
|
Result := 0
|
|
else begin
|
|
WS := V;
|
|
if WS = '' then
|
|
Result := 1
|
|
else begin
|
|
if (VarType(V2) and VarTypeMask = varByte) then
|
|
WS2 := ConvertBLOBToString(V2)
|
|
else
|
|
WS2 := V2;
|
|
Result := Pos(WS, WS2);
|
|
end; { if }
|
|
end; { if }
|
|
end;
|
|
sfSessionUser :
|
|
Result := IntToStr(Owner.FSessionID);
|
|
sfSubstring :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else begin
|
|
if (VarType(V) and VarTypeMask = varByte) then
|
|
S := ConvertBLOBToString(V)
|
|
else
|
|
S := V;
|
|
I1 := Arg2.GetValue;
|
|
if Arg3 = nil then
|
|
Result := copy(S, I1, length(S))
|
|
else begin
|
|
I2 := Arg3.GetValue;
|
|
Result := copy(S, I1, I2);
|
|
end;
|
|
end;
|
|
end;
|
|
sfSystemUser :
|
|
SQLError('SYSTEM_USER is not supported at this time');
|
|
sfTrim :
|
|
begin
|
|
if Arg2 = nil then begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then begin
|
|
Result := V;
|
|
Exit;
|
|
end;
|
|
if (VarType(V) and VarTypeMask = varByte) then
|
|
S := ConvertBLOBToString(V)
|
|
else
|
|
S := V;
|
|
Ch := ' ';
|
|
end else
|
|
if Arg1 = nil then begin
|
|
V := Arg2.GetValue;
|
|
if VarIsNull(V) then begin
|
|
Result := V;
|
|
Exit;
|
|
end;
|
|
if (VarType(V) and VarTypeMask = varByte) then
|
|
S := ConvertBLOBToString(V)
|
|
else
|
|
S := V;
|
|
Ch := ' ';
|
|
end else begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then begin
|
|
Result := V;
|
|
Exit;
|
|
end;
|
|
if (VarType(V) and VarTypeMask = varByte) then
|
|
S := ConvertBLOBToString(V)
|
|
else
|
|
S := V;
|
|
Ch := S[1];
|
|
V := Arg2.GetValue;
|
|
if VarIsNull(V) then
|
|
S := ''
|
|
else if (VarType(V) and VarTypeMask = varByte) then
|
|
S := ConvertBLOBToString(V)
|
|
else
|
|
S := V;
|
|
end;
|
|
case LTB of
|
|
ltbBoth :
|
|
begin
|
|
while (length(S) > 0) and (S[1] = Ch) do
|
|
Delete(S, 1, 1);
|
|
while (length(S) > 0) and (S[length(S)] = Ch) do
|
|
Delete(S, length(S), 1);
|
|
end;
|
|
ltbLeading :
|
|
while (length(S) > 0) and (S[1] = Ch) do
|
|
Delete(S, 1, 1);
|
|
ltbTrailing :
|
|
while (length(S) > 0) and (S[length(S)] = Ch) do
|
|
Delete(S, length(S), 1);
|
|
end;
|
|
Result := S;
|
|
end;
|
|
sfExtract :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then begin
|
|
Result := V;
|
|
exit;
|
|
end;
|
|
DT := V;
|
|
case XDef of
|
|
iYear :
|
|
begin
|
|
DecodeDate(DT, Y, M, D);
|
|
Result := Y;
|
|
end;
|
|
iMonth :
|
|
begin
|
|
DecodeDate(DT, Y, M, D);
|
|
Result := M;
|
|
end;
|
|
iDay :
|
|
begin
|
|
DecodeDate(DT, Y, M, D);
|
|
Result := D;
|
|
end;
|
|
iHour :
|
|
begin
|
|
DecodeTime(DT, Hour, Min, Sec, MSec);
|
|
Result := Hour;
|
|
end;
|
|
iMinute:
|
|
begin
|
|
DecodeTime(DT, Hour, Min, Sec, MSec);
|
|
Result := Min;
|
|
end;
|
|
else
|
|
//iSecond:
|
|
begin
|
|
DecodeTime(DT, Hour, Min, Sec, MSec);
|
|
Result := Sec;
|
|
end;
|
|
end;
|
|
end;
|
|
sfNullIf :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if V = Arg2.GetValue then
|
|
Result := Null
|
|
else
|
|
Result := V;
|
|
end;
|
|
sfAbs :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else
|
|
Result := abs(V);
|
|
end;
|
|
sfCeil :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else
|
|
Result := Ceil(V);
|
|
end;
|
|
sfFloor :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else
|
|
Result := Floor(V);
|
|
end;
|
|
sfExp :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else
|
|
Result := Exp(V);
|
|
end;
|
|
sfLog :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else
|
|
Result := Ln(V);
|
|
end;
|
|
sfRound :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else
|
|
Result := 1.0 * Round(V);
|
|
end;
|
|
sfRand :
|
|
Result := Random;
|
|
sfPower :
|
|
begin
|
|
V := Arg1.GetValue;
|
|
if VarIsNull(V) then
|
|
Result := V
|
|
else begin
|
|
V2 := Arg2.GetValue;
|
|
if VarIsNull(V2) then
|
|
Result := V2
|
|
else
|
|
Result := Power(V, V2);
|
|
end;
|
|
end;
|
|
else
|
|
Assert(False);
|
|
end;
|
|
end;
|
|
|
|
function TffSqlScalarFunc.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
function TffSqlScalarFunc.IsFieldFrom(Table: TFFSqlTableProxy;
|
|
var FieldReferenced: TFFSqlFieldProxy): Boolean;
|
|
var
|
|
SameCase: Boolean;
|
|
begin
|
|
if SQLFunction in [sfUpper, sfLower] then
|
|
Result := Arg1.IsFieldFrom(Table, FieldReferenced, SameCase)
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlScalarFunc.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
case ExpectedType of
|
|
{!!.11 begin}
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString,
|
|
fftShortAnsiStr,
|
|
fftNullString,
|
|
fftNullAnsiStr,
|
|
fftWideString,
|
|
fftBLOB..fftBLOBTypedBin :
|
|
case GetType of
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString,
|
|
fftShortAnsiStr,
|
|
fftNullString,
|
|
fftNullAnsiStr,
|
|
fftWideString,
|
|
fftBLOB..fftBLOBTypedBin :
|
|
; {ok}
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
{!!.11 end}
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime:
|
|
case GetType of
|
|
fftStDate,
|
|
fftStTime,
|
|
fftDateTime:
|
|
; {ok}
|
|
else
|
|
TypeMismatch;
|
|
end;
|
|
else
|
|
if GetType <> ExpectedType then
|
|
TypeMismatch;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlScalarFunc.Reduce: Boolean;
|
|
begin
|
|
case SQLFunction of
|
|
sfCase :
|
|
Result := CaseExp.Reduce;
|
|
sfCharlen :
|
|
Result := Arg1.Reduce;
|
|
sfCoalesce :
|
|
Result := CoalesceExp.Reduce;
|
|
sfCurrentDate :
|
|
Result := False;
|
|
sfCurrentTime :
|
|
Result := False;
|
|
sfCurrentTimestamp :
|
|
Result := False;
|
|
sfCurrentUser :
|
|
Result := False;
|
|
sfLower :
|
|
Result := Arg1.Reduce;
|
|
sfUpper :
|
|
Result := Arg1.Reduce;
|
|
sfPosition :
|
|
begin
|
|
Result := Arg1.Reduce;
|
|
if not Result and (Arg2 <> nil) then
|
|
Result := Arg2.Reduce;
|
|
end;
|
|
sfSessionUser :
|
|
Result := False;
|
|
sfSubstring :
|
|
begin
|
|
Result := Arg1.Reduce or Arg2.Reduce;
|
|
if not Result and (Arg3 <> nil) then
|
|
Result := Arg3.Reduce;
|
|
end;
|
|
sfSystemUser :
|
|
Result := False;
|
|
sfTrim :
|
|
begin
|
|
if Arg2 = nil then begin
|
|
Result := Arg1.Reduce
|
|
end else
|
|
if Arg1 = nil then begin
|
|
Result := Arg2.Reduce;
|
|
end else begin
|
|
Result := Arg1.Reduce or Arg2.Reduce;
|
|
end;
|
|
end;
|
|
sfExtract :
|
|
begin
|
|
Result := Arg1.Reduce;
|
|
end;
|
|
sfNullIf :
|
|
begin
|
|
Result := Arg1.Reduce or Arg2.Reduce;
|
|
end;
|
|
{!!.11 begin}
|
|
sfAbs, sfCeil, sfFloor, sfExp, sfLog, sfRound :
|
|
Result := Arg1.Reduce;
|
|
sfRand :
|
|
Result := False;
|
|
sfPower :
|
|
Result := Arg1.Reduce or Arg2.Reduce;
|
|
{!!.11 end}
|
|
else
|
|
Result := False;
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlScalarFunc.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlWhenClauseList=============================================}
|
|
function TffSqlWhenClauseList.AddWhenClause(Value: TffSqlWhenClause): TffSqlWhenClause;
|
|
begin
|
|
WhenClauseList.Add(Value);
|
|
Result := Value;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlWhenClauseList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlWhenClauseList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlWhenClauseList(Source).WhenClauseCount) do
|
|
AddWhenClause(TffSqlWhenClause.Create(Self)).Assign(
|
|
TffSqlWhenClauseList(Source).WhenClause[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlWhenClauseList.CheckIsConstant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
for i := 0 to pred(WhenClauseCount) do
|
|
if not WhenClause[i].IsConstant then begin
|
|
FIsConstant := False;
|
|
exit;
|
|
end;
|
|
FIsConstant := True;
|
|
end;
|
|
|
|
constructor TffSqlWhenClauseList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
WhenClauseList := TList.Create;
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClauseList.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(WhenClauseCount) do
|
|
if WhenClause[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlWhenClauseList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(WhenClauseCount) do
|
|
WhenClause[i].Free;
|
|
WhenClauseList.Clear;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlWhenClauseList.Destroy;
|
|
begin
|
|
Clear;
|
|
WhenClauseList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlWhenClauseList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(WhenClauseCount) do
|
|
WhenClause[i].EmitSQL(Stream);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlWhenClauseList.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(WhenClauseCount) do
|
|
WhenClause[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClauseList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlWhenClauseList then begin
|
|
if WhenClauseCount <> TffSqlWhenClauseList(Other).WhenClauseCount then
|
|
exit;
|
|
for i := 0 to pred(WhenClauseCount) do
|
|
if not WhenClause[i].Equals(TffSqlWhenClauseList(Other).WhenClause[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClauseList.GetWhenClause(
|
|
Index: Integer): TffSqlWhenClause;
|
|
begin
|
|
Result := TffSqlWhenClause(WhenClauseList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClauseList.GetWhenClauseCount: Integer;
|
|
begin
|
|
Result := WhenClauseList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClauseList.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
procedure TffSqlWhenClauseList.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlWhenClause=================================================}
|
|
procedure TffSqlWhenClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlWhenClause then begin
|
|
if WhenExp = nil then
|
|
WhenExp := TffSqlCondExp.Create(Self);
|
|
WhenExp.Assign(TffSqlWhenClause(Source).WhenExp);
|
|
ThenExp.Free;
|
|
ThenExp := nil;
|
|
if assigned(TffSqlWhenClause(Source).ThenExp) then begin
|
|
ThenExp := TffSqlSimpleExpression.Create(Self);
|
|
ThenExp.Assign(TffSqlWhenClause(Source).ThenExp);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlWhenClause.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
FIsConstant := WhenExp.IsConstant and
|
|
(not assigned(ThenExp) or
|
|
ThenExp.IsConstant);
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := WhenExp.DependsOn(Table) or
|
|
((ThenExp <> nil) and ThenExp.DependsOn(Table));
|
|
end;
|
|
|
|
destructor TffSqlWhenClause.Destroy;
|
|
begin
|
|
WhenExp.Free;
|
|
ThenExp.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlWhenClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' WHEN ');
|
|
WhenExp.EmitSQL(Stream);
|
|
WriteStr(Stream,' THEN ');
|
|
if ThenExp <> nil then
|
|
ThenExp.EmitSQL(Stream)
|
|
else
|
|
WriteStr(Stream,' NULL');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlWhenClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
WhenExp.EnumNodes(EnumMethod, Deep);
|
|
if assigned(ThenExp) then
|
|
ThenExp.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlWhenClause)
|
|
and (WhenExp.Equals(TffSqlWhenClause(Other).WhenExp))
|
|
and
|
|
BothNil(ThenExp, TffSqlWhenClause(Other).ThenExp)
|
|
or (BothNonNil(ThenExp, TffSqlWhenClause(Other).ThenExp)
|
|
and (ThenExp.Equals(TffSqlWhenClause(Other).ThenExp)));
|
|
end;
|
|
{--------}
|
|
function TffSqlWhenClause.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
|
|
procedure TffSqlWhenClause.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlCaseExpression=============================================}
|
|
procedure TffSqlCaseExpression.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlCaseExpression then begin
|
|
if WhenClauseList = nil then
|
|
WhenClauseList := TffSqlWhenClauseList.Create(Self);
|
|
WhenClauseList.Assign(TffSqlCaseExpression(Source).WhenClauseList);
|
|
ElseExp.Free;
|
|
ElseExp := nil;
|
|
if Assigned(TffSqlCaseExpression(Source).ElseExp) then begin
|
|
ElseExp := TffSqlSimpleExpression.Create(Self);
|
|
ElseExp.Assign(TffSqlCaseExpression(Source).ElseExp);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlCaseExpression.CheckIsConstant;
|
|
begin
|
|
FIsConstantChecked := True;
|
|
FIsConstant :=
|
|
WhenClauseList.IsConstant and ((ElseExp = nil) or ElseExp.IsConstant);
|
|
if FIsConstant then begin
|
|
FIsConstant := False;
|
|
ConstantValue := GetValue;
|
|
FIsConstant := True;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlCaseExpression.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := WhenClauseList.DependsOn(Table) or
|
|
(ElseExp <> nil) and ElseExp.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlCaseExpression.Destroy;
|
|
begin
|
|
WhenClauseList.Free;
|
|
ElseExp.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlCaseExpression.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' CASE');
|
|
WhenClauseList.EmitSQL(Stream);
|
|
WriteStr(Stream,' ELSE ');
|
|
if ElseExp <> nil then
|
|
ElseExp.EmitSQL(Stream)
|
|
else
|
|
WriteStr(Stream, 'NULL');
|
|
WriteStr(Stream,' END');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCaseExpression.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
WhenClauseList.EnumNodes(EnumMethod, Deep);
|
|
if ElseExp <> nil then
|
|
ElseExp.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlCaseExpression.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlCaseExpression)
|
|
and WhenClauseList.Equals(TffSqlCaseExpression(Other).WhenClauseList)
|
|
and (BothNil(ElseExp, TffSqlCaseExpression(Other).ElseExp)
|
|
or (BothNonNil(ElseExp, TffSqlCaseExpression(Other).ElseExp)
|
|
and
|
|
ElseExp.Equals(TffSqlCaseExpression(Other).ElseExp)
|
|
)
|
|
);
|
|
end;
|
|
{--------}
|
|
function TffSqlCaseExpression.GetSize: Integer;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to pred(WhenClauseList.WhenClauseCount) do
|
|
if WhenClauseList.WhenClause[i].ThenExp <> nil then
|
|
Result := FFMaxI(Result, WhenClauseList.WhenClause[i].ThenExp.GetSize);
|
|
if ElseExp <> nil then
|
|
Result := FFMaxI(Result, ElseExp.GetSize);
|
|
end;
|
|
|
|
function TffSqlCaseExpression.GetType: TffFieldType;
|
|
begin
|
|
if WhenClauseList.WhenClause[0].ThenExp <> nil then
|
|
Result := WhenClauseList.WhenClause[0].ThenExp.GetType
|
|
else
|
|
Result := fftShortString; {actually, NULL}
|
|
end;
|
|
|
|
function TffSqlCaseExpression.GetValue: Variant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if IsConstant then begin
|
|
Result := ConstantValue;
|
|
exit;
|
|
end;
|
|
for i := 0 to pred(WhenClauseList.WhenClauseCount) do
|
|
if WhenClauseList.WhenClause[i].WhenExp.AsBoolean then begin
|
|
if WhenClauseList.WhenClause[i].ThenExp <> nil then
|
|
Result := WhenClauseList.WhenClause[i].ThenExp.GetValue
|
|
else
|
|
Result := Null;
|
|
exit;
|
|
end;
|
|
if ElseExp <> nil then
|
|
Result := ElseExp.GetValue
|
|
else
|
|
Result := Null;
|
|
end;
|
|
{--------}
|
|
function TffSqlCaseExpression.IsConstant: Boolean;
|
|
begin
|
|
if not FIsConstantChecked then
|
|
CheckIsConstant;
|
|
Result := FIsConstant;
|
|
end;
|
|
{--------}
|
|
function TffSqlCaseExpression.Reduce: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(WhenClauseList.WhenClauseCount) do
|
|
if WhenClauseList.WhenClause[i].WhenExp.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end else
|
|
if WhenClauseList.WhenClause[i].ThenExp <> nil then
|
|
if WhenClauseList.WhenClause[i].ThenExp.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if ElseExp <> nil then
|
|
Result := ElseExp.Reduce
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlCaseExpression.ResetConstant;
|
|
begin
|
|
FIsConstantChecked := False;
|
|
FIsConstant := False;
|
|
end;
|
|
|
|
{====================================================================}
|
|
|
|
{===TffSqlMatchClause================================================}
|
|
function TffSqlMatchClause.AsBoolean(const TestValue: Variant): Boolean;
|
|
begin
|
|
Result := SubQuery.Match(TestValue, Unique, Option)
|
|
end;
|
|
{--------}
|
|
procedure TffSqlMatchClause.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlMatchClause then begin
|
|
Unique := TffSqlMatchClause(Source).Unique;
|
|
Option := TffSqlMatchClause(Source).Option;
|
|
SubQuery.Free;
|
|
SubQuery := TffSqlSELECT.Create(Self);
|
|
SubQuery.Assign(TffSqlMatchClause(Source).SubQuery);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlMatchClause.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Result := SubQuery.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlMatchClause.Destroy;
|
|
begin
|
|
SubQuery.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlMatchClause.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ' MATCH');
|
|
if Unique then
|
|
WriteStr(Stream,' UNIQUE');
|
|
case Option of
|
|
moPartial :
|
|
WriteStr(Stream,' PARTIAL');
|
|
moFull :
|
|
WriteStr(Stream,' FULL');
|
|
end;
|
|
WriteStr(Stream,'(');
|
|
SubQuery.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlMatchClause.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
SubQuery.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlMatchClause.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlMatchClause)
|
|
and (Unique = TffSqlMatchClause(Other).Unique)
|
|
and (Option = TffSqlMatchClause(Other).Option)
|
|
and (SubQuery.Equals(TffSqlMatchClause(Other).SubQuery));
|
|
end;
|
|
{--------}
|
|
procedure TffSqlMatchClause.MatchType(ExpectedType: TffFieldType);
|
|
begin
|
|
SubQuery.MatchType(ExpectedType, False);
|
|
end;
|
|
|
|
function TffSqlMatchClause.Reduce: Boolean;
|
|
begin
|
|
Result := SubQuery.Reduce;
|
|
end;
|
|
|
|
{====================================================================}
|
|
{ TffSqlCoalesceExpression }
|
|
function TffSqlCoalesceExpression.AddArg(Value: TffSqlSimpleExpression): TffSqlSimpleExpression;
|
|
begin
|
|
ArgList.Add(Value);
|
|
Result := Value;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCoalesceExpression.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlCoalesceExpression then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlCoalesceExpression(Source).ArgCount) do
|
|
AddArg(TffSqlSimpleExpression.Create(Self)).Assign(
|
|
TffSqlCoalesceExpression(Source).Arg[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
constructor TffSqlCoalesceExpression.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
ArgList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCoalesceExpression.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ArgCount) do
|
|
Arg[i].Free;
|
|
ArgList.Clear;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlCoalesceExpression.Destroy;
|
|
begin
|
|
Clear;
|
|
ArgList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCoalesceExpression.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
WriteStr(Stream,' COALESCE(');
|
|
Arg[0].EmitSQL(Stream);
|
|
for i := 1 to pred(ArgCount) do begin
|
|
WriteStr(Stream,' ,');
|
|
Arg[i].EmitSQL(Stream);
|
|
end;
|
|
WriteStr(Stream,')');
|
|
end;
|
|
{--------}
|
|
procedure TffSqlCoalesceExpression.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(ArgCount) do
|
|
Arg[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlCoalesceExpression then
|
|
if ArgCount = TffSqlCoalesceExpression(Other).ArgCount then begin
|
|
for i := 0 to pred(ArgCount) do
|
|
if not Arg[i].Equals(TffSqlCoalesceExpression(Other).Arg[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.GetArg(
|
|
Index: Integer): TffSqlSimpleExpression;
|
|
begin
|
|
Result := TffSqlSimpleExpression(ArgList[Index]);
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.GetArgCount: Integer;
|
|
begin
|
|
Result := ArgList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.GetValue: Variant;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := Null;
|
|
for i := 0 to pred(ArgCount) do begin
|
|
Result := Arg[i].GetValue;
|
|
if Result <> Null then
|
|
exit;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.DependsOn(
|
|
Table: TFFSqlTableProxy): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ArgCount) do
|
|
if Arg[i].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.GetType: TffFieldType;
|
|
begin
|
|
Result := Arg[0].GetType;
|
|
end;
|
|
{--------}
|
|
function TffSqlCoalesceExpression.Reduce: Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ArgCount) do
|
|
if Arg[i].Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
{====================================================================}
|
|
|
|
function TffSqlCoalesceExpression.GetSize: Integer;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to pred(ArgCount) do
|
|
Result := FFMaxI(Result, Arg[i].GetSize);
|
|
end;
|
|
|
|
{ TFFSqlTableProxySubset }
|
|
|
|
procedure TFFSqlTableProxySubset.Assign(
|
|
const Source: TFFSqlTableProxySubset);
|
|
begin
|
|
FTable := Source.Table;
|
|
KeyRelation := Source.KeyRelation;
|
|
Outer := Source.Outer;
|
|
Opposite := Source.Opposite;
|
|
end;
|
|
|
|
constructor TFFSqlTableProxySubset.Create;
|
|
begin
|
|
FTable := Table;
|
|
end;
|
|
|
|
procedure TFFSqlTableProxySubset.Iterate(Iterator: TFFSqlTableIterator;
|
|
Cookie: TffWord32);
|
|
begin
|
|
FTable.Iterate(Iterator, Cookie);
|
|
end;
|
|
|
|
function TFFSqlTableProxySubset.UniqueValue: Boolean;
|
|
begin
|
|
Result :=
|
|
(KeyRelation.RelationFieldCount = KeyRelation.RelationKeyFieldCount)
|
|
and (KeyRelation.RelationOperators[KeyRelation.RelationKeyFieldCount - 1] = roEQ);
|
|
end;
|
|
|
|
function TFFSqlTableProxySubset.ClosedSegment: Boolean;
|
|
begin
|
|
Result := KeyRelation.RelationOperatorB[KeyRelation.RelationKeyFieldCount - 1] <> roNone; {!!.11}
|
|
end;
|
|
|
|
function TFFSqlTableProxySubset.KeyDepth: Integer;
|
|
begin
|
|
Result := KeyRelation.RelationFieldCount;
|
|
end;
|
|
|
|
function TFFSqlTableProxySubset.EqualKeyDepth: Integer;
|
|
begin
|
|
Result := 0;
|
|
while (Result < KeyRelation.RelationFieldCount)
|
|
and (KeyRelation.RelationOperators[Result] = roEQ) do
|
|
inc(Result);
|
|
end;
|
|
|
|
{ TFFSqlTableProxySubsetList }
|
|
|
|
function TFFSqlTableProxySubsetList.Add(
|
|
TableProxySubset: TFFSqlTableProxySubset): TFFSqlTableProxySubset;
|
|
begin
|
|
FList.Add(TableProxySubset);
|
|
Result := TableProxySubset;
|
|
end;
|
|
|
|
{!!.10 new}
|
|
function TFFSqlTableProxySubsetList.Insert(
|
|
TableProxySubset: TFFSqlTableProxySubset): TFFSqlTableProxySubset;
|
|
begin
|
|
FList.Insert(0, TableProxySubset);
|
|
Result := TableProxySubset;
|
|
end;
|
|
|
|
procedure TFFSqlTableProxySubsetList.Assign(
|
|
const Source: TFFSqlTableProxySubsetList);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Clear;
|
|
for i := 0 to pred(Source.Count) do
|
|
Add(TFFSqlTableProxySubset.Create(Source.Item[i].Table)).Assign(Source.Item[i]);
|
|
OuterJoin := Source.OuterJoin;
|
|
end;
|
|
|
|
constructor TFFSqlTableProxySubsetList.Create;
|
|
begin
|
|
Assert(AOwner <> nil);
|
|
FOwner := AOwner;
|
|
FList := TList.Create;
|
|
end;
|
|
|
|
procedure TFFSqlTableProxySubsetList.Delete(Index: Integer);
|
|
begin
|
|
FList.Delete(Index);
|
|
end;
|
|
|
|
procedure TFFSqlTableProxySubsetList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FList.Count) do
|
|
Item[i].Free;
|
|
FList.Clear;
|
|
end;
|
|
|
|
destructor TFFSqlTableProxySubsetList.Destroy;
|
|
begin
|
|
Clear;
|
|
FList.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function TFFSqlTableProxySubsetList.GetCount: Integer;
|
|
begin
|
|
Result := FList.Count;
|
|
end;
|
|
|
|
function TFFSqlTableProxySubsetList.GetItem(
|
|
Index: Integer): TFFSqlTableProxySubset;
|
|
begin
|
|
Result := TFFSqlTableProxySubset(FList[Index]);
|
|
end;
|
|
|
|
function TFFSqlTableProxySubsetList.RelationUsed(
|
|
Relation: TffSqlCondFactor): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(Count) do
|
|
if Item[i].KeyRelation.CondF = Relation then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function TFFSqlTableProxySubsetList.DependencyExists(
|
|
Table : TFFSqlTableProxy): Boolean;
|
|
var
|
|
i, j : Integer;
|
|
begin
|
|
for i := 0 to pred(Count) do
|
|
for j := 0 to Item[i].KeyRelation.RelationFieldCount - 1 do begin
|
|
if Item[i].KeyRelation.ArgExpressions[j].DependsOn(Table) then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if (Item[i].KeyRelation.ArgExpressionB[j] <> nil) {!!.11}
|
|
and Item[i].KeyRelation.ArgExpressionB[j].DependsOn(Table) then begin {!!.11}
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function TFFSqlTableProxySubsetList.ProcessLevel(Cookie1: TffWord32): Boolean;
|
|
begin
|
|
inc(FRecordsRead);
|
|
inc(Owner.RecordsRead);
|
|
{ Time to check for timeout? }
|
|
if FRecordsRead mod 1000 = 0 then
|
|
FFCheckRemainingTime;
|
|
Result := True; {continue}
|
|
if Level = 0 then begin
|
|
if FCondTerm.AsBoolean then
|
|
if not SkipInner then
|
|
FCreateResultRecord;
|
|
if SkipInner then
|
|
{SkipInner means we're writing NULL records for outer join
|
|
records with no match, so we just need to know if there
|
|
were any here; we don't need to see the rest, so stop reading:}
|
|
Result := False;
|
|
WroteRow := True;
|
|
end else begin
|
|
if FCondTerm.AsBooleanLevel(Level) then begin
|
|
dec(Level);
|
|
ReadSources;
|
|
inc(Level);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TFFSqlTableProxySubsetList.ReadSources;
|
|
var
|
|
{V : array[0..pred(ffcl_MaxIndexFlds)] of Variant;
|
|
VB : array[0..pred(ffcl_MaxIndexFlds)] of Variant;} {!!.11}
|
|
i : Integer;
|
|
NullLimit,
|
|
BUsed : Boolean;
|
|
KeyHasIntervals: Boolean; {!!.11}
|
|
begin
|
|
with Item[Level] do begin
|
|
NullLimit := False;
|
|
if KeyRelation.CondF <> nil then begin
|
|
Table.SetIndex(KeyRelation.NativeKeyIndex - 1);
|
|
for i := 0 to KeyRelation.RelationFieldCount - 1 do begin
|
|
Assert(KeyRelation.ArgExpressions[i] is TffSqlSimpleExpression);
|
|
V[i] := TffSqlSimpleExpression(KeyRelation.ArgExpressions[i]).GetValue;
|
|
if VarIsNull(V[i]) then
|
|
NullLimit := True;
|
|
VB[i] := V[i];
|
|
end;
|
|
|
|
{!!.11 begin}
|
|
KeyHasIntervals := False;
|
|
for i := 0 to KeyRelation.RelationFieldCount - 2 do
|
|
if KeyRelation.RelationOperators[i] <> roEQ then begin
|
|
KeyHasIntervals := True;
|
|
break;
|
|
end;
|
|
{!!.11 end}
|
|
{!!.13}
|
|
{can't preevaluate open intervals on key alone because of possible null values}
|
|
for i := 0 to KeyRelation.RelationFieldCount - 1 do
|
|
case KeyRelation.RelationOperators[i] of
|
|
roL, roG : begin
|
|
KeyHasIntervals := True;
|
|
break;
|
|
end;
|
|
end;
|
|
{!!.13}
|
|
|
|
if not KeyHasIntervals and {!!.11}
|
|
not KeyRelation.RelationKeyIsCaseInsensitive then
|
|
KeyRelation.CondF.MarkTrue;
|
|
|
|
for i := 0 to KeyRelation.RelationFieldCount - 1 do {!!.11}
|
|
if KeyRelation.RelationOperatorB[i] <> roNone then begin {!!.11}
|
|
Assert(KeyRelation.ArgExpressionB[i] is TffSqlSimpleExpression); {!!.11}
|
|
VB[i{KeyRelation.RelationFieldCount - 1}] := {!!.11}
|
|
TffSqlSimpleExpression(KeyRelation.ArgExpressionB[i]).GetValue; {!!.11}
|
|
if VarIsNull(VB[i{KeyRelation.RelationFieldCount - 1}]) then {!!.11}
|
|
NullLimit := True;
|
|
end;
|
|
BUsed := False;
|
|
if not NullLimit then
|
|
case KeyRelation.RelationOperators[KeyRelation.RelationFieldCount - 1] of
|
|
roEQ :
|
|
Table.SetRange(V, VB, KeyRelation.RelationFieldCount, {!!.11}
|
|
KeyRelation.RelationFieldCount, True, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
roLE :
|
|
case KeyRelation.RelationOperatorB[KeyRelation.RelationFieldCount - 1] of {!!.11}
|
|
roG :
|
|
begin
|
|
Table.SetRange(VB, V, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, False, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
roGE :
|
|
begin
|
|
Table.SetRange(VB, V, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, True, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
else
|
|
Table.SetRange(V, V, KeyRelation.RelationFieldCount - 1,
|
|
KeyRelation.RelationFieldCount, True, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
end;
|
|
roL :
|
|
case KeyRelation.RelationOperatorB[KeyRelation.RelationFieldCount - 1] of {!!.11}
|
|
roG :
|
|
begin
|
|
Table.SetRange(VB, V, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, False, False,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
roGE :
|
|
begin
|
|
Table.SetRange(VB, V, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, True, False,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
else
|
|
Table.SetRange(V, V, KeyRelation.RelationFieldCount - 1,
|
|
KeyRelation.RelationFieldCount, True, False,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
end;
|
|
roG :
|
|
case KeyRelation.RelationOperatorB[KeyRelation.RelationFieldCount - 1] of {!!.11}
|
|
roLE :
|
|
begin
|
|
Table.SetRange(V, VB, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, False, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
roL :
|
|
begin
|
|
Table.SetRange(V, VB, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, False, False,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
else
|
|
Table.SetRange(V, V, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount - 1, False, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
end;
|
|
roGE :
|
|
case KeyRelation.RelationOperatorB[KeyRelation.RelationFieldCount - 1] of {!!.11}
|
|
roLE :
|
|
begin
|
|
Table.SetRange(V, VB, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, True, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
roL :
|
|
begin
|
|
Table.SetRange(V, VB, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount, True, False,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
BUsed := True;
|
|
end;
|
|
else
|
|
Table.SetRange(V, V, KeyRelation.RelationFieldCount,
|
|
KeyRelation.RelationFieldCount - 1, True, True,
|
|
KeyRelation.RelationKeyIndexAsc);
|
|
end;
|
|
else
|
|
Assert(False);
|
|
end;
|
|
if not KeyHasIntervals and {!!.11}
|
|
not KeyRelation.RelationKeyIsCaseInsensitive and BUsed then
|
|
KeyRelation.RelationB[KeyRelation.RelationFieldCount - 1].MarkTrue; {!!.11}
|
|
end else
|
|
Table.SetIndex(-1);
|
|
{if not NullLimit then begin} {!!.11}
|
|
WroteRow := False;
|
|
if not NullLimit then {!!.11}
|
|
Iterate(ProcessLevel, 0);
|
|
if OuterJoin and not WroteRow and (Level = 0) then begin
|
|
Item[0].Table.NullRecord;
|
|
FCreateResultRecord;
|
|
end;
|
|
{end;} {!!.11}
|
|
if KeyRelation.CondF <> nil then begin
|
|
KeyRelation.CondF.MarkUnknown;
|
|
if KeyRelation.RelationOperatorB[KeyRelation.RelationFieldCount - 1] <> roNone then {!!.11}
|
|
KeyRelation.RelationB[KeyRelation.RelationFieldCount - 1].MarkUnknown; {!!.11}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TFFSqlTableProxySubsetList.Join;
|
|
begin
|
|
FCondTerm := CondTerm;
|
|
CondTerm.SetLevelDep(Self);
|
|
FCreateResultRecord := CreateResultRecord;
|
|
Level := Count - 1;
|
|
ReadSources;
|
|
end;
|
|
|
|
{ TffSqlINSERT }
|
|
|
|
procedure TffSqlINSERT.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlINSERT then begin
|
|
Clear;
|
|
DefaultValues := TffSqlINSERT(Source).DefaultValues;
|
|
TableName := TffSqlINSERT(Source).TableName;
|
|
if TffSqlINSERT(Source).InsertColumnList <> nil then begin
|
|
InsertColumnList := TffSqlInsertColumnList.Create(Self);
|
|
InsertColumnList.Assign(TffSqlINSERT(Source).InsertColumnList);
|
|
end;
|
|
|
|
if TffSqlINSERT(Source).TableExp <> nil then begin
|
|
TableExp := TffSqlTableExp.Create(Self);
|
|
TableExp.Assign(TffSqlINSERT(Source).TableExp);
|
|
end;
|
|
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlINSERT.AddColumns(Node: TffSqlNode);
|
|
begin
|
|
Node.AddColumnDef(Self);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlINSERT.Bind;
|
|
var
|
|
i: Integer;
|
|
F: TFFSqlFieldProxy;
|
|
begin
|
|
if InsertColumnList <> nil then
|
|
InsertColumnList.EnumNodes(ClearBindings, False);
|
|
T := Owner.FDatabase.TableByName(Self, TableName, False, ''); {!!.11}
|
|
if T = nil then
|
|
SQLError('Unable to open table: ' + TableName +
|
|
'. Ensure the table exists and is not in use by ' +
|
|
'another process.');
|
|
|
|
{build column list}
|
|
Assert(Assigned(Columns));
|
|
Columns.Clear;
|
|
if InsertColumnList <> nil then
|
|
InsertColumnList.EnumNodes(AddColumns, False);
|
|
if Columns.Count = 0 then begin
|
|
for i := 0 to T.FieldCount - 1 do begin
|
|
F := T.Field(i);
|
|
if not F.CanUpdate then
|
|
SQLError('Changing fields of this type is not currently supported ' +
|
|
'through SQL:' + Columns[i]);
|
|
Columns.AddObject(T.Field(i).Name, F);
|
|
end;
|
|
end else begin
|
|
for i := 0 to Columns.Count - 1 do begin
|
|
F := T.FieldByName(Columns[i]);
|
|
if F = nil then
|
|
SQLError('Unknown field for table ' + TableName + 'in INSERT statement:' +
|
|
Columns[i]);
|
|
|
|
if not F.CanUpdate then
|
|
SQLError('Changing fields of this type is not currently supported through SQL:' +
|
|
Columns[i]);
|
|
|
|
Columns.Objects[i] := F;
|
|
end;
|
|
end;
|
|
Bound := True;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlINSERT.Clear;
|
|
begin
|
|
TableName := '';
|
|
InsertColumnList.Free;
|
|
InsertColumnList := nil;
|
|
TableExp.Free;
|
|
TableExp := nil;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlINSERT.ClearBindings(Node: TffSqlNode);
|
|
begin
|
|
Node.ClearBinding;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlINSERT.Destroy;
|
|
begin
|
|
Clear;
|
|
if T <> nil then
|
|
if T.Owner = Self then begin
|
|
T.Owner := nil;
|
|
T.Free;
|
|
end;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlINSERT.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, 'INSERT INTO ');
|
|
WriteStr(Stream, TableName);
|
|
WriteStr(Stream,' ');
|
|
if DefaultValues then
|
|
WriteStr(Stream,'DEFAULT VALUES ')
|
|
else begin
|
|
if assigned(InsertColumnList) then begin
|
|
WriteStr(Stream,'(');
|
|
InsertColumnList.EmitSQL(Stream);
|
|
WriteStr(Stream,') ');
|
|
end;
|
|
if assigned(TableExp) then
|
|
TableExp.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlINSERT.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(InsertColumnList) then
|
|
InsertColumnList.EnumNodes(EnumMethod,Deep);
|
|
if assigned(TableExp) then
|
|
TableExp.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlINSERT.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlINSERT)
|
|
and (DefaultValues = TffSqlINSERT(Other).DefaultValues)
|
|
and (TableName = TffSqlINSERT(Other).TableName)
|
|
and (BothNil(InsertColumnList, TffSqlINSERT(Other).InsertColumnList)
|
|
or (BothNonNil(InsertColumnList, TffSqlINSERT(Other).InsertColumnList)
|
|
and InsertColumnList.Equals(TffSqlINSERT(Other).InsertColumnList))
|
|
)
|
|
and (BothNil(TableExp, TffSqlINSERT(Other).TableExp)
|
|
or (BothNonNil(TableExp, TffSqlINSERT(Other).TableExp)
|
|
and TableExp.Equals(TffSqlINSERT(Other).TableExp))
|
|
);
|
|
end;
|
|
{Begin !!.13}
|
|
{--------}
|
|
function CanInsert(const SrcType, TgtType : TffFieldType) : Boolean;
|
|
begin
|
|
{ According to our past rules, which are very lax, most every type is
|
|
compatible with all other types. New rules:
|
|
- BLOBs may not be inserted into non-BLOB fields
|
|
- strings may be inserted into BLOBs
|
|
- strings cannot be inserted into numerics or date time }
|
|
if SrcType <> TgtType then
|
|
case TgtType of
|
|
{ Numerics & datetime values may be inserted into numerics. }
|
|
fftByte..fftCurrency :
|
|
case SrcType of
|
|
fftByte..fftCurrency, fftStDate..fftDateTime :
|
|
Result := True;
|
|
else
|
|
Result := False;
|
|
end;
|
|
fftStDate..fftDateTime :
|
|
{ Numerics, datetime, and string values may be inserted into datetime
|
|
columns. If a date is to be inserted via a string, the string must
|
|
be preceded via the DATE keyword. }
|
|
case SrcType of
|
|
fftByte..fftCurrency,
|
|
fftStDate..fftDateTime :
|
|
Result := True;
|
|
else
|
|
Result := False;
|
|
end; { case }
|
|
fftChar,
|
|
fftWideChar,
|
|
fftShortString..fftWideString :
|
|
{ Everything except BLOBs may be inserted into a string. }
|
|
case SrcType of
|
|
fftBLOB..fftBLOBTypedBIN :
|
|
Result := False;
|
|
else
|
|
Result := True;
|
|
end; { case }
|
|
fftBLOB..fftBLOBTypedBIN :
|
|
{ Strings & other BLOBs may be inserted into BLOBs. }
|
|
case SrcType of
|
|
fftChar, fftWideChar,
|
|
fftShortString..fftWideString,
|
|
fftBLOB..fftBLOBTypedBIN :
|
|
Result := True;
|
|
else
|
|
Result := False;
|
|
end; { case }
|
|
else
|
|
Result := False;
|
|
end { case }
|
|
else
|
|
Result := True;
|
|
end;
|
|
{End !!.13}
|
|
{--------}
|
|
function TffSqlINSERT.Execute(var RowsAffected: Integer) : TffResult;
|
|
{Revised !!.13}
|
|
var
|
|
i : Integer;
|
|
ST : TffSQLTableProxy;
|
|
begin
|
|
Result := Owner.FDatabase.StartTransaction([T]);
|
|
if Result = DBIERR_NONE then
|
|
try
|
|
RowsAffected := 0;
|
|
if not Bound then
|
|
Bind;
|
|
{ Make sure the target table can be modified. }
|
|
Result := T.EnsureWritable;
|
|
if Result <> DBIERR_NONE then begin
|
|
Owner.FDatabase.AbortTransaction;
|
|
Exit;
|
|
end;
|
|
|
|
{ If inserting default values only then do so. }
|
|
if DefaultValues then begin
|
|
T.Insert;
|
|
T.SetDefaults;
|
|
Result := T.Post;
|
|
if Result = DBIERR_NONE then begin
|
|
Owner.FDatabase.Commit;
|
|
RowsAffected := 1;
|
|
end
|
|
else
|
|
Owner.FDatabase.AbortTransaction;
|
|
end
|
|
else if TableExp <> nil then begin
|
|
{ Values are coming from a valuelist or subquery. }
|
|
ST := TableExp.ResultTable;
|
|
{ Validate the number of source and target columns. }
|
|
if ST.FieldCount <> Columns.Count then
|
|
SQLError('The number of columns in the source clause must match ' +
|
|
'the number of columns in the INSERT statement.');
|
|
|
|
{ Do the field types match? }
|
|
for i := 0 to Pred(ST.FieldCount) do
|
|
if not CanInsert(ST.Field(i).GetType,
|
|
TffSqlFieldProxy(Columns.Objects[i]).GetType) then
|
|
SQLError(Format('The type for source column %d (column name ' +
|
|
'"%s") is incompatible with the type for ' +
|
|
'target column %d (column name "%s")',
|
|
[i, ST.Field(i).Name, i, Columns[i]]));
|
|
|
|
{ Roll through the source table, inserting its rows into the result
|
|
table. }
|
|
ST.First;
|
|
while not ST.EOF do begin
|
|
T.Insert;
|
|
T.SetDefaults;
|
|
for i := 0 to FFMinI(Pred(ST.FieldCount), Pred(Columns.Count)) do
|
|
TFFSqlFieldProxy(Columns.Objects[i]).SetValue(ST.Field(i).GetValue);
|
|
Result := T.PostNoDefaults;
|
|
if Result = DBIERR_NONE then
|
|
inc(RowsAffected)
|
|
else
|
|
break;
|
|
ST.Next;
|
|
end;
|
|
if Result = DBIERR_NONE Then
|
|
Owner.FDatabase.Commit
|
|
else begin
|
|
Owner.FDatabase.AbortTransaction;
|
|
RowsAffected := 0;
|
|
end;
|
|
end else
|
|
Assert(False, 'Unexpected INSERT scenario');
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
RowsAffected := 0;
|
|
raise;
|
|
end
|
|
else if Result = DBIERR_LOCKED then
|
|
FFRaiseException(EffException, ffStrResServer, fferrLockRejected,
|
|
[ffcLockExclusive, '', T.Name])
|
|
else
|
|
FFRaiseException(EffException, ffStrResServer, Result, [T.Name]);
|
|
end;
|
|
{--------}
|
|
{!!.11 new}
|
|
function TffSqlINSERT.Reduce: Boolean;
|
|
begin
|
|
if TableExp <> nil then
|
|
if TableExp.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
{ TffSqlInsertItem }
|
|
|
|
procedure TffSqlInsertItem.AddColumnDef(Target: TffSqlColumnListOwner);
|
|
begin
|
|
Target.Columns.Add(ColumnName);
|
|
end;
|
|
|
|
procedure TffSqlInsertItem.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlInsertItem then begin
|
|
ColumnName := TffSqlInsertItem(Source).ColumnName;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlInsertItem.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ColumnName);
|
|
end;
|
|
|
|
procedure TffSqlInsertItem.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
|
|
function TffSqlInsertItem.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlInsertItem)
|
|
and (ColumnName = TffSqlInsertItem(Other).ColumnName);
|
|
end;
|
|
|
|
{ TffSqlInsertColumnList }
|
|
|
|
function TffSqlInsertColumnList.AddItem(
|
|
NewInsertColumn: TffSqlInsertItem): TffSqlInsertItem;
|
|
begin
|
|
FInsertColumnItemList.Add(NewInsertColumn);
|
|
Result := NewInsertColumn;
|
|
end;
|
|
|
|
procedure TffSqlInsertColumnList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlInsertColumnList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlInsertColumnList(Source).InsertColumnCount) do
|
|
AddItem(TffSqlInsertItem.Create(Self)).Assign(
|
|
TffSqlInsertColumnList(Source).InsertColumnItem[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlInsertColumnList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(InsertColumnCount) do
|
|
InsertColumnItem[i].Free;
|
|
FInsertColumnItemList.Clear;
|
|
end;
|
|
|
|
constructor TffSqlInsertColumnList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited;
|
|
FInsertColumnItemList := TList.Create;
|
|
end;
|
|
|
|
destructor TffSqlInsertColumnList.Destroy;
|
|
begin
|
|
Clear;
|
|
FInsertColumnItemList.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlInsertColumnList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
First: Boolean;
|
|
begin
|
|
First := True;
|
|
for i := 0 to pred(InsertColumnCount) do begin
|
|
if First then
|
|
First := False
|
|
else
|
|
WriteStr(Stream, ', ');
|
|
InsertColumnItem[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlInsertColumnList.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(InsertColumnCount) do
|
|
InsertColumnItem[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlInsertColumnList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlInsertColumnList then begin
|
|
if InsertColumnCount <> TffSqlInsertColumnList(Other).InsertColumnCount then
|
|
exit;
|
|
for i := 0 to pred(InsertColumnCount) do
|
|
if not InsertColumnItem[i].Equals(TffSqlInsertColumnList(Other).InsertColumnItem[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlInsertColumnList.GetInsertColumnCount: Integer;
|
|
begin
|
|
Result := FInsertColumnItemList.Count;
|
|
end;
|
|
|
|
function TffSqlInsertColumnList.GetInsertColumnItem(
|
|
Index: Integer): TffSqlInsertItem;
|
|
begin
|
|
Result := TffSqlInsertItem(FInsertColumnItemList[Index]);
|
|
end;
|
|
|
|
procedure TffSqlInsertColumnList.SetInsertColumnItem(Index: Integer;
|
|
const Value: TffSqlInsertItem);
|
|
begin
|
|
FInsertColumnItemList[Index] := Value;
|
|
end;
|
|
|
|
{ TffSqlValueItem }
|
|
|
|
procedure TffSqlValueItem.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlValueItem then begin
|
|
Simplex.Free;
|
|
{Simplex := nil;} {unnecessary}
|
|
Default := TffSqlUpdateItem(Source).Default;
|
|
Simplex := TffSqlSimpleExpression.Create(Self);
|
|
Simplex.Assign(TffSqlValueItem(Source).Simplex);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
destructor TffSqlValueItem.Destroy;
|
|
begin
|
|
Simplex.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlValueItem.EmitSQL(Stream: TStream);
|
|
begin
|
|
if Default then
|
|
WriteStr(Stream, 'DEFAULT ')
|
|
else if Simplex = nil then
|
|
WriteStr(Stream, 'NULL ')
|
|
else
|
|
Simplex.EmitSQL(Stream);
|
|
end;
|
|
|
|
procedure TffSqlValueItem.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(Simplex) then
|
|
Simplex.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlValueItem.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlValueItem)
|
|
and (Default = TffSqlValueItem(Other).Default)
|
|
and (BothNil(Simplex, TffSqlValueItem(Other).Simplex)
|
|
or (BothNonNil(Simplex, TffSqlValueItem(Other).Simplex)
|
|
and Simplex.Equals(TffSqlValueItem(Other).Simplex)));
|
|
end;
|
|
|
|
function TffSqlValueItem.GetDecimals: Integer;
|
|
begin
|
|
if assigned(Simplex) then
|
|
Result := Simplex.GetDecimals
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TffSqlValueItem.GetSize: Integer;
|
|
begin
|
|
if assigned(Simplex) then
|
|
Result := Simplex.GetSize
|
|
else
|
|
Result := 1;
|
|
end;
|
|
|
|
function TffSqlValueItem.GetType: TffFieldType;
|
|
begin
|
|
if assigned(Simplex) then
|
|
Result := Simplex.GetType
|
|
else
|
|
Result := fftBoolean;
|
|
end;
|
|
|
|
{ TffSqlValueList }
|
|
|
|
function TffSqlValueList.AddItem(
|
|
NewValue: TffSqlValueItem): TffSqlValueItem;
|
|
begin
|
|
FValueItemList.Add(NewValue);
|
|
Result := NewValue;
|
|
end;
|
|
|
|
procedure TffSqlValueList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlValueList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlValueList(Source).ValueCount) do
|
|
AddItem(TffSqlValueItem.Create(Self)).Assign(
|
|
TffSqlValueList(Source).ValueItem[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlValueList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(ValueCount) do
|
|
ValueItem[i].Free;
|
|
FValueItemList.Clear;
|
|
end;
|
|
|
|
constructor TffSqlValueList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited;
|
|
FValueItemList := TList.Create;
|
|
end;
|
|
|
|
destructor TffSqlValueList.Destroy;
|
|
begin
|
|
Clear;
|
|
FValueItemList.Free;
|
|
if FResultTable <> nil then begin
|
|
if FResultTable.Owner = Self then begin
|
|
FResultTable.Owner := nil;
|
|
FResultTable.Free;
|
|
end;
|
|
end;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlValueList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
First: Boolean;
|
|
begin
|
|
First := True;
|
|
for i := 0 to pred(ValueCount) do begin
|
|
if First then
|
|
First := False
|
|
else
|
|
WriteStr(Stream, ', ');
|
|
ValueItem[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlValueList.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(ValueCount) do
|
|
ValueItem[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlValueList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlValueList then begin
|
|
if ValueCount <> TffSqlValueList(Other).ValueCount then
|
|
exit;
|
|
for i := 0 to pred(ValueCount) do
|
|
if not ValueItem[i].Equals(TffSqlValueList(Other).ValueItem[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlValueList.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
begin
|
|
raise Exception.Create('Not yet implemented');
|
|
end;
|
|
|
|
function TffSqlValueList.GetResultTable: TFFSqlTableProxy;
|
|
var
|
|
FieldDefList : TffSqlFieldDefList;
|
|
i: Integer;
|
|
FldName : string; {!!.11}
|
|
Field : TffSqlFieldProxy; {!!.11}
|
|
begin
|
|
{Begin !!.13}
|
|
if FResultTable <> nil then
|
|
for i := 0 to pred(ValueCount) do
|
|
if (ValueItem[i].Simplex <> nil) and
|
|
not ValueItem[i].Simplex.IsConstant then begin
|
|
FResultTable.Owner := nil;
|
|
FResultTable.Free;
|
|
FResultTable := nil;
|
|
break;
|
|
end; { if }
|
|
{End !!.13}
|
|
if FResultTable = nil then begin
|
|
FieldDefList := TffSqlFieldDefList.Create;
|
|
try
|
|
{Begin !!.11}
|
|
for i := 0 to pred(ValueCount) do begin
|
|
FldName := 'Value_'+IntToStr(i+1);
|
|
Field := OwnerStmt.T.Field(i);
|
|
if ValueItem[i].Default then
|
|
FieldDefList.AddField(FldName, Field.GetType, Field.GetSize,
|
|
Field.GetDecimals)
|
|
else
|
|
FieldDefList.AddField(FldName, ValueItem[i].GetType,
|
|
ValueItem[i].GetSize, ValueItem[i].GetDecimals);
|
|
end; { for }
|
|
{End !!.11}
|
|
FResultTable := Owner.FDatabase.CreateTemporaryTableWithoutIndex(Self, FieldDefList); {!!.10}
|
|
finally
|
|
FieldDefList.Free;
|
|
end;
|
|
Owner.FDatabase.StartTransaction([nil]);
|
|
try
|
|
FResultTable.Insert;
|
|
for i := 0 to pred(ValueCount) do
|
|
if ValueItem[i].Simplex <> nil then
|
|
FResultTable.Field(i).SetValue(ValueItem[i].Simplex.GetValue)
|
|
{Begin !!.11}
|
|
else if ValueItem[i].Default then
|
|
FResultTable.Field(i).SetDefault
|
|
{End !!.11}
|
|
else
|
|
FResultTable.Field(i).SetFieldToNull;
|
|
FResultTable.Post;
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
FResultTable.Owner := nil;
|
|
FResultTable.Free;
|
|
FResultTable := nil;
|
|
raise;
|
|
end;
|
|
Owner.FDatabase.Commit;
|
|
end;
|
|
Result := FResultTable;
|
|
end;
|
|
|
|
function TffSqlValueList.GetValueCount: Integer;
|
|
begin
|
|
Result := FValueItemList.Count;
|
|
end;
|
|
|
|
function TffSqlValueList.GetValueItem(Index: Integer): TffSqlValueItem;
|
|
begin
|
|
Result := TffSqlValueItem(FValueItemList[Index]);
|
|
end;
|
|
|
|
function TffSqlValueList.Reduce: Boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlValueList.SetValueItem(Index: Integer;
|
|
const Value: TffSqlValueItem);
|
|
begin
|
|
FValueItemList[Index] := Value;
|
|
end;
|
|
|
|
{ TffSqlDELETE }
|
|
|
|
procedure TffSqlDELETE.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlDELETE then begin
|
|
Clear;
|
|
|
|
if TffSqlDELETE(Source).TableRef <> nil then begin
|
|
TableRef := TffSqlTableRef.Create(Self);
|
|
TableRef.Assign(TffSqlDELETE(Source).TableRef);
|
|
end;
|
|
|
|
if TffSqlDELETE(Source).CondExpWhere <> nil then begin
|
|
CondExpWhere := TffSqlCondExp.Create(Self);
|
|
CondExpWhere.Assign(TffSqlDELETE(Source).CondExpWhere);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlDELETE.Bind;
|
|
begin
|
|
Assert(TableRef <> nil);
|
|
T := TableRef.GetTable(Self, False); {!!.11}
|
|
if T = nil then
|
|
SQLError('Unable to open table: ' + TableRef.SQLName + //TableName +
|
|
'. Ensure the table exists and is not in use by ' +
|
|
'another process.');
|
|
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.MatchType(fftBoolean);
|
|
Bound := True;
|
|
end;
|
|
|
|
function TffSqlDELETE.BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
Result := nil;
|
|
Assert(T <> nil);
|
|
Assert(T is TffSqlTableProxy);
|
|
if T.FieldByName(FieldName) <> nil then begin
|
|
Result := T.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
SQLError('Unknown field:' + FieldName);
|
|
end;
|
|
|
|
procedure TffSqlDELETE.Clear;
|
|
begin
|
|
TableRef.Free;
|
|
TableRef := nil;
|
|
CondExpWhere.Free;
|
|
CondExpWhere := nil;
|
|
end;
|
|
|
|
procedure TffSqlDELETE.DeleteRecord;
|
|
var
|
|
Pos: TffInt64;
|
|
begin
|
|
Pos := T.GetCurrentRecordID;
|
|
DeleteList.Add(Pointer(Pos.iLow));
|
|
DeleteList.Add(Pointer(Pos.iHigh));
|
|
end;
|
|
|
|
destructor TffSqlDELETE.Destroy;
|
|
begin
|
|
if T <> nil then
|
|
if T.Owner = Self then begin
|
|
T.Owner := nil;
|
|
T.Free;
|
|
end;
|
|
Clear;
|
|
Joiner.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlDELETE.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,'DELETE FROM ');
|
|
TableRef.EmitSQL(Stream);
|
|
WriteStr(Stream,' ');
|
|
if assigned(CondExpWhere) then begin
|
|
WriteStr(Stream,'WHERE ');
|
|
CondExpWhere.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlDELETE.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(TableRef) then
|
|
TableRef.EnumNodes(EnumMethod, Deep);
|
|
if assigned(CondExpWhere) then
|
|
CondExpWhere.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlDELETE.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlDELETE)
|
|
and (BothNil(TableRef, TffSqlDELETE(Other).TableRef)
|
|
or (BothNonNil(TableRef, TffSqlDELETE(Other).TableRef)
|
|
and TableRef.Equals(TffSqlDELETE(Other).TableRef)))
|
|
and (BothNil(CondExpWhere, TffSqlDELETE(Other).CondExpWhere)
|
|
or (BothNonNil(CondExpWhere, TffSqlDELETE(Other).CondExpWhere)
|
|
and CondExpWhere.Equals(TffSqlDELETE(Other).CondExpWhere)));
|
|
end;
|
|
|
|
function TffSqlDELETE.Execute(var RowsAffected: Integer) : TffResult; {!!.11}
|
|
var
|
|
i: Integer;
|
|
Pos: TffInt64;
|
|
begin
|
|
Result := Owner.FDatabase.StartTransaction([T]);
|
|
if Result = DBIERR_NONE then
|
|
try
|
|
if not Bound then
|
|
Bind;
|
|
{Begin !!.11}
|
|
Result := T.EnsureWritable;
|
|
if Result <> DBIERR_NONE then begin
|
|
Owner.FDatabase.AbortTransaction;
|
|
Exit;
|
|
end;
|
|
{End !!.11}
|
|
RowsAffected := 0;
|
|
if Joiner = nil then begin
|
|
Joiner := TffSqlJoiner.Create(Owner, CondExpWhere);
|
|
Joiner.Sources.Add(TFFSqlTableProxySubset.Create(T));
|
|
end;
|
|
|
|
Joiner.ClearColumnList;
|
|
|
|
Joiner.Target := nil;
|
|
DeleteList := TList.Create;
|
|
try
|
|
Joiner.Execute(Owner.UseIndex, DeleteRecord, jmNone);
|
|
T.SetIndex(-1); {switch to raw record id index} {!!.11}
|
|
i := 0;
|
|
while (Result = DBIERR_NONE) and {!!.11}
|
|
(i < DeleteList.Count) do begin {!!.11}
|
|
Pos.iLow := TffWord32(DeleteList[i]);
|
|
inc(i);
|
|
Assert(i < DeleteList.Count);
|
|
Pos.iHigh := TffWord32(DeleteList[i]);
|
|
inc(i);
|
|
T.GetRecordByID(Pos, ffsltExclusive); {!!.11}
|
|
Result := T.Delete; {!!.11}
|
|
if Result = DBIERR_NONE then {!!.11}
|
|
inc(RowsAffected); {!!.11}
|
|
end;
|
|
// RowsAffected := DeleteList.Count div 2; {Deleted !!.11}
|
|
finally
|
|
DeleteList.Free;
|
|
end;
|
|
{Begin !!.11}
|
|
if Result = DBIERR_NONE then
|
|
Owner.FDatabase.Commit
|
|
else
|
|
Owner.FDatabase.AbortTransaction;
|
|
{End !!.11}
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
RowsAffected := 0;
|
|
raise;
|
|
end
|
|
else if Result = DBIERR_LOCKED then
|
|
FFRaiseException(EffException, ffStrResServer, fferrLockRejected,
|
|
[ffcLockExclusive, '', T.Name])
|
|
else
|
|
FFRaiseException(EffException, ffStrResServer, Result, [T.Name]);
|
|
end;
|
|
{--------}
|
|
|
|
{!!.11 new}
|
|
function TffSqlDELETE.Reduce: Boolean;
|
|
begin
|
|
if TableRef <> nil then
|
|
if TableRef.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if CondExpWhere <> nil then
|
|
if CondExpWhere.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
{ TffSqlUPDATE }
|
|
|
|
procedure TffSqlUPDATE.AddColumns(Node: TffSqlNode);
|
|
begin
|
|
Node.AddColumnDef(Self);
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlUPDATE then begin
|
|
Clear;
|
|
if TffSqlUPDATE(Source).TableRef <> nil then begin
|
|
TableRef := TffSqlTableRef.Create(Self);
|
|
TableRef.Assign(TffSqlUPDATE(Source).TableRef);
|
|
end;
|
|
if TffSqlUPDATE(Source).UpdateList <> nil then begin
|
|
UpdateList := TffSqlUpdateList.Create(Self);
|
|
UpdateList.Assign(TffSqlUPDATE(Source).UpdateList);
|
|
end;
|
|
if TffSqlUPDATE(Source).CondExpWhere <> nil then begin
|
|
CondExpWhere := TffSqlCondExp.Create(Self);
|
|
CondExpWhere.Assign(TffSqlUPDATE(Source).CondExpWhere);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.Bind;
|
|
var
|
|
i: Integer;
|
|
F: TFFSqlFieldProxy;
|
|
begin
|
|
Assert(UpdateList <> nil);
|
|
UpdateList.EnumNodes(ClearBindings, False);
|
|
T := TableRef.GetTable(Self, False); {!!.11}
|
|
if T = nil then
|
|
SQLError('Unable to open table: ' + TableRef.SQLName + //TableName +
|
|
'. Ensure the table exists and is not in use by ' +
|
|
'another process.');
|
|
|
|
{build column list}
|
|
Assert(Assigned(Columns));
|
|
Columns.Clear;
|
|
UpdateList.EnumNodes(AddColumns, False);
|
|
Assert(Columns.Count > 0);
|
|
for i := 0 to Columns.Count - 1 do begin
|
|
F := T.FieldByName(Columns[i]);
|
|
if F = nil then
|
|
SQLError('Unknown field for table ' + TableRef.SQLName + 'in UPDATE statement:' +
|
|
Columns[i]);
|
|
|
|
if not F.CanUpdate then
|
|
SQLError('Changing fields of this type is not currently supported through SQL:' +
|
|
Columns[i]);
|
|
|
|
TffSqlUpdateItem(Columns.Objects[i]).F := F;
|
|
with TffSqlUpdateItem(Columns.Objects[i]) do
|
|
if Simplex <> nil then
|
|
Simplex.MatchType(F.GetType);
|
|
|
|
end;
|
|
if CondExpWhere <> nil then
|
|
CondExpWhere.MatchType(fftBoolean);
|
|
Bound := True;
|
|
end;
|
|
|
|
function TffSqlUPDATE.BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
Result := nil;
|
|
Assert(T <> nil);
|
|
Assert(T is TffSqlTableProxy);
|
|
if T.FieldByName(FieldName) <> nil then begin
|
|
Result := T.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
SQLError('Unknown field:' + FieldName);
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.Clear;
|
|
begin
|
|
TableRef.Free;
|
|
TableRef := nil;
|
|
UpdateList.Free;
|
|
UpdateList := nil;
|
|
CondExpWhere.Free;
|
|
CondExpWhere := nil;
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.ClearBindings(Node: TffSqlNode);
|
|
begin
|
|
Node.ClearBinding;
|
|
end;
|
|
|
|
destructor TffSqlUPDATE.Destroy;
|
|
begin
|
|
if T <> nil then
|
|
if T.Owner = Self then begin
|
|
T.Owner := nil;
|
|
T.Free;
|
|
end;
|
|
Clear;
|
|
Joiner.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, 'UPDATE ');
|
|
TableRef.EmitSQL(Stream);
|
|
WriteStr(Stream,' SET ');
|
|
if assigned(UpdateList) then
|
|
UpdateList.EmitSQL(Stream);
|
|
if assigned(CondExpWhere) then
|
|
CondExpWhere.EmitSQL(Stream);
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(TableRef) then
|
|
TableRef.EnumNodes(EnumMethod, Deep);
|
|
if assigned(UpdateList) then
|
|
UpdateList.EnumNodes(EnumMethod, Deep);
|
|
if assigned(CondExpWhere) then
|
|
CondExpWhere.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlUPDATE.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlUPDATE)
|
|
and (BothNil(TableRef, TffSqlUPDATE(Other).TableRef)
|
|
or (BothNonNil(TableRef, TffSqlUPDATE(Other).TableRef)
|
|
and UpdateList.Equals(TffSqlUPDATE(Other).UpdateList)))
|
|
and (BothNil(UpdateList, TffSqlUPDATE(Other).UpdateList)
|
|
or (BothNonNil(UpdateList, TffSqlUPDATE(Other).UpdateList)
|
|
and UpdateList.Equals(TffSqlUPDATE(Other).UpdateList)))
|
|
and (BothNil(CondExpWhere, TffSqlUPDATE(Other).CondExpWhere)
|
|
or (BothNonNil(CondExpWhere, TffSqlUPDATE(Other).CondExpWhere)
|
|
and CondExpWhere.Equals(TffSqlUPDATE(Other).CondExpWhere)));
|
|
end;
|
|
|
|
function TffSqlUPDATE.Execute(var RowsAffected: Integer) : TffResult; {!!.11}
|
|
var
|
|
i: Integer;
|
|
Pos: TffInt64;
|
|
begin
|
|
Result := Owner.FDatabase.StartTransaction([T]);
|
|
if Result = DBIERR_NONE then
|
|
try
|
|
if not Bound then
|
|
Bind;
|
|
{Begin !!.11}
|
|
Result := T.EnsureWritable;
|
|
if Result <> DBIERR_NONE then begin
|
|
Owner.FDatabase.AbortTransaction;
|
|
Exit;
|
|
end;
|
|
{End !!.11}
|
|
FRowsAffected := 0;
|
|
if Joiner = nil then begin
|
|
Joiner := TffSqlJoiner.Create(Owner, CondExpWhere);
|
|
Joiner.Sources.Add(
|
|
TFFSqlTableProxySubset.Create(
|
|
TFFSqlTableProxy(T)));
|
|
end;
|
|
|
|
Joiner.ClearColumnList;
|
|
|
|
Joiner.Target := nil;
|
|
UpdateRecList := TList.Create;
|
|
try
|
|
Joiner.Execute(Owner.UseIndex, UpdateRecord, jmNone);
|
|
T.SetIndex(-1); {switch to raw record id index} {!!.11}
|
|
i := 0;
|
|
while (Result = DBIERR_NONE) and {!!.11}
|
|
(i < UpdateRecList.Count) do begin {!!.11}
|
|
Pos.iLow := TffWord32(UpdateRecList[i]);
|
|
inc(i);
|
|
Assert(i < UpdateRecList.Count);
|
|
Pos.iHigh := TffWord32(UpdateRecList[i]);
|
|
inc(i);
|
|
T.GetRecordByID(Pos, ffsltExclusive); {!!.11}
|
|
Result := UpdateList.Update; {!!.11}
|
|
if Result = DBIERR_NONE then {!!.11}
|
|
inc(FRowsAffected);
|
|
end;
|
|
finally
|
|
UpdateRecList.Free;
|
|
end;
|
|
{Begin !!.11}
|
|
if Result = DBIERR_NONE then begin
|
|
Owner.FDatabase.Commit;
|
|
RowsAffected := FRowsAffected;
|
|
end
|
|
else
|
|
Owner.FDatabase.AbortTransaction;
|
|
{End !!.11}
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
RowsAffected := 0;
|
|
raise;
|
|
end
|
|
else if Result = DBIERR_LOCKED then
|
|
FFRaiseException(EffException, ffStrResServer, fferrLockRejected,
|
|
[ffcLockExclusive, '', T.Name])
|
|
else
|
|
FFRaiseException(EffException, ffStrResServer, Result, [T.Name]);
|
|
end;
|
|
{--------}
|
|
|
|
{!!.11 new}
|
|
function TffSqlUPDATE.Reduce: Boolean;
|
|
begin
|
|
if TableRef <> nil then
|
|
if TableRef.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if CondExpWhere <> nil then
|
|
if CondExpWhere.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if UpdateList <> nil then
|
|
if UpdateList.Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlUPDATE.UpdateRecord;
|
|
var
|
|
Pos: TffInt64;
|
|
begin
|
|
Pos := T.GetCurrentRecordID;
|
|
UpdateRecList.Add(Pointer(Pos.iLow));
|
|
UpdateRecList.Add(Pointer(Pos.iHigh));
|
|
end;
|
|
|
|
{ TffSqlUpdateItem }
|
|
|
|
procedure TffSqlUpdateItem.AddColumnDef(Target: TffSqlColumnListOwner);
|
|
begin
|
|
Target.Columns.AddObject(ColumnName, Self);
|
|
end;
|
|
|
|
procedure TffSqlUpdateItem.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlUpdateItem then begin
|
|
Simplex.Free;
|
|
Simplex := nil;
|
|
ColumnName := TffSqlUpdateItem(Source).ColumnName;
|
|
Default := TffSqlUpdateItem(Source).Default;
|
|
if TffSqlUpdateItem(Source).Simplex <> nil then begin
|
|
Simplex := TffSqlSimpleExpression.Create(Self);
|
|
Simplex.Assign(TffSqlUpdateItem(Source).Simplex);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
destructor TffSqlUpdateItem.Destroy;
|
|
begin
|
|
Simplex.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlUpdateItem.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ColumnName);
|
|
WriteStr(Stream,' = ');
|
|
if Default then
|
|
WriteStr(Stream, 'DEFAULT ')
|
|
else
|
|
if Simplex = nil then
|
|
WriteStr(Stream, 'NULL ')
|
|
else
|
|
Simplex.EmitSQL(Stream);
|
|
end;
|
|
|
|
procedure TffSqlUpdateItem.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if Simplex <> nil then
|
|
Simplex.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlUpdateItem.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
(Other is TffSqlUpdateItem)
|
|
and (ColumnName = TffSqlUpdateItem(Other).ColumnName)
|
|
and (Default = TffSqlUpdateItem(Other).Default)
|
|
and (BothNil(Simplex, TffSqlUpdateItem(Other).Simplex)
|
|
or (BothNonNil(Simplex, TffSqlUpdateItem(Other).Simplex)
|
|
and Simplex.Equals(TffSqlUpdateItem(Other).Simplex)));
|
|
end;
|
|
|
|
function TffSqlUpdateItem.Reduce: Boolean;
|
|
begin
|
|
Result := (Simplex <> nil) and Simplex.Reduce;
|
|
end;
|
|
|
|
procedure TffSqlUpdateItem.Update;
|
|
begin
|
|
Assert(F <> nil);
|
|
if Simplex <> nil then
|
|
F.SetValue(Simplex.GetValue)
|
|
else
|
|
F.SetFieldToNull;
|
|
end;
|
|
|
|
{ TffSqlUpdateList }
|
|
|
|
function TffSqlUpdateList.AddItem(
|
|
NewValue: TffSqlUpdateItem): TffSqlUpdateItem;
|
|
begin
|
|
FUpdateItemList.Add(NewValue);
|
|
Result := NewValue;
|
|
end;
|
|
|
|
procedure TffSqlUpdateList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
if Source is TffSqlValueList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlValueList(Source).ValueCount) do
|
|
AddItem(TffSqlUpdateItem.Create(Self)).Assign(
|
|
TffSqlValueList(Source).ValueItem[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlUpdateList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(UpdateCount) do
|
|
UpdateItem[i].Free;
|
|
FUpdateItemList.Clear;
|
|
end;
|
|
|
|
constructor TffSqlUpdateList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited;
|
|
FUpdateItemList := TList.Create;
|
|
end;
|
|
|
|
destructor TffSqlUpdateList.Destroy;
|
|
begin
|
|
Clear;
|
|
FUpdateItemList.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlUpdateList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
First: Boolean;
|
|
begin
|
|
First := True;
|
|
for i := 0 to pred(UpdateCount) do begin
|
|
if First then
|
|
First := False
|
|
else
|
|
WriteStr(Stream, ', ');
|
|
UpdateItem[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlUpdateList.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(UpdateCount) do
|
|
UpdateItem[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlUpdateList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlValueList then begin
|
|
if UpdateCount <> TffSqlUpdateList(Other).UpdateCount then
|
|
exit;
|
|
for i := 0 to pred(UpdateCount) do
|
|
if not UpdateItem[i].Equals(TffSqlUpdateList(Other).UpdateItem[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlUpdateList.GetUpdateCount: Integer;
|
|
begin
|
|
Result := FUpdateItemList.Count;
|
|
end;
|
|
|
|
function TffSqlUpdateList.GetUpdateItem(Index: Integer): TffSqlUpdateItem;
|
|
begin
|
|
Result := TffSqlUpdateItem(FUpdateItemList[Index]);
|
|
end;
|
|
|
|
{!!.11 new}
|
|
function TffSqlUpdateList.Reduce: Boolean;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to UpdateCount - 1 do
|
|
if UpdateItem[i].Reduce then begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
function TffSqlUpdateList.Update : TffResult; {!!.11}
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to UpdateCount - 1 do
|
|
UpdateItem[i].Update;
|
|
Assert(Parent <> nil);
|
|
Assert(TObject(Parent) is TffSqlUpdate);
|
|
Result := TffSqlUpdate(Parent).T.Update; {!!.11}
|
|
end;
|
|
|
|
{ TffSqlColumnListOwner }
|
|
|
|
constructor TffSqlColumnListOwner.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited;
|
|
Columns := TStringList.Create;
|
|
end;
|
|
|
|
destructor TffSqlColumnListOwner.Destroy;
|
|
begin
|
|
Columns.Free;
|
|
inherited;
|
|
end;
|
|
|
|
{ TffSqlNonJoinTablePrimary }
|
|
|
|
procedure TffSqlNonJoinTablePrimary.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlNonJoinTablePrimary then begin
|
|
Clear;
|
|
if TffSqlNonJoinTablePrimary(Source).SelectSt <> nil then begin
|
|
SelectSt := TFFSqlSELECT.Create(Self);
|
|
SelectSt.Assign(TffSqlNonJoinTablePrimary(Source).SelectSt);
|
|
end;
|
|
if TffSqlNonJoinTablePrimary(Source).ValueList <> nil then begin
|
|
ValueList := TffSqlValueList.Create(Self);
|
|
ValueList.Assign(TffSqlNonJoinTablePrimary(Source).ValueList);
|
|
end;
|
|
if TffSqlNonJoinTablePrimary(Source).NonJoinTableExp <> nil then begin
|
|
NonJoinTableExp := TffSqlNonJoinTableExp.Create(Self);
|
|
NonJoinTableExp.Assign(TffSqlNonJoinTablePrimary(Source).NonJoinTableExp);
|
|
end;
|
|
if TffSqlNonJoinTablePrimary(Source).TableRef <> nil then begin
|
|
TableRef := TffSqlTableRef.Create(Self);
|
|
TableRef.Assign(TffSqlNonJoinTablePrimary(Source).TableRef);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
if SelectSt <> nil then
|
|
Result := SelectSt.BindField(TableName, FieldName)
|
|
else
|
|
if NonJoinTableExp <> nil then
|
|
Result := NonJoinTableExp.BindFieldDown(TableName, FieldName)
|
|
else
|
|
if TableRef <> nil then
|
|
Result := TableRef.BindFieldDown(TableName, FieldName)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
if SelectSt <> nil then
|
|
Result := SelectSt.BindTable(AOwner, TableName)
|
|
else
|
|
if NonJoinTableExp <> nil then
|
|
Result := NonJoinTableExp.BindTable(AOwner, TableName)
|
|
else
|
|
if TableRef <> nil then
|
|
Result := TableRef.BindTable(AOwner, TableName)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTablePrimary.Clear;
|
|
begin
|
|
SelectSt.Free;
|
|
SelectSt := nil;
|
|
ValueList.Free;
|
|
ValueList := nil;
|
|
NonJoinTableExp.Free;
|
|
NonJoinTableExp := nil;
|
|
TableRef.Free;
|
|
TableRef := nil;
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.DependsOn(
|
|
Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if SelectSt <> nil then
|
|
Result := SelectSt.DependsOn(Table)
|
|
else
|
|
if NonJoinTableExp <> nil then
|
|
Result := NonJoinTableExp.DependsOn(Table)
|
|
else
|
|
if TableRef <> nil then
|
|
Result := TableRef.DependsOn(Table)
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
destructor TffSqlNonJoinTablePrimary.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTablePrimary.EmitSQL(Stream: TStream);
|
|
begin
|
|
if SelectSt <> nil then
|
|
SelectSt.EmitSQL(Stream);
|
|
if ValueList <> nil then
|
|
ValueList.EmitSQL(Stream);
|
|
if NonJoinTableExp <> nil then begin
|
|
WriteStr(Stream,' (');
|
|
NonJoinTableExp.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end;
|
|
if TableRef <> nil then begin
|
|
WriteStr(Stream,' TABLE ');
|
|
TableRef.EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTablePrimary.EnsureResultTable(NeedData: Boolean);
|
|
begin
|
|
if SelectSt <> nil then
|
|
SelectSt.EnsureResultTable(NeedData);
|
|
if NonJoinTableExp <> nil then
|
|
NonJoinTableExp.EnsureResultTable(NeedData);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTablePrimary.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if SelectSt <> nil then
|
|
SelectSt.EnumNodes(EnumMethod, Deep);
|
|
if ValueList <> nil then
|
|
ValueList.EnumNodes(EnumMethod, Deep);
|
|
if NonJoinTableExp <> nil then
|
|
NonJoinTableExp.EnumNodes(EnumMethod, Deep);
|
|
if TableRef <> nil then
|
|
TableRef.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
Other is TffSqlNonJoinTablePrimary
|
|
and ((BothNil(SelectSt, TffSqlNonJoinTablePrimary(Other).SelectSt)
|
|
or (BothNonNil(SelectSt, TffSqlNonJoinTablePrimary(Other).SelectSt)
|
|
and SelectSt.Equals(TffSqlNonJoinTablePrimary(Other).SelectSt))))
|
|
and ((BothNil(ValueList, TffSqlNonJoinTablePrimary(Other).ValueList)
|
|
or (BothNonNil(ValueList, TffSqlNonJoinTablePrimary(Other).ValueList)
|
|
and ValueList.Equals(TffSqlNonJoinTablePrimary(Other).ValueList))))
|
|
and ((BothNil(NonJoinTableExp, TffSqlNonJoinTablePrimary(Other).NonJoinTableExp)
|
|
or (BothNonNil(NonJoinTableExp, TffSqlNonJoinTablePrimary(Other).NonJoinTableExp)
|
|
and NonJoinTableExp.Equals(TffSqlNonJoinTablePrimary(Other).NonJoinTableExp))))
|
|
and ((BothNil(TableRef, TffSqlNonJoinTablePrimary(Other).TableRef)
|
|
or (BothNonNil(TableRef, TffSqlNonJoinTablePrimary(Other).TableRef)
|
|
and TableRef.Equals(TffSqlNonJoinTablePrimary(Other).TableRef))));
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTablePrimary.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
begin
|
|
if assigned(SelectSt) then
|
|
SelectSt.Execute(aLiveResult, aCursorID, RecordsRead)
|
|
else
|
|
if assigned(ValueList) then
|
|
ValueList.Execute(aLiveResult, aCursorID, RecordsRead)
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
NonJoinTableExp.Execute(aLiveResult, aCursorID, RecordsRead)
|
|
else
|
|
if assigned(TableRef) then
|
|
TableRef.Execute(aLiveResult, aCursorID, RecordsRead)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.GetResultTable: TffSqlTableProxy;
|
|
begin
|
|
Result := nil;
|
|
if assigned(SelectSt) then
|
|
Result := SelectSt.ResultTable
|
|
else
|
|
if assigned(ValueList) then
|
|
Result := ValueList.ResultTable
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.ResultTable
|
|
else
|
|
if assigned(TableRef) then
|
|
Result := TableRef.ResultTable
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.Reduce: Boolean;
|
|
begin
|
|
Result := False;
|
|
if assigned(SelectSt) then
|
|
Result := SelectSt.Reduce
|
|
else
|
|
if assigned(ValueList) then
|
|
Result := ValueList.Reduce
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.Reduce
|
|
else
|
|
if assigned(TableRef) then
|
|
Result := False //TableRef.Reduce
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
function TffSqlNonJoinTablePrimary.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
begin
|
|
Result := nil;
|
|
if assigned(SelectSt) then
|
|
Result := SelectSt.TargetFieldFromSourceField(F)
|
|
else
|
|
if assigned(ValueList) then
|
|
Result := nil
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.TargetFieldFromSourceField(F)
|
|
else
|
|
if assigned(TableRef) then
|
|
Result := TableRef.TargetFieldFromSourceField(F)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
{ TffSqlTableExp }
|
|
|
|
procedure TffSqlTableExp.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlTableExp then begin
|
|
Clear;
|
|
if TffSqlTableExp(Source).NestedTableExp <> nil then begin
|
|
NestedTableExp := TffSqlTableExp.Create(Self);
|
|
NestedTableExp.Assign(TffSqlTableExp(Source).NestedTableExp);
|
|
end;
|
|
if TffSqlTableExp(Source).JoinTableExp <> nil then begin
|
|
JoinTableExp := TffSqlJoinTableExp.Create(Self);
|
|
JoinTableExp.Assign(TffSqlTableExp(Source).JoinTableExp);
|
|
end;
|
|
if TffSqlTableExp(Source).NonJoinTableExp <> nil then begin
|
|
NonJoinTableExp := TffSqlNonJoinTableExp.Create(Self);
|
|
NonJoinTableExp.Assign(TffSqlTableExp(Source).NonJoinTableExp);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlTableExp.Clear;
|
|
begin
|
|
NestedTableExp.Free;
|
|
NestedTableExp := nil;
|
|
JoinTableExp.Free;
|
|
JoinTableExp := nil;
|
|
NonJoinTableExp.Free;
|
|
NonJoinTableExp := nil;
|
|
end;
|
|
|
|
destructor TffSqlTableExp.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlTableExp.EmitSQL(Stream: TStream);
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
NestedTableExp.EmitSQL(Stream);
|
|
if assigned(JoinTableExp) then
|
|
JoinTableExp.EmitSQL(Stream);
|
|
if assigned(NonJoinTableExp) then
|
|
NonJoinTableExp.EmitSQL(Stream);
|
|
end;
|
|
|
|
function TffSqlTableExp.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.BindFieldDown(TableName, FieldName)
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.BindFieldDown(TableName, FieldName)
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.BindFieldDown(TableName, FieldName)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TffSqlTableExp.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.BindTable(AOwner, TableName)
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.BindTable(AOwner, TableName)
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.BindTable(AOwner, TableName)
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TffSqlTableExp.CheckNoDups: Boolean;
|
|
begin
|
|
EnsureResultTable(True);
|
|
Result := not ResultTable.HasDuplicates(True); {!!.13}
|
|
end;
|
|
|
|
function TffSqlTableExp.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.DependsOn(Table)
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.DependsOn(Table)
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.DependsOn(Table)
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TffSqlTableExp.EnsureResultTable(NeedData: Boolean);
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
NestedTableExp.EnsureResultTable(NeedData);
|
|
if assigned(JoinTableExp) then
|
|
JoinTableExp.EnsureResultTable(NeedData);
|
|
if assigned(NonJoinTableExp) then
|
|
NonJoinTableExp.EnsureResultTable(NeedData);
|
|
end;
|
|
|
|
procedure TffSqlTableExp.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(NestedTableExp) then
|
|
NestedTableExp.EnumNodes(EnumMethod, Deep);
|
|
if assigned(JoinTableExp) then
|
|
JoinTableExp.EnumNodes(EnumMethod, Deep);
|
|
if assigned(NonJoinTableExp) then
|
|
NonJoinTableExp.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlTableExp.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
Other is TffSqlTableExp
|
|
and ((BothNil(NestedTableExp, TffSqlTableExp(Other).NestedTableExp)
|
|
or (BothNonNil(NestedTableExp, TffSqlTableExp(Other).NestedTableExp)
|
|
and NestedTableExp.Equals(TffSqlTableExp(Other).NestedTableExp))))
|
|
and ((BothNil(JoinTableExp, TffSqlTableExp(Other).JoinTableExp)
|
|
or (BothNonNil(JoinTableExp, TffSqlTableExp(Other).JoinTableExp)
|
|
and JoinTableExp.Equals(TffSqlTableExp(Other).JoinTableExp))))
|
|
and ((BothNil(NonJoinTableExp, TffSqlTableExp(Other).NonJoinTableExp)
|
|
or (BothNonNil(NonJoinTableExp, TffSqlTableExp(Other).NonJoinTableExp)
|
|
and NonJoinTableExp.Equals(TffSqlTableExp(Other).NonJoinTableExp))));
|
|
end;
|
|
|
|
procedure TffSqlTableExp.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
NestedTableExp.Execute(aLiveResult, aCursorID, RecordsRead);
|
|
if assigned(JoinTableExp) then
|
|
JoinTableExp.Execute(aLiveResult, aCursorID, RecordsRead);
|
|
if assigned(NonJoinTableExp) then
|
|
NonJoinTableExp.Execute(aLiveResult, aCursorID, RecordsRead);
|
|
end;
|
|
|
|
{!!.11 new}
|
|
function TffSqlTableExp.GetFieldsFromTable(const TableName: string; List: TList):
|
|
TffSqlTableProxy;
|
|
{-returns fields from table that are ultimately coming from the table
|
|
specified in the TableName argument. NIL if not found.}
|
|
begin
|
|
Result := nil;
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.GetFieldsFromTable(TableName, List)
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.GetFieldsFromTable(TableName, List)
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.GetFieldsFromTable(TableName, List)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
function TffSqlTableExp.GetResultTable: TFFSqlTableProxy;
|
|
begin
|
|
Result := nil;
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.ResultTable
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.ResultTable
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
Result := NonJoinTableExp.ResultTable
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
function TffSqlTableExp.Reduce: Boolean;
|
|
begin
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.Reduce
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.Reduce
|
|
else
|
|
Result := False;
|
|
if assigned(NonJoinTableExp) then
|
|
Result := Result or NonJoinTableExp.Reduce;
|
|
end;
|
|
|
|
function TffSqlTableExp.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
begin
|
|
Result := nil;
|
|
if assigned(NestedTableExp) then
|
|
Result := NestedTableExp.TargetFieldFromSourceField(F)
|
|
else
|
|
if assigned(JoinTableExp) then
|
|
Result := JoinTableExp.TargetFieldFromSourceField(F)
|
|
else
|
|
if assigned(NonJoinTableExp) then
|
|
NonJoinTableExp.TargetFieldFromSourceField(F)
|
|
else
|
|
Assert(False);
|
|
end;
|
|
|
|
{ TffSqlJoinTableExp }
|
|
|
|
function TffSqlJoinTableExp.BuildSimpleFieldExpr(AOwner: TffSqlNode;
|
|
const ATableName, AFieldName: string; AField: TffSqlFieldProxy
|
|
): TffSqlSimpleExpression;
|
|
var
|
|
Term: TffSqlTerm;
|
|
Fact: TffSqlFactor;
|
|
FieldRef: TffSqlFieldRef;
|
|
begin
|
|
Result := TffSqlSimpleExpression.Create(AOwner);
|
|
Term := TffSqlTerm.Create(Result);
|
|
Fact := TffSqlFactor.Create(Term);
|
|
FieldRef := TffSqlFieldRef.Create(Fact);
|
|
FieldRef.TableName := ATableName;
|
|
FieldRef.FieldName := AFieldName;
|
|
FieldRef.FField := AField;
|
|
Fact.FieldRef := FieldRef;
|
|
Term.AddFactor(Fact);
|
|
Result.AddTerm(Term);
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.ClearColumns;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Columns = nil then exit;
|
|
for i := 0 to Columns.Count - 1 do
|
|
if TObject(Columns.Objects[i]) is TffSqlSimpleExpression then
|
|
TObject(Columns.Objects[i]).Free;
|
|
Columns.Clear;
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.Bind;
|
|
var
|
|
i, j : Integer;
|
|
FL, FR: TffSqlFieldProxy;
|
|
lCondTerm: TffSqlCondTerm;
|
|
lCondFact: TffSqlCondFactor;
|
|
lCondPrim: TffSqlCondPrimary;
|
|
lSimp1, lSimp2, cSimp, cSimp1, cSimp2: TffSqlSimpleExpression;
|
|
cTerm: TffSqlTerm;
|
|
cFact: TffSqlFactor;
|
|
cScalar : TffSqlScalarFunc;
|
|
cCoalesce : TffSqlCoalesceExpression;
|
|
S: string; {!!.11}
|
|
OS: TffSqlSELECT;
|
|
CF, NewCF: TffSqlCondFactor;
|
|
CP: TffSqlCondPrimary;
|
|
const
|
|
UorN: array[Boolean] of string = ('UNION', 'NATURAL');
|
|
begin
|
|
if JoinType = jtUnion then
|
|
SQLError('UNION JOIN is not currently supported by FlashFiler SQL');
|
|
if Natural and (JoinType = jtUnion) then
|
|
SQLError('NATURAL and UNION cannot both be specified on a JOIN');
|
|
if Natural or (JoinType = jtUnion) then begin
|
|
if CondExp <> nil then
|
|
SQLError(UorN[Natural] + ' joins do not accept an ON clause');
|
|
if UsingList <> nil then
|
|
sQLError(UorN[Natural] + ' joins do not accept a USING clause');
|
|
end;
|
|
if not Natural and not (JoinType in [jtCross,jtUnion]) then begin
|
|
if (CondExp = nil) and (UsingList = nil) then
|
|
SQLError('The join must have either an ON or a USING clause');
|
|
end;
|
|
if CondExp <> nil then
|
|
CondExp.EnumNodes(ClearBindings, False);
|
|
Assert(assigned(TableRef1));
|
|
TL := TableRef1.BindTable(Self, TableRef1.TableName);
|
|
Assert(assigned(TL));
|
|
Assert(assigned(TableRef2));
|
|
TR := TableRef2.BindTable(Self, TableRef2.TableName);
|
|
Assert(assigned(TR));
|
|
|
|
{build column list}
|
|
Assert(Assigned(Columns));
|
|
ClearColumns;
|
|
|
|
if Natural then begin
|
|
UsingCondExp := TffSqlCondExp.Create(Self);
|
|
lCondTerm := TffSqlCondTerm.Create(UsingCondExp);
|
|
for i := 0 to TL.FieldCount - 1 do begin
|
|
FL := TL.Field(i);
|
|
FR := TR.FieldByName(FL.Name);
|
|
if FR <> nil then begin
|
|
{common field}
|
|
lCondFact := TffSqlCondFactor.Create(lCondTerm);
|
|
lCondPrim := TffSqlCondPrimary.Create(lCondFact);
|
|
lSimp1 := BuildSimpleFieldExpr(lCondPrim, TableRef1.SQLName,
|
|
FL.Name, FL);
|
|
lSimp2 := BuildSimpleFieldExpr(lCondPrim, TableRef2.SQLName,
|
|
FR.Name, FR);
|
|
case JoinType of
|
|
jtRightOuter :
|
|
Columns.AddObject(FL.Name, FR);
|
|
jtFullOuter :
|
|
begin
|
|
cSimp := TffSqlSimpleExpression.Create(Self);
|
|
cTerm := TffSqlTerm.Create(cSimp);
|
|
cFact := TffSqlFactor.Create(cTerm);
|
|
cScalar := TffSqlScalarFunc.Create(cFact);
|
|
cScalar.SQLFunction := sfCoalesce;
|
|
cCoalesce := TffSqlCoalesceExpression.Create(cScalar);
|
|
cSimp1 := BuildSimpleFieldExpr(cCoalesce, TableRef1.SQLName,
|
|
FL.Name, FL);
|
|
cSimp2 := BuildSimpleFieldExpr(cCoalesce, TableRef2.SQLName,
|
|
FR.Name, FR);
|
|
cCoalesce.AddArg(cSimp1);
|
|
cCoalesce.AddArg(cSimp2);
|
|
cScalar.CoalesceExp := cCoalesce;
|
|
cFact.ScalarFunc := cScalar;
|
|
cTerm.AddFactor(cFact);
|
|
cSimp.AddTerm(cTerm);
|
|
Columns.AddObject(FL.Name, cSimp);
|
|
end;
|
|
else
|
|
Columns.AddObject(FL.Name, FL);
|
|
end;
|
|
lCondPrim.SimpleExp1 := lSimp1;
|
|
lCondPrim.SimpleExp2 := lSimp2;
|
|
lCondPrim.RelOp := roEQ;
|
|
lCondFact.CondPrimary := lCondPrim;
|
|
lCondTerm.AddCondFactor(lCondFact);
|
|
end;
|
|
end;
|
|
if lCondTerm.CondFactorCount = 0 then begin
|
|
lCondTerm.Free;
|
|
UsingCondExp.Free;
|
|
UsingCondExp := nil;
|
|
end else begin
|
|
UsingCondExp.AddCondTerm(lCondTerm);
|
|
UsingCondExp.MatchType(fftBoolean);
|
|
end;
|
|
for i := 0 to TL.FieldCount - 1 do begin
|
|
FL := TL.Field(i);
|
|
if Columns.IndexOf(FL.Name) = -1 then
|
|
Columns.AddObject(FL.Name, FL);
|
|
end;
|
|
for i := 0 to TR.FieldCount - 1 do begin
|
|
FR := TR.Field(i);
|
|
if Columns.IndexOf(FR.Name) = -1 then
|
|
Columns.AddObject(FR.Name, FR);
|
|
end;
|
|
end else
|
|
if UsingList <> nil then begin
|
|
UsingCondExp := TffSqlCondExp.Create(Self);
|
|
lCondTerm := TffSqlCondTerm.Create(UsingCondExp);
|
|
for i := 0 to UsingList.UsingCount - 1 do begin
|
|
lCondFact := TffSqlCondFactor.Create(lCondTerm);
|
|
lCondPrim := TffSqlCondPrimary.Create(lCondFact);
|
|
FL := TL.FieldByName(UsingList.UsingItem[i].ColumnName);
|
|
if FL = nil then
|
|
SQLError(format('Field %s does not exist in table %s.',
|
|
[UsingList.UsingItem[i].ColumnName, TableRef1.SQLName]));
|
|
FR := TR.FieldByName(UsingList.UsingItem[i].ColumnName);
|
|
if FR = nil then
|
|
SQLError(format('Field %s does not exist in table %s.',
|
|
[UsingList.UsingItem[i].ColumnName, TableRef2.SQLName]));
|
|
lSimp1 := BuildSimpleFieldExpr(lCondPrim, TableRef1.SQLName,
|
|
FL.Name, FL);
|
|
lSimp2 := BuildSimpleFieldExpr(lCondPrim, TableRef2.SQLName,
|
|
FR.Name, FR);
|
|
case JoinType of
|
|
jtRightOuter :
|
|
Columns.AddObject(FL.Name, FR);
|
|
jtFullOuter :
|
|
begin
|
|
cSimp := TffSqlSimpleExpression.Create(Self);
|
|
cTerm := TffSqlTerm.Create(cSimp);
|
|
cFact := TffSqlFactor.Create(cTerm);
|
|
cScalar := TffSqlScalarFunc.Create(cFact);
|
|
cScalar.SQLFunction := sfCoalesce;
|
|
cCoalesce := TffSqlCoalesceExpression.Create(cScalar);
|
|
cSimp1 := BuildSimpleFieldExpr(cCoalesce, TableRef1.SQLName,
|
|
FL.Name, FL);
|
|
cSimp2 := BuildSimpleFieldExpr(cCoalesce, TableRef2.SQLName,
|
|
FR.Name, FR);
|
|
cCoalesce.AddArg(cSimp1);
|
|
cCoalesce.AddArg(cSimp2);
|
|
cScalar.CoalesceExp := cCoalesce;
|
|
cFact.ScalarFunc := cScalar;
|
|
cTerm.AddFactor(cFact);
|
|
cSimp.AddTerm(cTerm);
|
|
Columns.AddObject(FL.Name, cSimp);
|
|
end;
|
|
else
|
|
Columns.AddObject(FL.Name, FL);
|
|
end;
|
|
lCondPrim.SimpleExp1 := lSimp1;
|
|
lCondPrim.SimpleExp2 := lSimp2;
|
|
lCondPrim.RelOp := roEQ;
|
|
lCondFact.CondPrimary := lCondPrim;
|
|
lCondTerm.AddCondFactor(lCondFact);
|
|
end;
|
|
UsingCondExp.AddCondTerm(lCondTerm);
|
|
(*
|
|
{!!.11 begin}
|
|
{if this join is enclosed in a SELECT with a WHERE clause,
|
|
and if the WHERE clause consists only of a single conditional term,
|
|
and if any of the conditional factors limit either side of the join,
|
|
then copy those conditional factors into the join condition}
|
|
//writeln(SqlText);
|
|
//writeln(' ',CondExp.SqlText);
|
|
OS := OwnerSelect;
|
|
if (OS <> nil)
|
|
and (OS.CondExpWhere <> nil)
|
|
and (OS.CondExpWhere.CondTermCount = 1) then begin
|
|
for i := 0 to OS.CondExpWhere.CondTerm[0].CondFactorCount - 1 do begin
|
|
CF := OS.CondExpWhere.CondTerm[0].CondFactor[i];
|
|
//writeln(' ',CF.SqlText);
|
|
if not CF.IsConstant
|
|
and not CF.UnaryNot then begin
|
|
CP := CF.CondPrimary;
|
|
if CP.RelOp in [roEQ, roLE, roL, roG, roGE] then begin
|
|
if CP.SimpleExp2.IsConstant or CP.SimpleExp2.IsParameter then begin
|
|
if Cp.SimpleExp1.TermCount = 1 then
|
|
if Cp.SimpleExp1.Term[0].FactorCount = 1 then
|
|
if Cp.SimpleExp1.Term[0].Factor[0].FieldRef <> nil then
|
|
if (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef1.TableName)
|
|
or (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef1.Alias) then begin
|
|
//writeln(' found left constraint:', CP.SqlText);
|
|
NewCF := TffSqlCondFactor.Create(lCondTerm);
|
|
NewCF.Assign(CF);
|
|
lCondTerm.AddCondFactor(NewCF);
|
|
//writeln(' ',CondExp.SqlText);
|
|
end
|
|
else
|
|
if Cp.SimpleExp1.Term[0].Factor[0].FieldRef <> nil then
|
|
if (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef2.TableName)
|
|
or (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef2.Alias) then begin
|
|
//writeln(' found right constraint', CP.SqlText);
|
|
NewCF := TffSqlCondFactor.Create(lCondTerm);
|
|
NewCF.Assign(CF);
|
|
lCondTerm.AddCondFactor(NewCF);
|
|
//writeln(' ',CondExp.SqlText);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
{!!.11 end}
|
|
*)
|
|
UsingCondExp.MatchType(fftBoolean);
|
|
for i := 0 to TL.FieldCount - 1 do begin
|
|
FL := TL.Field(i);
|
|
if Columns.IndexOf(FL.Name) = -1 then
|
|
Columns.AddObject(FL.Name, FL);
|
|
end;
|
|
for i := 0 to TR.FieldCount - 1 do begin
|
|
FL := TR.Field(i);
|
|
j := Columns.IndexOf(FL.Name);
|
|
if j = -1 then
|
|
Columns.AddObject(FL.Name, FL)
|
|
else
|
|
if j >= UsingList.UsingCount then
|
|
Columns.AddObject(TR.Name + '.' + FL.Name, FL);
|
|
end;
|
|
end else begin
|
|
for i := 0 to TL.FieldCount - 1 do
|
|
Columns.AddObject(TL.Field(i).Name, TL.Field(i));
|
|
for i := 0 to TR.FieldCount - 1 do
|
|
if Columns.IndexOf(TR.Field(i).Name) = -1 then
|
|
Columns.AddObject(TR.Field(i).Name, TR.Field(i))
|
|
{!!.11 begin}
|
|
else begin
|
|
S := TR.Name + '.' + TR.Field(i).Name;
|
|
if Columns.IndexOf(S) = -1 then
|
|
Columns.AddObject(S, TR.Field(i))
|
|
else begin
|
|
j := 2;
|
|
while Columns.IndexOf(S + '_' + IntToStr(j)) <> -1 do
|
|
inc(j);
|
|
Columns.AddObject(S+ '_' + IntToStr(j), TR.Field(i));
|
|
end;
|
|
end;
|
|
{!!.11 end}
|
|
end;
|
|
|
|
if (CondExp <> nil) then begin
|
|
{!!.11 begin}
|
|
if (CondExp.CondTermCount = 1) then begin
|
|
{if this join is enclosed in a SELECT with a WHERE clause,
|
|
and if the WHERE clause consists only of a single conditional term,
|
|
and if any of the conditional factors limit either side of the join,
|
|
then copy those conditional factors into the join condition}
|
|
//writeln(SqlText);
|
|
//writeln(' ',CondExp.SqlText);
|
|
OS := OwnerSelect;
|
|
if (OS <> nil)
|
|
and (OS.CondExpWhere <> nil)
|
|
and (OS.CondExpWhere.CondTermCount = 1) then begin
|
|
for i := 0 to OS.CondExpWhere.CondTerm[0].CondFactorCount - 1 do begin
|
|
CF := OS.CondExpWhere.CondTerm[0].CondFactor[i];
|
|
//writeln(' ',CF.SqlText);
|
|
if not CF.IsConstant
|
|
and not CF.UnaryNot then begin
|
|
CP := CF.CondPrimary;
|
|
if CP.RelOp in [roEQ, roLE, roL, roG, roGE, roNE] then begin
|
|
if CP.SimpleExp2.IsConstant or CP.SimpleExp2.IsParameter then begin
|
|
if Cp.SimpleExp1.TermCount = 1 then
|
|
if Cp.SimpleExp1.Term[0].FactorCount = 1 then
|
|
if Cp.SimpleExp1.Term[0].Factor[0].FieldRef <> nil then
|
|
if (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName <> '') {!!.13}
|
|
and ( {!!.13}
|
|
(Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef1.TableName)
|
|
or (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef1.Alias)) then begin {!!.13}
|
|
//writeln(' found left constraint:', CP.SqlText);
|
|
NewCF := TffSqlCondFactor.Create(CondExp.CondTerm[0]);
|
|
NewCF.Assign(CF);
|
|
CondExp.CondTerm[0].AddCondFactor(NewCF);
|
|
//writeln(' ',CondExp.SqlText);
|
|
end
|
|
else
|
|
if Cp.SimpleExp1.Term[0].Factor[0].FieldRef <> nil then
|
|
if (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName <> '') {!!.13}
|
|
and ( {!!.13}
|
|
((Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef2.TableName)
|
|
or (Cp.SimpleExp1.Term[0].Factor[0].FieldRef.TableName
|
|
= TableRef2.Alias))) then begin {!!.13}
|
|
//writeln(' found right constraint', CP.SqlText);
|
|
NewCF := TffSqlCondFactor.Create(CondExp.CondTerm[0]);
|
|
NewCF.Assign(CF);
|
|
CondExp.CondTerm[0].AddCondFactor(NewCF);
|
|
//writeln(' ',CondExp.SqlText);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
{!!.11 end}
|
|
CondExp.MatchType(fftBoolean);
|
|
end;
|
|
|
|
Bound := True;
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
Result := TableRef1.BindTable(AOwner, TableName);
|
|
if Result = nil then
|
|
Result := TableRef2.BindTable(AOwner, TableName);
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.BindField(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
var
|
|
T: TFFSqlTableProxy;
|
|
begin
|
|
Result := nil;
|
|
if TableName <> '' then begin
|
|
T := TableRef1.BindTable(Self, TableName);
|
|
if T <> nil then
|
|
if T <> TL then begin
|
|
Result := TableRef1.TargetFieldFromSourceField(T.FieldByName(FieldName));
|
|
exit;
|
|
end;
|
|
if T = nil then begin
|
|
T := TableRef2.BindTable(Self, TableName);
|
|
if T <> nil then {!!.11}
|
|
if T <> TR then begin
|
|
Result := TableRef2.TargetFieldFromSourceField(T.FieldByName(FieldName));
|
|
exit;
|
|
end;
|
|
end;
|
|
if T = nil then
|
|
SQLError('Unknown field:' + TableName + '.' + FieldName);
|
|
|
|
Assert(T <> nil);
|
|
Result := T.FieldByName(FieldName);
|
|
if Result = nil then
|
|
SQLError('Unknown field:' + TableName + '.' + FieldName);
|
|
end else begin
|
|
if TL.FieldByName(FieldName) <> nil then begin
|
|
Result := TL.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
if TR.FieldByName(FieldName) <> nil then begin
|
|
Result := TR.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
SQLError('Unknown field:' + FieldName);
|
|
end;
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
if TableName <> '' then begin
|
|
Result := TableRef1.BindFieldDown(TableName, FieldName);
|
|
if Result = nil then
|
|
Result := TableRef2.BindFieldDown(TableName, FieldName);
|
|
if Result = nil then
|
|
exit;
|
|
|
|
EnsureResultTable(False{True});
|
|
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] = Result then begin
|
|
Result := FResultTable.Field(i);
|
|
exit;
|
|
end;
|
|
|
|
Result := nil;
|
|
end else begin
|
|
if TL.FieldByName(FieldName) <> nil then begin
|
|
Result := TL.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
if TR.FieldByName(FieldName) <> nil then begin
|
|
Result := TR.FieldByName(FieldName);
|
|
Exit;
|
|
end;
|
|
SQLError('Unknown field:' + FieldName);
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.ClearBindings(Node: TffSqlNode);
|
|
begin
|
|
Node.ClearBinding;
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
if not Bound then
|
|
Bind;
|
|
Result :=
|
|
((UsingCondExp <> nil) and UsingCondExp.DependsOn(Table))
|
|
or ((CondExp <> nil) and CondExp.DependsOn(Table));
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.DoJoin(NeedData: Boolean): TffSqlTableProxy;
|
|
var
|
|
i : Integer;
|
|
T2 : TffSqlTableProxy;
|
|
F : TffSqlFieldProxy;
|
|
N : TffSqlNode;
|
|
FieldDefList: TffSqlFieldDefList;
|
|
OuterJoinMode: TffSqlOuterJoinMode;
|
|
begin
|
|
|
|
{build a normal answer table}
|
|
|
|
{build field definition for answer table}
|
|
FieldDefList := TffSqlFieldDefList.Create;
|
|
try
|
|
Assert(Assigned(Columns));
|
|
for i := 0 to pred(Columns.Count) do begin
|
|
if Columns.Objects[i] is TffSqlFieldProxy then begin
|
|
F := TffSqlFieldProxy(Columns.Objects[i]);
|
|
FieldDefList.AddField(Columns[i], F.GetType, F.GetSize, F.GetDecimals);
|
|
end else begin
|
|
N := TffSqlNode(Columns.Objects[i]);
|
|
FieldDefList.AddField(Columns[i], N.GetType, N.GetSize, N.GetDecimals);
|
|
end;
|
|
end;
|
|
|
|
Result := Owner.FDatabase.CreateTemporaryTableWithoutIndex(Self, FieldDefList);
|
|
finally
|
|
FieldDefList.Free;
|
|
end;
|
|
|
|
try
|
|
|
|
if Joiner = nil then begin
|
|
|
|
if UsingCondExp <> nil then
|
|
Joiner := TffSqlJoiner.Create(Owner, UsingCondExp)
|
|
else
|
|
Joiner := TffSqlJoiner.Create(Owner, CondExp);
|
|
|
|
Joiner.Sources.Add(
|
|
TFFSqlTableProxySubset.Create(TL));
|
|
Joiner.Sources.Add(
|
|
TFFSqlTableProxySubset.Create(TR));
|
|
|
|
end;
|
|
|
|
Joiner.ClearColumnList;
|
|
|
|
Assert(Assigned(Columns));
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] is TffSqlFieldProxy then
|
|
Joiner.AddColumn(
|
|
nil,
|
|
TffSqlFieldProxy(Columns.Objects[i]),
|
|
Result.Field(i))
|
|
else
|
|
Joiner.AddColumn(
|
|
TffSqlSimpleExpression(Columns.Objects[i]),
|
|
nil,
|
|
Result.Field(i));
|
|
|
|
if NeedData then begin
|
|
Joiner.Target := Result;
|
|
Owner.FDatabase.StartTransaction([nil]);
|
|
try
|
|
case JoinType of
|
|
jtLeftOuter :
|
|
OuterJoinMode := jmLeft;
|
|
jtRightOuter :
|
|
OuterJoinMode := jmRight;
|
|
jtFullOuter :
|
|
OuterJoinMode := jmFull;
|
|
else
|
|
OuterJoinMode := jmNone;
|
|
end;
|
|
|
|
Joiner.Execute(Owner.UseIndex, nil, OuterJoinMode);
|
|
except
|
|
Owner.FDatabase.AbortTransaction;
|
|
raise;
|
|
end;
|
|
Owner.FDatabase.Commit;
|
|
end;
|
|
|
|
for i := 0 to Result.FieldCount - 1 do
|
|
Result.Field(i).IsTarget := False;
|
|
|
|
if (Parent is TffSqlInClause) or (Parent is TffSqlMatchClause) then begin
|
|
{need an index to allow the IN and MATCH clauses to be evaluated}
|
|
|
|
T2 := Result.CopySortedOnAllFields(Self);
|
|
|
|
Result.Owner := nil;
|
|
Result.Free;
|
|
Result := T2;
|
|
end;
|
|
except
|
|
Result.Owner := nil;
|
|
Result.Free;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.EnsureResultTable(NeedData: Boolean);
|
|
begin
|
|
if (NeedData and not HaveData) then begin
|
|
FResultTable.Free;
|
|
FResultTable := nil;
|
|
end;
|
|
if FResultTable = nil then begin
|
|
FResultTable := Execute2(NeedData);
|
|
HaveData := NeedData;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.Execute2(NeedData: Boolean): TffSqlTableProxy;
|
|
begin
|
|
{check that all referenced tables and fields exist}
|
|
if not Bound then
|
|
Bind;
|
|
|
|
{create the result}
|
|
Result := DoJoin(NeedData);
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.GetResultTable: TffSqlTableProxy;
|
|
begin
|
|
EnsureResultTable(True);
|
|
Result := FResultTable;
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlJoinTableExp then begin
|
|
Clear;
|
|
JoinType := TffSqlJoinTableExp(Source).JoinType;
|
|
Natural := TffSqlJoinTableExp(Source).Natural;
|
|
if TffSqlJoinTableExp(Source).TableRef1 <> nil then begin
|
|
TableRef1 := TffSqlTableRef.Create(Self);
|
|
TableRef1.Assign(TffSqlJoinTableExp(Source).TableRef1);
|
|
end;
|
|
if TffSqlJoinTableExp(Source).TableRef2 <> nil then begin
|
|
TableRef2 := TffSqlTableRef.Create(Self);
|
|
TableRef2.Assign(TffSqlJoinTableExp(Source).TableRef2);
|
|
end;
|
|
if TffSqlJoinTableExp(Source).CondExp <> nil then begin
|
|
CondExp := TFFSqlCondExp.Create(Self);
|
|
CondExp.Assign(TffSqlJoinTableExp(Source).CondExp);
|
|
end;
|
|
if TffSqlJoinTableExp(Source).UsingList <> nil then begin
|
|
UsingList := TFFSqlUsingList.Create(Self);
|
|
UsingList.Assign(TffSqlJoinTableExp(Source).UsingList);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.Clear;
|
|
begin
|
|
ClearColumns;
|
|
UsingCondExp.Free;
|
|
UsingCondExp := nil;
|
|
TableRef1.Free;
|
|
TableRef1 := nil;
|
|
TableRef2.Free;
|
|
TableRef2 := nil;
|
|
CondExp.Free;
|
|
CondExp := nil;
|
|
UsingList.Free;
|
|
UsingList := nil;
|
|
end;
|
|
|
|
destructor TffSqlJoinTableExp.Destroy;
|
|
begin
|
|
ClearColumns;
|
|
Columns.Free;
|
|
Columns := nil;
|
|
{only free the tables if they belongs to us}
|
|
{if they are sub-expressions they will be
|
|
destroyed by the owning expression object}
|
|
if (TL <> nil) and (TL.Owner = Self) then begin
|
|
TL.Owner := nil;
|
|
TL.Free;
|
|
end;
|
|
if (TR <> nil) and (TR.Owner = Self) then begin
|
|
TR.Owner := nil;
|
|
TR.Free;
|
|
end;
|
|
Clear;
|
|
Joiner.Free;
|
|
if FResultTable <> nil then
|
|
if FResultTable.Owner = Self then begin
|
|
FResultTable.Owner := nil;
|
|
FResultTable.Free;
|
|
FResultTable := nil;
|
|
end;
|
|
UsingCondExp.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream,' ');
|
|
TableRef1.EmitSQL(Stream);
|
|
if JoinType = jtCross then
|
|
WriteStr(Stream,' CROSS JOIN ')
|
|
else begin
|
|
if Natural then
|
|
WriteStr(Stream,' NATURAL');
|
|
case JoinType of
|
|
jtInner :
|
|
WriteStr(Stream,' INNER');
|
|
jtLeftOuter :
|
|
WriteStr(Stream,' LEFT OUTER');
|
|
jtRightOuter :
|
|
WriteStr(Stream,' RIGHT OUTER');
|
|
jtFullOuter :
|
|
WriteStr(Stream,' FULL OUTER');
|
|
jtUnion :
|
|
WriteStr(Stream,' UNION');
|
|
end;
|
|
WriteStr(Stream,' JOIN');
|
|
end;
|
|
TableRef2.EmitSQL(Stream);
|
|
if CondExp <> nil then begin
|
|
WriteStr(Stream,' ON');
|
|
CondExp.EmitSQL(Stream);
|
|
end;
|
|
if UsingList <> nil then begin
|
|
WriteStr(Stream,' USING (');
|
|
UsingList.EmitSQL(Stream);
|
|
WriteStr(Stream,')');
|
|
end;
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(TableRef1) then
|
|
TableRef1.EnumNodes(EnumMethod, Deep);
|
|
if assigned(TableRef2) then
|
|
TableRef2.EnumNodes(EnumMethod, Deep);
|
|
if assigned(CondExp) then
|
|
CondExp.EnumNodes(EnumMethod, Deep);
|
|
if assigned(UsingList) then
|
|
UsingList.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
Other is TffSqlJoinTableExp
|
|
and (JoinType = TffSqlJoinTableExp(Other).JoinType)
|
|
and (Natural = TffSqlJoinTableExp(Other).Natural)
|
|
and ((BothNil(TableRef1, TffSqlJoinTableExp(Other).TableRef1)
|
|
or (BothNonNil(TableRef1, TffSqlJoinTableExp(Other).TableRef1)
|
|
and TableRef1.Equals(TffSqlJoinTableExp(Other).TableRef1))))
|
|
and ((BothNil(TableRef2, TffSqlJoinTableExp(Other).TableRef2)
|
|
or (BothNonNil(TableRef2, TffSqlJoinTableExp(Other).TableRef2)
|
|
and TableRef2.Equals(TffSqlJoinTableExp(Other).TableRef2))))
|
|
and ((BothNil(CondExp, TffSqlJoinTableExp(Other).CondExp)
|
|
or (BothNonNil(CondExp, TffSqlJoinTableExp(Other).CondExp)
|
|
and CondExp.Equals(TffSqlJoinTableExp(Other).CondExp))))
|
|
and ((BothNil(UsingList, TffSqlJoinTableExp(Other).UsingList)
|
|
or (BothNonNil(UsingList, TffSqlJoinTableExp(Other).UsingList)
|
|
and UsingList.Equals(TffSqlJoinTableExp(Other).UsingList))));
|
|
end;
|
|
|
|
procedure TffSqlJoinTableExp.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
var
|
|
T : TffSqlTableProxy;
|
|
begin
|
|
Assert(Owner <> nil);
|
|
aLiveResult := False;
|
|
T := Execute2(True);
|
|
aCursorID := T.CursorID;
|
|
T.LeaveCursorOpen := True;
|
|
if T.Owner = self then begin
|
|
T.Owner := nil;
|
|
T.Free;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.GetFieldsFromTable(const TableName: string; List: TList): TffSqlTableProxy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
if SameText(TableRef1.Alias, TableName)
|
|
or SameText(TableRef1.TableName, TableName) then begin
|
|
Result := ResultTable;
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] is TffSqlFieldProxy then
|
|
if TffSqlFieldProxy(Columns.Objects[i]).OwnerTable = TableRef1.FTable then
|
|
List.Add(Columns.Objects[i]);
|
|
exit;
|
|
end;
|
|
if SameText(TableRef2.Alias, TableName)
|
|
or SameText(TableRef2.TableName, TableName) then begin
|
|
Result := ResultTable;
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] is TffSqlFieldProxy then
|
|
if TffSqlFieldProxy(Columns.Objects[i]).OwnerTable = TableRef2.FTable then
|
|
List.Add(Columns.Objects[i]);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.Reduce: Boolean;
|
|
begin
|
|
if assigned(CondExp) then
|
|
Result := CondExp.Reduce
|
|
else
|
|
Result := False;
|
|
{!!.11 begin}
|
|
if not Result then
|
|
if TableRef1.Reduce then
|
|
Result := True
|
|
else
|
|
if TableRef2.Reduce then
|
|
Result := True;
|
|
{!!.11 end}
|
|
end;
|
|
|
|
function TffSqlJoinTableExp.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] = F then begin
|
|
Result := ResultTable.Field(i);
|
|
exit;
|
|
end;
|
|
{!!.11 begin}
|
|
{We don't have the sought after source field represented in
|
|
our answer table directly, but it might be represented
|
|
indirectly as a field in a nested table expression}
|
|
Result := TableRef1.TargetFieldFromSourceField(F);
|
|
if Result <> nil then begin
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] = Result then begin
|
|
Result := ResultTable.Field(i);
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := TableRef2.TargetFieldFromSourceField(F);
|
|
if Result <> nil then begin
|
|
for i := 0 to pred(Columns.Count) do
|
|
if Columns.Objects[i] = Result then begin
|
|
Result := ResultTable.Field(i);
|
|
exit;
|
|
end;
|
|
end;
|
|
{!!.11 end}
|
|
Result := nil;
|
|
end;
|
|
|
|
{ TffSqlNonJoinTableTerm }
|
|
|
|
procedure TffSqlNonJoinTableTerm.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlNonJoinTableTerm then begin
|
|
Clear;
|
|
if TffSqlNonJoinTableTerm(Source).NonJoinTablePrimary <> nil then begin
|
|
NonJoinTablePrimary := TffSqlNonJoinTablePrimary.Create(Self);
|
|
NonJoinTablePrimary.Assign(TffSqlNonJoinTableTerm(Source).NonJoinTablePrimary);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
Result := NonJoinTablePrimary.BindFieldDown(TableName, FieldName);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
Result := NonJoinTablePrimary.BindTable(AOwner, TableName);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableTerm.Clear;
|
|
begin
|
|
NonJoinTablePrimary.Free;
|
|
NonJoinTablePrimary := nil;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.DependsOn(
|
|
Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Assert(NonJoinTablePrimary <> nil);
|
|
Result := NonJoinTablePrimary.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlNonJoinTableTerm.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableTerm.EmitSQL(Stream: TStream);
|
|
begin
|
|
if assigned(NonJoinTablePrimary) then
|
|
NonJoinTablePrimary.EmitSQL(Stream);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableTerm.EnsureResultTable(NeedData: Boolean);
|
|
begin
|
|
assert(assigned(NonJoinTablePrimary));
|
|
NonJoinTablePrimary.EnsureResultTable(NeedData);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableTerm.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(NonJoinTablePrimary) then
|
|
NonJoinTablePrimary.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
Other is TffSqlNonJoinTableTerm
|
|
and ((BothNil(NonJoinTablePrimary, TffSqlNonJoinTableTerm(Other).NonJoinTablePrimary)
|
|
or (BothNonNil(NonJoinTablePrimary, TffSqlNonJoinTableTerm(Other).NonJoinTablePrimary)
|
|
and NonJoinTablePrimary.Equals(TffSqlNonJoinTableTerm(Other).NonJoinTablePrimary))))
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableTerm.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
begin
|
|
Assert(NonJoinTablePrimary <> nil);
|
|
NonJoinTablePrimary.Execute(aLiveResult, aCursorID, RecordsRead);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.GetResultTable: TffSqlTableProxy;
|
|
begin
|
|
Assert(NonJoinTablePrimary <> nil);
|
|
Result := NonJoinTablePrimary.ResultTable;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.Reduce: Boolean;
|
|
begin
|
|
Assert(NonJoinTablePrimary <> nil);
|
|
Result := NonJoinTablePrimary.Reduce;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableTerm.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
begin
|
|
Result := NonJoinTablePrimary.TargetFieldFromSourceField(F);
|
|
end;
|
|
|
|
{ TffSqlNonJoinTableExp }
|
|
|
|
procedure TffSqlNonJoinTableExp.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TffSqlNonJoinTableExp then begin
|
|
Clear;
|
|
if TffSqlNonJoinTableExp(Source).NonJoinTableTerm <> nil then begin
|
|
NonJoinTableTerm := TffSqlNonJoinTableTerm.Create(Self);
|
|
NonJoinTableTerm.Assign(TffSqlNonJoinTableExp(Source).NonJoinTableTerm);
|
|
end;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.BindFieldDown(const TableName,
|
|
FieldName: string): TFFSqlFieldProxy;
|
|
begin
|
|
Result := NonJoinTableTerm.BindFieldDown(TableName, FieldName);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.BindTable(AOwner: TObject;
|
|
const TableName: string): TFFSqlTableProxy;
|
|
begin
|
|
Result := NonJoinTableTerm.BindTable(AOwner, TableName);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableExp.Clear;
|
|
begin
|
|
NonJoinTableTerm.Free;
|
|
NonJoinTableTerm := nil;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.DependsOn(Table: TFFSqlTableProxy): Boolean;
|
|
begin
|
|
Assert(NonJoinTableTerm <> nil);
|
|
Result := NonJoinTableTerm.DependsOn(Table);
|
|
end;
|
|
|
|
destructor TffSqlNonJoinTableExp.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableExp.EmitSQL(Stream: TStream);
|
|
begin
|
|
if assigned(NonJoinTableTerm) then
|
|
NonJoinTableTerm.EmitSQL(Stream);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableExp.EnsureResultTable(NeedData: Boolean);
|
|
begin
|
|
Assert(Assigned(NonJoinTableTerm));
|
|
NonJoinTableTerm.EnsureResultTable(NeedData);
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableExp.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
if assigned(NonJoinTableTerm) then
|
|
NonJoinTableTerm.EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
Result :=
|
|
Other is TffSqlNonJoinTableExp
|
|
and ((BothNil(NonJoinTableTerm, TffSqlNonJoinTableExp(Other).NonJoinTableTerm)
|
|
or (BothNonNil(NonJoinTableTerm, TffSqlNonJoinTableExp(Other).NonJoinTableTerm)
|
|
and NonJoinTableTerm.Equals(TffSqlNonJoinTableExp(Other).NonJoinTableTerm))))
|
|
end;
|
|
|
|
procedure TffSqlNonJoinTableExp.Execute(
|
|
var aLiveResult: Boolean; var aCursorID: TffCursorID;
|
|
var RecordsRead: Integer);
|
|
begin
|
|
Assert(NonJoinTableTerm <> nil);
|
|
NonJoinTableTerm.Execute(aLiveResult, aCursorID, RecordsRead);
|
|
end;
|
|
|
|
{!!.11 new}
|
|
function TffSqlNonJoinTableExp.GetFieldsFromTable(const TableName: string; List: TList): TffSqlTableProxy; {!!.11}
|
|
begin
|
|
Result := nil;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.GetResultTable: TffSqlTableProxy;
|
|
begin
|
|
Assert(NonJoinTableTerm <> nil);
|
|
Result := NonJoinTableTerm.ResultTable;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.Reduce: Boolean;
|
|
begin
|
|
Assert(NonJoinTableTerm <> nil);
|
|
Result := NonJoinTableTerm.Reduce;
|
|
end;
|
|
|
|
constructor TffSqlJoinTableExp.Create;
|
|
begin
|
|
inherited;
|
|
Columns := TStringList.Create;
|
|
end;
|
|
|
|
function TffSqlNonJoinTableExp.TargetFieldFromSourceField(
|
|
const F: TffSqlFieldProxy): TffSqlFieldProxy;
|
|
begin
|
|
Result := NonJoinTableTerm.TargetFieldFromSourceField(F);
|
|
end;
|
|
|
|
{ TFFSqlUsingItem }
|
|
|
|
procedure TFFSqlUsingItem.Assign(const Source: TffSqlNode);
|
|
begin
|
|
if Source is TFFSqlUsingItem then begin
|
|
ColumnName := TFFSqlUsingItem(Source).ColumnName;
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
procedure TFFSqlUsingItem.EmitSQL(Stream: TStream);
|
|
begin
|
|
WriteStr(Stream, ' ');
|
|
WriteStr(Stream, ColumnName);
|
|
end;
|
|
|
|
procedure TFFSqlUsingItem.EnumNodes(EnumMethod: TffSqlEnumMethod;
|
|
const Deep: Boolean);
|
|
begin
|
|
EnumMethod(Self);
|
|
end;
|
|
|
|
function TFFSqlUsingItem.Equals(Other: TffSqlNode): Boolean;
|
|
begin
|
|
if Other is TFFSqlUsingItem then
|
|
Result := ColumnName = TFFSqlUsingItem(Other).ColumnName
|
|
else
|
|
Result := False;
|
|
end;
|
|
|
|
{===TffSqlUsingList==================================================}
|
|
function TffSqlUsingList.AddItem(NewUsing: TffSqlUsingItem): TffSqlUsingItem;
|
|
begin
|
|
FUsingItemList.Add(NewUsing);
|
|
Result := NewUsing;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUsingList.Assign(const Source: TffSqlNode);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Source is TffSqlUsingList then begin
|
|
Clear;
|
|
for i := 0 to pred(TffSqlUsingList(Source).UsingCount) do
|
|
AddItem(TffSqlUsingItem.Create(Self)).Assign(
|
|
TffSqlUsingList(Source).UsingItem[i]);
|
|
end else
|
|
AssignError(Source);
|
|
end;
|
|
|
|
constructor TffSqlUsingList.Create(AParent: TffSqlNode);
|
|
begin
|
|
inherited Create(AParent);
|
|
FUsingItemList := TList.Create;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUsingList.Clear;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
for i := 0 to pred(FUsingItemList.Count) do
|
|
UsingItem[i].Free;
|
|
FUsingItemList.Clear;
|
|
end;
|
|
{--------}
|
|
destructor TffSqlUsingList.Destroy;
|
|
begin
|
|
Clear;
|
|
FUsingItemList.Free;
|
|
inherited;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUsingList.EmitSQL(Stream: TStream);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
UsingItem[0].EmitSQL(Stream);
|
|
for i := 1 to pred(UsingCount) do begin
|
|
WriteStr(Stream,', ');
|
|
UsingItem[i].EmitSQL(Stream);
|
|
end;
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUsingList.EnumNodes(EnumMethod: TffSqlEnumMethod; const Deep: Boolean);
|
|
var
|
|
i : Integer;
|
|
begin
|
|
EnumMethod(Self);
|
|
for i := 0 to pred(UsingCount) do
|
|
UsingItem[i].EnumNodes(EnumMethod, Deep);
|
|
end;
|
|
{--------}
|
|
function TffSqlUsingList.Equals(Other: TffSqlNode): Boolean;
|
|
var
|
|
i : Integer;
|
|
begin
|
|
Result := False;
|
|
if Other is TffSqlUsingList then begin
|
|
if UsingCount <> TffSqlUsingList(Other).UsingCount then
|
|
exit;
|
|
for i := 0 to pred(UsingCount) do
|
|
if not UsingItem[i].Equals(TffSqlUsingList(Other).UsingItem[i]) then
|
|
exit;
|
|
Result := True;
|
|
end;
|
|
end;
|
|
{--------}
|
|
function TffSqlUsingList.GetUsingCount: Integer;
|
|
begin
|
|
Result := FUsingItemList.Count;
|
|
end;
|
|
{--------}
|
|
function TffSqlUsingList.GetUsingItem(
|
|
Index: Integer): TffSqlUsingItem;
|
|
begin
|
|
Result := TffSqlUsingItem(FUsingItemList[Index]);
|
|
end;
|
|
{--------}
|
|
procedure TffSqlUsingList.SetUsingItem(Index: Integer;
|
|
const Value: TffSqlUsingItem);
|
|
begin
|
|
FUsingItemList[Index] := Value;
|
|
end;
|
|
{====================================================================}
|
|
|
|
initialization
|
|
{calculate TimeDelta as one second} {!!.01}
|
|
TimeDelta := EncodeTime(0, 0, 2, 0) - EncodeTime(0, 0, 1, 0); {!!.01}
|
|
end.
|
|
|
|
|