|
From: <na...@us...> - 2012-02-14 15:19:15
|
Revision: 958
http://instantobjects.svn.sourceforge.net/instantobjects/revision/?rev=958&view=rev
Author: nandod
Date: 2012-02-14 15:19:08 +0000 (Tue, 14 Feb 2012)
Log Message:
-----------
* Ability to generate both INNER and OUTER SQL joins. Fixes #3487574.
Modified Paths:
--------------
trunk/Source/Core/InstantBrokers.pas
Modified: trunk/Source/Core/InstantBrokers.pas
===================================================================
--- trunk/Source/Core/InstantBrokers.pas 2012-01-29 04:09:48 UTC (rev 957)
+++ trunk/Source/Core/InstantBrokers.pas 2012-02-14 15:19:08 UTC (rev 958)
@@ -799,6 +799,43 @@
property Query: TInstantQuery read GetQuery;
end;
+ // A table path with its list of join clauses.
+ // Helper class used by TInstantTranslationContext.
+ TInstantTablePath = class
+ private
+ FJoinClauses: TStrings;
+ FIsOuterJoin: Boolean;
+ FName: string;
+ function GetCount: Integer;
+ function GetJoinClause(I: Integer): string;
+ public
+ constructor Create(const AName: string);
+ procedure AfterConstruction; override;
+ destructor Destroy; override;
+ property Name: string read FName write FName;
+ property IsOuterJoin: Boolean read FIsOuterJoin write FIsOuterJoin;
+ function AddJoinClause(const AJoinClause: string): Integer;
+ property JoinClauses[I: Integer]: string read GetJoinClause; default;
+ property Count: Integer read GetCount;
+ end;
+
+ // A list of table paths. Helper class used by TInstantTranslationContext.
+ TInstantTablePathList = class
+ private
+ FPaths: TObjectList;
+ function GetPath(I: Integer): TInstantTablePath;
+ function GetCount: Integer;
+ public
+ procedure AfterConstruction; override;
+ destructor Destroy; override;
+ property Paths[I: Integer]: TInstantTablePath read GetPath; default;
+ property Count: Integer read GetCount;
+ function IndexOfName(const ATablePathName: string): Integer;
+ function Add(const ATablePathName: string): Integer;
+ function AddJoinClause(const ATablePathName, AJoinClause: string;
+ const AIsOuterJoin: Boolean): Integer;
+ end;
+
// Holds all information pertaining to a class used in a command. A command
// may use several classes (because of subqueries), and a relational translator
// has a tree of class context objects.
@@ -813,12 +850,13 @@
FQuote: Char;
FSpecifier: TInstantIQLSpecifier;
FStatement: TInstantIQLObject;
- FTablePathList: TStringList;
+ FTablePathList: TInstantTablePathList;
FParentContext: TInstantTranslationContext;
FIdDataType: TInstantDataType;
FRequestedLoadMode: TInstantLoadMode;
FActualLoadMode: TInstantLoadMode;
- procedure AddJoin(const FromPath, FromField, ToPath, ToField: string);
+ procedure AddJoin(const FromPath, FromField, ToPath, ToField: string;
+ const IsOuter: Boolean);
function GetClassTablePath: string;
function GetChildContext(const AIndex: Integer): TInstantTranslationContext;
function GetChildContextCount: Integer;
@@ -830,7 +868,7 @@
function GetTableName: string;
function GetTablePathAliases(Index: Integer): string;
function GetTablePathCount: Integer;
- function GetTablePathList: TStringList;
+ function GetTablePathList: TInstantTablePathList;
function GetTablePaths(Index: Integer): string;
function PathToTablePath(const PathText: string): string;
function PathToTarget(const PathText: string;
@@ -842,6 +880,7 @@
function GetChildContextIndex: Integer;
function GetChildContextLevel: Integer;
function RootAttribToFieldName(const AttribName: string): string;
+ function IsRequiredAttribute(const AAttributeName: string): Boolean;
protected
function AddCriteria(const Criteria: string): Integer;
function AddTablePath(const TablePath: string): Integer;
@@ -852,7 +891,7 @@
procedure MakeJoins(Path: TInstantIQLPath);
procedure MakeTablePaths(Path: TInstantIQLPath);
property CriteriaList: TStringList read GetCriteriaList;
- property TablePathList: TStringList read GetTablePathList;
+ property TablePathList: TInstantTablePathList read GetTablePathList;
public
constructor Create(const AStatement: TInstantIQLObject; const AQuote: Char;
const ADelimiters: string; const AIdDataType: TInstantDataType;
@@ -1323,7 +1362,7 @@
begin
Map := StorageMaps[I];
if (Map <> RootMap) and (Info.Conflict or OperationRequired(Map)) then
- Operation(AObject, AObjectId, Map);
+ Operation(AObject, AObjectId, Map, ConflictAction, @Info);
end;
end;
end;
@@ -5590,8 +5629,8 @@
Writer.WriteString(Format('%s AS %s, %s AS %s', [ClassQual,
InstantClassFieldName, IdQual, InstantIdFieldName]));
- // Mind that LContext.ActualBurstLoadMode might be different than
- // Self.RequestedBurstLoadMode.
+ // Mind that LContext.ActualLoadMode might be different than
+ // Self.RequestedLoadMode.
if IsBurstLoadMode(LContext.ActualLoadMode) then
begin
// Use the Id just to get the table path needed to add the updatecount
@@ -6200,10 +6239,10 @@
end;
procedure TInstantTranslationContext.AddJoin(const FromPath, FromField, ToPath,
- ToField: string);
+ ToField: string; const IsOuter: Boolean);
begin
- AddCriteria(Format('%s = %s', [Qualify(FromPath, FromField),
- Qualify(ToPath, ToField)]));
+ TablePathList.AddJoinClause(ToPath, Format('%s = %s', [Qualify(FromPath, FromField),
+ Qualify(ToPath, ToField)]), IsOuter);
end;
function TInstantTranslationContext.AddTablePath(
@@ -6416,16 +6455,16 @@
Result := TablePathList.Count;
end;
-function TInstantTranslationContext.GetTablePathList: TStringList;
+function TInstantTranslationContext.GetTablePathList: TInstantTablePathList;
begin
if not Assigned(FTablePathList) then
- FTablePathList := TStringList.Create;
+ FTablePathList := TInstantTablePathList.Create;
Result := FTablePathList;
end;
function TInstantTranslationContext.GetTablePaths(Index: Integer): string;
begin
- Result := TablePathList[Index];
+ Result := TablePathList[Index].Name;
end;
function TInstantTranslationContext.HasParentContext: Boolean;
@@ -6448,7 +6487,7 @@
function TInstantTranslationContext.IndexOfTablePath(
const TablePath: string): Integer;
begin
- Result := TablePathList.IndexOf(TablePath);
+ Result := TablePathList.IndexOfName(TablePath);
end;
procedure TInstantTranslationContext.Initialize;
@@ -6530,8 +6569,8 @@
LTableName := LClassMeta.TableName;
if (LTableName <> TableName) and LClassMeta.IsStored then
begin
- AddJoin(TableName, InstantClassFieldName, LTableName, InstantClassFieldName);
- AddJoin(TableName, InstantIdFieldName, LTableName, InstantIdFieldName);
+ AddJoin(TableName, InstantClassFieldName, LTableName, InstantClassFieldName, False);
+ AddJoin(TableName, InstantIdFieldName, LTableName, InstantIdFieldName, False);
end;
end;
LClassMeta := LClassMeta.Parent;
@@ -6581,12 +6620,30 @@
end;
end;
+function TInstantTranslationContext.IsRequiredAttribute(const AAttributeName: string): Boolean;
+var
+ LClassMetadata: TInstantClassMetadata;
+ LAttributeMetadata: TInstantAttributeMetadata;
+begin
+ Assert(AAttributeName <> '');
+ Assert(Assigned(ClassRef));
+
+ Result := False;
+ LClassMetadata := InstantModel.ClassMetadatas.Find(ClassRef.ObjectClassName);
+ if Assigned(LClassMetadata) then begin
+ LAttributeMetadata := LClassMetadata.AttributeMetadatas.Find(AAttributeName);
+ if Assigned(LAttributeMetadata) then
+ Result := LAttributeMetadata.IsRequired;
+ end;
+end;
+
procedure TInstantTranslationContext.MakeJoins(Path: TInstantIQLPath);
procedure MakePathJoins(Path: TInstantIQLPath);
var
I: Integer;
PathText, FromPath, ToPath, FromField, ToField: string;
+ LIsRequiredAttribute: Boolean;
begin
if Path.AttributeCount > 1 then
begin
@@ -6597,10 +6654,11 @@
if not IsRootAttribute(ExtractTarget(PathText)) then
begin
PathToTarget(PathText, ToPath, ToField);
+ LIsRequiredAttribute := IsRequiredAttribute(FromField);
AddJoin(FromPath, FromField + InstantClassFieldName, ToPath,
- InstantClassFieldName);
+ InstantClassFieldName, not LIsRequiredAttribute);
AddJoin(FromPath, FromField + InstantIdFieldName, ToPath,
- InstantIdFieldName);
+ InstantIdFieldName, not LIsRequiredAttribute);
FromPath := ToPath;
FromField := ToField;
end;
@@ -6618,9 +6676,9 @@
if TablePath <> ClassTablePath then
begin
AddJoin(ClassTablePath, InstantClassFieldName,
- TablePath, InstantClassFieldName);
+ TablePath, InstantClassFieldName, False);
AddJoin(ClassTablePath, InstantIdFieldName,
- TablePath, InstantIdFieldName);
+ TablePath, InstantIdFieldName, False);
end;
end;
end;
@@ -6813,14 +6871,26 @@
procedure TInstantTranslationContext.WriteTables(Writer: TInstantIQLWriter);
var
- I: Integer;
+ I, J: Integer;
begin
for I := 0 to Pred(TablePathCount) do
begin
- if I > 0 then
- Writer.WriteString(', ');
+ if I > 0 then begin
+ if TablePathList[I].IsOuterJoin then
+ Writer.WriteString(' LEFT OUTER');
+ Writer.WriteString(' JOIN ');
+ end;
Writer.WriteString(Format('%s %s',[InstantEmbrace(
ExtractTarget(TablePaths[I]), Delimiters), TablePathAliases[I]]));
+ if (I > 0) and (TablePathList[I].Count > 0) then begin
+ Writer.WriteString(' ON (');
+ for J := 0 to TablePathList[I].Count - 1 do begin
+ if J > 0 then
+ Writer.WriteString(' AND ');
+ Writer.WriteString(TablePathList[I][J]);
+ end;
+ Writer.WriteString(')');
+ end;
end;
end;
@@ -6855,4 +6925,94 @@
FDataSet := nil;
end;
+{ TInstantTablePathList }
+
+function TInstantTablePathList.Add(const ATablePathName: string): Integer;
+begin
+ Result := IndexOfName(ATablePathName);
+ if Result < 0 then
+ Result := FPaths.Add(TInstantTablePath.Create(ATablePathName));
+end;
+
+function TInstantTablePathList.AddJoinClause(const ATablePathName,
+ AJoinClause: string; const AIsOuterJoin: Boolean): Integer;
+begin
+ Result := Add(ATablePathName);
+ Paths[Result].AddJoinClause(AJoinClause);
+ Paths[Result].IsOuterJoin := AIsOuterJoin;
+end;
+
+procedure TInstantTablePathList.AfterConstruction;
+begin
+ inherited;
+ FPaths := TObjectList.Create(True);
+end;
+
+destructor TInstantTablePathList.Destroy;
+begin
+ FreeAndNil(FPaths);
+ inherited;
+end;
+
+function TInstantTablePathList.GetCount: Integer;
+begin
+ Result := FPaths.Count;
+end;
+
+function TInstantTablePathList.GetPath(I: Integer): TInstantTablePath;
+begin
+ Result := TInstantTablePath(FPaths[I]);
+end;
+
+function TInstantTablePathList.IndexOfName(
+ const ATablePathName: string): Integer;
+var
+ I: Integer;
+begin
+ Result := -1;
+ for I := 0 to Count - 1 do begin
+ if Paths[I].Name = ATablePathName then begin
+ Result := I;
+ Break;
+ end;
+ end;
+end;
+
+{ TInstantTablePath }
+
+function TInstantTablePath.AddJoinClause(const AJoinClause: string): Integer;
+begin
+ Result := FJoinClauses.IndexOf(AJoinClause);
+ if Result < 0 then
+ Result := FJoinClauses.Add(AJoinClause);
+end;
+
+procedure TInstantTablePath.AfterConstruction;
+begin
+ inherited;
+ FJoinClauses := TStringList.Create;
+end;
+
+constructor TInstantTablePath.Create(const AName: string);
+begin
+ inherited Create;
+ FName := AName;
+end;
+
+destructor TInstantTablePath.Destroy;
+begin
+ FreeAndNil(FJoinClauses);
+ inherited;
+end;
+
+function TInstantTablePath.GetCount: Integer;
+begin
+ Result := FJoinClauses.Count;
+end;
+
+function TInstantTablePath.GetJoinClause(I: Integer): string;
+begin
+ Result := FJoinClauses[I];
+end;
+
end.
|