mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
Added foReadonly and foWriteOnly os MVCActiveRecord Field Options
This commit is contained in:
parent
2e42f10466
commit
a690bea612
49
README.md
49
README.md
@ -404,6 +404,55 @@ Congratulations to Daniele Teti and all the staff for the excellent work!" -- Ma
|
||||
|
||||
- `IMVCJSONRPCExecutor.ExecuteNotification` returns a `IJSONRPCResponse`. In case of error response contains information about the error, in case of successful execution the response is a [Null Object](https://en.wikipedia.org/wiki/Null_object_pattern).
|
||||
|
||||
- Added `foReadOnly` and `foWriteOnly` as field options in `MVCTableField` attribute (used by `TMVCActiveRecord`). Currently available field options are:
|
||||
|
||||
- *foPrimaryKey* { it's the primary key of the mapped table }
|
||||
- *foAutoGenerated* { not written, read - similar to foReadOnly but is reloaded after insert and update }
|
||||
- *foTransient* { never stored nor read - managed only at run-time }
|
||||
- *foReadOnly* { not written, read }
|
||||
- *foWriteOnly* { written, not read }
|
||||
|
||||
Now it is possible to declare entities like the followings (or with any other combinations):
|
||||
|
||||
```delphi
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('articles')]
|
||||
TArticleWithWriteOnlyFields = class(TCustomEntity)
|
||||
private
|
||||
[MVCTableField('ID', [foPrimaryKey, foAutoGenerated])]
|
||||
fID: NullableInt32;
|
||||
[MVCTableField('description', [foWriteOnly])]
|
||||
fDescription: string;
|
||||
[MVCTableField('price', [foWriteOnly])]
|
||||
fPrice: Integer;
|
||||
public
|
||||
property ID: NullableInt32 read fID write fID;
|
||||
property Description: string read fDescription write fDescription;
|
||||
property Price: Integer read fPrice write fPrice;
|
||||
end;
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('articles')]
|
||||
TArticleWithReadOnlyFields = class(TCustomEntity)
|
||||
private
|
||||
[MVCTableField('ID', [foPrimaryKey, foReadOnly])]
|
||||
fID: NullableInt32;
|
||||
[MVCTableField('code', [foTransient])]
|
||||
fCode: NullableString;
|
||||
[MVCTableField('description', [foReadOnly])]
|
||||
fDescrizione: string;
|
||||
[MVCTableField('price', [foReadOnly])]
|
||||
fPrice: Currency;
|
||||
public
|
||||
property ID: NullableInt32 read fID write fID;
|
||||
property Code: NullableString read fCode write fCode;
|
||||
property Description: string read fDescription write fDescription;
|
||||
property Price: Currency read fPrice write fPrice;
|
||||
end;
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Roadmap
|
||||
|
||||
DelphiMVCFramework roadmap is always updated as-soon-as the features planned are implemented. Check the roadmap [here](roadmap.md).
|
||||
|
@ -46,9 +46,9 @@ type
|
||||
[MVCTable('articles')]
|
||||
TArticle = class(TCustomEntity)
|
||||
private
|
||||
[MVCTableField('ID')]
|
||||
[MVCTableField('ID', [foPrimaryKey, foAutoGenerated])]
|
||||
fID: NullableInt32;
|
||||
[MVCTableField('code')]
|
||||
[MVCTableField('code', [foTransient])]
|
||||
fCodice: NullableString;
|
||||
[MVCTableField('description')]
|
||||
fDescrizione: string;
|
||||
@ -63,6 +63,41 @@ type
|
||||
property Price: Currency read fPrezzo write fPrezzo;
|
||||
end;
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('articles')]
|
||||
TArticleWithWriteOnlyFields = class(TCustomEntity)
|
||||
private
|
||||
[MVCTableField('ID', [foPrimaryKey, foAutoGenerated, foReadOnly])]
|
||||
fID: NullableInt32;
|
||||
[MVCTableField('description', [foWriteOnly])]
|
||||
fDescrizione: string;
|
||||
[MVCTableField('price', [foWriteOnly])]
|
||||
fPrice: Integer;
|
||||
public
|
||||
property ID: NullableInt32 read fID write fID;
|
||||
property Description: string read fDescrizione write fDescrizione;
|
||||
property Price: Integer read fPrice write fPrice;
|
||||
end;
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
[MVCTable('articles')]
|
||||
TArticleWithReadOnlyFields = class(TCustomEntity)
|
||||
private
|
||||
[MVCTableField('ID', [foPrimaryKey, foReadOnly])]
|
||||
fID: NullableInt32;
|
||||
[MVCTableField('code', [foTransient])]
|
||||
fCodice: NullableString;
|
||||
[MVCTableField('description', [foReadOnly])]
|
||||
fDescrizione: string;
|
||||
[MVCTableField('price', [foReadOnly])]
|
||||
fPrezzo: Currency;
|
||||
public
|
||||
property ID: NullableInt32 read fID write fID;
|
||||
property Code: NullableString read fCodice write fCodice;
|
||||
property Description: string read fDescrizione write fDescrizione;
|
||||
property Price: Currency read fPrezzo write fPrezzo;
|
||||
end;
|
||||
|
||||
TOrder = class;
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
@ -108,7 +143,6 @@ type
|
||||
fID: Integer;
|
||||
[MVCTableField('code', [foTransient])]
|
||||
fCode: string;
|
||||
[MVCTableField('', [foTransient])]
|
||||
fFormattedCode: string;
|
||||
[MVCTableField('description')]
|
||||
fCompanyName: string;
|
||||
@ -459,7 +493,7 @@ end;
|
||||
constructor TNullablesTest.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
// ff_blob := TMemoryStream.Create;
|
||||
ff_blob := TMemoryStream.Create;
|
||||
end;
|
||||
|
||||
destructor TNullablesTest.Destroy;
|
||||
|
@ -158,6 +158,15 @@ object MainForm: TMainForm
|
||||
TabOrder = 13
|
||||
OnClick = btnCountWithRQLClick
|
||||
end
|
||||
object btnReadAndWriteOnly: TButton
|
||||
Left = 8
|
||||
Top = 523
|
||||
Width = 121
|
||||
Height = 33
|
||||
Caption = 'R/O, R/W'
|
||||
TabOrder = 14
|
||||
OnClick = btnReadAndWriteOnlyClick
|
||||
end
|
||||
object FDConnection1: TFDConnection
|
||||
Left = 176
|
||||
Top = 56
|
||||
|
@ -42,6 +42,7 @@ type
|
||||
btnCRUDWithStringPKs: TButton;
|
||||
btnWithSpaces: TButton;
|
||||
btnCountWithRQL: TButton;
|
||||
btnReadAndWriteOnly: TButton;
|
||||
procedure btnCRUDClick(Sender: TObject);
|
||||
procedure btnInheritanceClick(Sender: TObject);
|
||||
procedure btnMultiThreadingClick(Sender: TObject);
|
||||
@ -58,6 +59,7 @@ type
|
||||
procedure btnCRUDWithStringPKsClick(Sender: TObject);
|
||||
procedure btnWithSpacesClick(Sender: TObject);
|
||||
procedure btnCountWithRQLClick(Sender: TObject);
|
||||
procedure btnReadAndWriteOnlyClick(Sender: TObject);
|
||||
private
|
||||
procedure Log(const Value: string);
|
||||
procedure LoadCustomers;
|
||||
@ -137,7 +139,7 @@ begin
|
||||
Log('There are ' + TMVCActiveRecord.Count<TCustomer>().ToString + ' row/s for entity ' + TCustomer.ClassName);
|
||||
lCustomer := TCustomer.Create;
|
||||
try
|
||||
Log('Entity ' + TCustomer.ClassName + ' is mapped to table ' + lCustomer.TableName);
|
||||
Log('Entity ' + TCustomer.ClassName + ' is mapped to table ' + lCustomer.TableName);
|
||||
lCustomer.CompanyName := 'Google Inc.';
|
||||
lCustomer.City := 'Montain View, CA';
|
||||
lCustomer.Note := 'Hello there!';
|
||||
@ -449,7 +451,12 @@ begin
|
||||
lTest.f_int2 := 2;
|
||||
lTest.f_int4 := 4;
|
||||
lTest.f_int8 := 8;
|
||||
lTest.f_blob := TStringStream.Create('Hello World');
|
||||
with TStreamWriter.Create(lTest.f_blob) do
|
||||
try
|
||||
write('Hello World');
|
||||
finally
|
||||
Free;
|
||||
end;
|
||||
lTest.Insert;
|
||||
Log('Inserting nulls');
|
||||
finally
|
||||
@ -474,8 +481,7 @@ begin
|
||||
lTest.f_int2 := lTest.f_int2.Value + 2;
|
||||
lTest.f_int4 := lTest.f_int4.Value + 4;
|
||||
lTest.f_int8 := lTest.f_int8.Value + 8;
|
||||
lTest.f_blob.Free;
|
||||
lTest.f_blob := nil;
|
||||
lTest.f_blob.Size := 0;
|
||||
lTest.Update;
|
||||
finally
|
||||
lTest.Free;
|
||||
@ -494,7 +500,7 @@ begin
|
||||
Assert(not lTest.f_float4.HasValue);
|
||||
Assert(not lTest.f_float8.HasValue);
|
||||
Assert(not lTest.f_bool.HasValue);
|
||||
Assert(not Assigned(lTest.f_blob), 'Blob contains a value when should not');
|
||||
Assert(lTest.f_blob.Size = 0, 'Blob contains a value when should not');
|
||||
TMVCActiveRecord.DeleteRQL(TNullablesTest, 'eq(f_int2,4)');
|
||||
finally
|
||||
lTest.Free;
|
||||
@ -572,6 +578,65 @@ begin
|
||||
|
||||
end;
|
||||
|
||||
procedure TMainForm.btnReadAndWriteOnlyClick(Sender: TObject);
|
||||
var
|
||||
lArtWO, lArtWO2: TArticleWithWriteOnlyFields;
|
||||
lArtRO: TArticleWithReadOnlyFields;
|
||||
lID: NullableInt32;
|
||||
lArt: TArticle;
|
||||
begin
|
||||
lArtWO := TArticleWithWriteOnlyFields.Create();
|
||||
try
|
||||
lArtWO.Description := 'Description1';
|
||||
lArtWO.Price := 12;
|
||||
lArtWO.Insert;
|
||||
Log('Stored TArticleWithWriteOnlyFields');
|
||||
lID := lArtWO.ID;
|
||||
|
||||
lArt := TMVCActiveRecord.GetByPK<TArticle>(lID);
|
||||
try
|
||||
Assert(lArtWO.Description = lArt.Description);
|
||||
Assert(lArtWO.Price = lArt.Price);
|
||||
Log('Check Stored version of TArticleWithWriteOnlyFields');
|
||||
|
||||
Log('Reading data using TArticleWithReadOnlyFields');
|
||||
lArtRO := TMVCActiveRecord.GetByPK<TArticleWithReadOnlyFields>(lID);
|
||||
try
|
||||
Assert(lArtRO.Description = lArt.Description);
|
||||
Assert(lArtRO.Price = lArt.Price);
|
||||
Log('Check Read data of TArticleWithWriteOnlyFields using TArticleWithReadOnlyFields');
|
||||
finally
|
||||
lArtRO.Free;
|
||||
end;
|
||||
|
||||
Log('Reading data using TArticleWithWriteOnlyFields (???)');
|
||||
lArtWO2 := TMVCActiveRecord.GetByPK<TArticleWithWriteOnlyFields>(lID);
|
||||
try
|
||||
Assert(lArtWO2.ID.ValueOrDefault = lID.ValueOrDefault);
|
||||
Assert(lArtWO2.Description = '');
|
||||
Assert(lArtWO2.Price = 0);
|
||||
finally
|
||||
lArtWO2.Free;
|
||||
end;
|
||||
finally
|
||||
lArt.Free;
|
||||
end;
|
||||
|
||||
lArtRO := TArticleWithReadOnlyFields.Create();
|
||||
try
|
||||
lArtRO.Description := 'Description1';
|
||||
lArtRO.Price := 12;
|
||||
ShowMessage('Now an exception will be raised...');
|
||||
lArtRO.Insert; // exception here :-)
|
||||
finally
|
||||
lArtRO.Free;
|
||||
end;
|
||||
|
||||
finally
|
||||
lArtWO.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.btnRelationsClick(Sender: TObject);
|
||||
var
|
||||
lCustomer: TCustomerEx;
|
||||
@ -818,7 +883,7 @@ begin
|
||||
lCustomer := TCustomerWithTransient.Create;
|
||||
try
|
||||
{
|
||||
'Code' and City will not be persisted because defined as 'transient'
|
||||
'Code' will not be persisted because defined as 'transient'
|
||||
}
|
||||
lCustomer.Code := '1234';
|
||||
lCustomer.CompanyName := 'Google Inc.';
|
||||
|
@ -163,13 +163,13 @@
|
||||
</Excluded_Packages>
|
||||
</Delphi.Personality>
|
||||
<Deployment Version="3">
|
||||
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="BUILD" Class="ProjectOutput">
|
||||
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="Debug" Class="ProjectOutput">
|
||||
<Platform Name="Win32">
|
||||
<RemoteName>activerecord_showcase.exe</RemoteName>
|
||||
<Overwrite>true</Overwrite>
|
||||
</Platform>
|
||||
</DeployFile>
|
||||
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="Debug" Class="ProjectOutput">
|
||||
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="BUILD" Class="ProjectOutput">
|
||||
<Platform Name="Win32">
|
||||
<RemoteName>activerecord_showcase.exe</RemoteName>
|
||||
<Overwrite>true</Overwrite>
|
||||
|
Binary file not shown.
@ -58,7 +58,12 @@ type
|
||||
end;
|
||||
|
||||
TMVCActiveRecordClass = class of TMVCActiveRecord;
|
||||
TMVCActiveRecordFieldOption = (foPrimaryKey, foAutoGenerated, foTransient);
|
||||
TMVCActiveRecordFieldOption = (
|
||||
foPrimaryKey, { it's the primary key of the mapped table }
|
||||
foAutoGenerated, { not written, read - similar to readonly }
|
||||
foTransient, { not written, not read }
|
||||
foReadOnly, { not written, read }
|
||||
foWriteOnly); { written, not read }
|
||||
TMVCActiveRecordFieldOptions = set of TMVCActiveRecordFieldOption;
|
||||
TMVCEntityAction = (eaCreate, eaRetrieve, eaUpdate, eaDelete);
|
||||
TMVCEntityActions = set of TMVCEntityAction;
|
||||
@ -81,7 +86,7 @@ type
|
||||
|
||||
TFieldInfo = class
|
||||
public
|
||||
// TableName: string;
|
||||
// TableName: string;
|
||||
FieldName: string;
|
||||
FieldOptions: TMVCActiveRecordFieldOptions;
|
||||
DataTypeName: string;
|
||||
@ -91,11 +96,13 @@ type
|
||||
|
||||
TFieldsMap = class(TObjectDictionary<TRTTIField, TFieldInfo>)
|
||||
private
|
||||
fNonTransientFieldsCount: Integer;
|
||||
fWritableFieldsCount: Integer;
|
||||
fReadableFieldsCount: Integer;
|
||||
public
|
||||
constructor Create;
|
||||
procedure EndUpdates;
|
||||
property NonTransientFieldsCount: Integer read fNonTransientFieldsCount;
|
||||
property WritableFieldsCount: Integer read fWritableFieldsCount;
|
||||
property ReadableFieldsCount: Integer read fWritableFieldsCount;
|
||||
function GetInfoByFieldName(const FieldName: string): TFieldInfo;
|
||||
end;
|
||||
|
||||
@ -155,7 +162,7 @@ type
|
||||
function GetPrimaryKeyIsAutogenerated: Boolean;
|
||||
procedure SetPrimaryKeyIsAutogenerated(const Value: Boolean);
|
||||
function GetPrimaryKeyFieldType: TFieldType;
|
||||
procedure SetTableName(const Value: String);
|
||||
procedure SetTableName(const Value: string);
|
||||
protected
|
||||
fRTTIType: TRttiInstanceType;
|
||||
fProps: TArray<TRTTIField>;
|
||||
@ -282,7 +289,7 @@ type
|
||||
procedure AddChildren(const ChildObject: TObject);
|
||||
procedure RemoveChildren(const ChildObject: TObject);
|
||||
[MVCDoNotSerialize]
|
||||
property TableName: String read fTableName write SetTableName;
|
||||
property TableName: string read fTableName write SetTableName;
|
||||
[MVCDoNotSerialize]
|
||||
property PrimaryKeyIsAutogenerated: Boolean read GetPrimaryKeyIsAutogenerated write SetPrimaryKeyIsAutogenerated;
|
||||
class function GetByPK(const aClass: TMVCActiveRecordClass; const aValue: int64;
|
||||
@ -452,7 +459,7 @@ type
|
||||
// end-capabilities
|
||||
function CreateSQLWhereByRQL(const RQL: string; const Mapping: TMVCFieldsMapping;
|
||||
const UseArtificialLimit: Boolean = True;
|
||||
const UseFilterOnly: Boolean = False
|
||||
const UseFilterOnly: Boolean = false
|
||||
): string; virtual; abstract;
|
||||
function CreateSelectSQL(const TableName: string; const Map: TFieldsMap;
|
||||
const PKFieldName: string; const PKOptions: TMVCActiveRecordFieldOptions): string; virtual; abstract;
|
||||
@ -603,6 +610,13 @@ begin
|
||||
lName := aName.ToLower;
|
||||
lConnKeyName := GetKeyName(lName);
|
||||
|
||||
{ If the transaction is not started, initialize TxIsolation as ReadCommitted }
|
||||
if aConnection.Transaction = nil then
|
||||
begin
|
||||
{ needed for Delphi 10.4 Sydney+ }
|
||||
aConnection.TxOptions.Isolation := TFDTxIsolation.xiReadCommitted;
|
||||
end;
|
||||
|
||||
fMREW.BeginWrite;
|
||||
try
|
||||
lConnHolder := TConnHolder.Create;
|
||||
@ -955,7 +969,7 @@ begin
|
||||
begin
|
||||
fPrimaryKeyFieldType := ftLargeInt;
|
||||
end
|
||||
else if lPrimaryFieldTypeAsStr.EndsWith('integer') then
|
||||
else if lPrimaryFieldTypeAsStr.EndsWith('integer') or lPrimaryFieldTypeAsStr.EndsWith('int32') then
|
||||
begin
|
||||
fPrimaryKeyFieldType := ftInteger;
|
||||
end
|
||||
@ -975,9 +989,8 @@ begin
|
||||
continue;
|
||||
end;
|
||||
|
||||
{ TODO -oDanieleT -cGeneral : Definire TFieldInfo per tute le info del field }
|
||||
lFieldInfo := TFieldInfo.Create;
|
||||
//lFieldInfo.TableName := fTableName;
|
||||
// lFieldInfo.TableName := fTableName;
|
||||
lFieldInfo.FieldName := MVCTableFieldAttribute(lAttribute).FieldName;
|
||||
lFieldInfo.FieldOptions := MVCTableFieldAttribute(lAttribute).FieldOptions;
|
||||
lFieldInfo.DataTypeName := MVCTableFieldAttribute(lAttribute).DataTypeName;
|
||||
@ -1010,10 +1023,11 @@ begin
|
||||
OnValidation(TMVCEntityAction.eaCreate);
|
||||
OnBeforeInsert;
|
||||
OnBeforeInsertOrUpdate;
|
||||
if fMap.NonTransientFieldsCount = 0 then
|
||||
if fMap.WritableFieldsCount = 0 then
|
||||
begin
|
||||
raise EMVCActiveRecord.CreateFmt
|
||||
('Cannot insert an entity if all fields are transient. Class [%s] mapped on table [%s]', [ClassName, fTableName]);
|
||||
('Cannot insert an entity if all fields are not writable or transient. Class [%s] mapped on table [%s]',
|
||||
[ClassName, fTableName]);
|
||||
end;
|
||||
if (foAutoGenerated in fPrimaryKeyOptions) then
|
||||
begin
|
||||
@ -1049,7 +1063,7 @@ begin
|
||||
lSQL := Self.SQLGenerator.CreateSelectCount(fTableName);
|
||||
if not RQL.IsEmpty then
|
||||
begin
|
||||
lSQL := lSQL + fSQLGenerator.CreateSQLWhereByRQL(RQL, GetMapping, False, True);
|
||||
lSQL := lSQL + fSQLGenerator.CreateSQLWhereByRQL(RQL, GetMapping, false, True);
|
||||
end;
|
||||
Result := GetScalar(lSQL, []);
|
||||
end;
|
||||
@ -1135,19 +1149,24 @@ class function TMVCActiveRecordHelper.GetByPK<T>(const aValue: int64;
|
||||
const RaiseExceptionIfNotFound: Boolean = True): T;
|
||||
var
|
||||
lActiveRecord: TMVCActiveRecord;
|
||||
lLoaded: Boolean;
|
||||
begin
|
||||
Result := T.Create;
|
||||
lActiveRecord := TMVCActiveRecord(Result);
|
||||
if not lActiveRecord.LoadByPK(aValue) then
|
||||
|
||||
try
|
||||
lLoaded := lActiveRecord.LoadByPK(aValue);
|
||||
except
|
||||
FreeAndNil(Result);
|
||||
raise;
|
||||
end;
|
||||
|
||||
if not lLoaded then
|
||||
begin
|
||||
Result.Free;
|
||||
FreeAndNil(Result);
|
||||
if RaiseExceptionIfNotFound then
|
||||
begin
|
||||
raise EMVCActiveRecordNotFound.Create('Data not found');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@ -2247,7 +2266,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCActiveRecord.SetTableName(const Value: String);
|
||||
procedure TMVCActiveRecord.SetTableName(const Value: string);
|
||||
begin
|
||||
fTableName := Value;
|
||||
end;
|
||||
@ -2361,7 +2380,7 @@ begin
|
||||
OnValidation(TMVCEntityAction.eaUpdate);
|
||||
OnBeforeUpdate;
|
||||
OnBeforeInsertOrUpdate;
|
||||
if fMap.NonTransientFieldsCount = 0 then
|
||||
if fMap.WritableFieldsCount = 0 then
|
||||
begin
|
||||
raise EMVCActiveRecord.CreateFmt
|
||||
('Cannot update an entity if all fields are transient. Class [%s] mapped on table [%s]', [ClassName, fTableName]);
|
||||
@ -2378,7 +2397,7 @@ begin
|
||||
begin
|
||||
fChildren := TObjectList<TObject>.Create(True);
|
||||
end;
|
||||
if not(fChildren.Contains(ChildObject)) and (not (ChildObject = Self)) then
|
||||
if not(fChildren.Contains(ChildObject)) and (not(ChildObject = Self)) then
|
||||
begin
|
||||
fChildren.Add(ChildObject);
|
||||
end;
|
||||
@ -2659,7 +2678,14 @@ begin
|
||||
Result := Copy(Result, 1, Length(Result) - Length(Delimiter));
|
||||
if not PKFieldName.IsEmpty then
|
||||
begin
|
||||
Result := GetFieldNameForSQL(PKFieldName) + ', ' + Result;
|
||||
if not Result.IsEmpty then
|
||||
begin
|
||||
Result := GetFieldNameForSQL(PKFieldName) + ', ' + Result
|
||||
end
|
||||
else
|
||||
begin
|
||||
Result := GetFieldNameForSQL(PKFieldName)
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -2744,20 +2770,27 @@ end;
|
||||
constructor TFieldsMap.Create;
|
||||
begin
|
||||
inherited Create([doOwnsValues]);
|
||||
fNonTransientFieldsCount := 0;
|
||||
fWritableFieldsCount := -1;
|
||||
fReadableFieldsCount := -1;
|
||||
end;
|
||||
|
||||
procedure TFieldsMap.EndUpdates;
|
||||
var
|
||||
lPair: TPair<TRTTIField, TFieldInfo>;
|
||||
begin
|
||||
fNonTransientFieldsCount := 0;
|
||||
fWritableFieldsCount := 0;
|
||||
fReadableFieldsCount := 0;
|
||||
for lPair in Self do
|
||||
begin
|
||||
lPair.Value.EndUpdates;
|
||||
if not(foTransient in lPair.Value.FieldOptions) then
|
||||
// if not(foTransient in lPair.Value.FieldOptions) then
|
||||
if lPair.Value.Writeable then
|
||||
begin
|
||||
Inc(fNonTransientFieldsCount);
|
||||
Inc(fWritableFieldsCount);
|
||||
end;
|
||||
if lPair.Value.Readable then
|
||||
begin
|
||||
Inc(fReadableFieldsCount);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@ -2781,8 +2814,18 @@ end;
|
||||
|
||||
procedure TFieldInfo.EndUpdates;
|
||||
begin
|
||||
Writeable := (not FieldName.IsEmpty) and (not((foAutoGenerated in FieldOptions) or (foTransient in FieldOptions)));
|
||||
Readable := not(foTransient in FieldOptions) and (not FieldName.IsEmpty);
|
||||
if FieldName.IsEmpty then
|
||||
begin
|
||||
Writeable := false;
|
||||
Readable := false;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// Writeable := (not (foReadOnly in FieldOptions)) and (not((foAutoGenerated in FieldOptions) or (foTransient in FieldOptions)));
|
||||
Writeable := ((FieldOptions * [foReadOnly, foTransient, foAutoGenerated]) = []);
|
||||
// Readable := (not (foWriteOnly in FieldOptions)) and (not(foTransient in FieldOptions));
|
||||
Readable := (FieldOptions * [foWriteOnly, foTransient]) = [];
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
@ -167,7 +167,14 @@ end;
|
||||
|
||||
function TRQLPostgreSQLCompiler.RQLLimitToSQL(const aRQLLimit: TRQLLimit): string;
|
||||
begin
|
||||
Result := Format(' /*limit*/ LIMIT %d OFFSET %d', [aRQLLimit.Count, aRQLLimit.Start]);
|
||||
if aRQLLimit.Start = 0 then
|
||||
begin
|
||||
Result := Format(' /*limit*/ LIMIT %d', [aRQLLimit.Count]);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Result := Format(' /*limit*/ LIMIT %d OFFSET %d', [aRQLLimit.Count, aRQLLimit.Start]);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TRQLPostgreSQLCompiler.RQLLogicOperatorToSQL(const aRQLFIlter: TRQLLogicOperator): string;
|
||||
|
@ -91,6 +91,7 @@ var
|
||||
lPKInInsert: Boolean;
|
||||
begin
|
||||
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
|
||||
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
lSB.Append('INSERT INTO ' + TableName + '(');
|
||||
|
@ -63,6 +63,7 @@ var
|
||||
lPKInInsert: Boolean;
|
||||
begin
|
||||
lPKInInsert := (not PKFieldName.IsEmpty); // and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
|
||||
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
lSB.Append('INSERT INTO ' + TableName + '(');
|
||||
|
@ -88,6 +88,7 @@ var
|
||||
lPKInInsert: Boolean;
|
||||
begin
|
||||
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
|
||||
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
lSB.Append('INSERT INTO ' + TableName + '(');
|
||||
|
@ -88,6 +88,7 @@ var
|
||||
lPKInInsert: Boolean;
|
||||
begin
|
||||
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
|
||||
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
lSB.Append('INSERT INTO ' + TableName + '(');
|
||||
|
@ -99,6 +99,7 @@ var
|
||||
lPKInInsert: Boolean;
|
||||
begin
|
||||
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
|
||||
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
lSB.Append('INSERT INTO ' + GetTableNameForSQL(TableName) + ' (');
|
||||
@ -146,6 +147,11 @@ function TMVCSQLGeneratorPostgreSQL.CreateSelectByPKSQL(
|
||||
const Map: TFieldsMap; const PKFieldName: string;
|
||||
const PKOptions: TMVCActiveRecordFieldOptions): string;
|
||||
begin
|
||||
if PKFieldName.IsEmpty then
|
||||
begin
|
||||
raise EMVCActiveRecord.Create('No primary key provided. [HINT] Define a primary key field adding foPrimaryKey in field options.');
|
||||
end;
|
||||
|
||||
Result := CreateSelectSQL(TableName, Map, PKFieldName, PKOptions) + ' WHERE ' +
|
||||
GetFieldNameForSQL(PKFieldName) + '= :' + GetParamNameForSQL(PKFieldName); // IntToStr(PrimaryKeyValue);
|
||||
end;
|
||||
|
@ -87,6 +87,7 @@ var
|
||||
lPKInInsert: Boolean;
|
||||
begin
|
||||
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
|
||||
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
lSB.Append('INSERT INTO ' + TableName + ' (');
|
||||
|
@ -736,7 +736,7 @@ var
|
||||
Attrs: TArray<TCustomAttribute>;
|
||||
Attr: TCustomAttribute;
|
||||
begin
|
||||
{ TODO -oDanieleT -cGeneral : in un rendering di una lista, quante volte viene chiamata questa funzione?}
|
||||
{ TODO -oDanieleT -cGeneral : in un rendering di una lista, quante volte viene chiamata questa funzione? }
|
||||
{ Tante volte, ma eliminando tutta la logica si guadagnerebbe al massiom il 6% nel caso tipico, forse non vale la pena di aggiungere una cache apposita }
|
||||
Result := AProperty.Name;
|
||||
|
||||
@ -1006,6 +1006,7 @@ var
|
||||
lInternalStream: TStream;
|
||||
lSStream: TStringStream;
|
||||
lValue: TValue;
|
||||
lStrValue: string;
|
||||
{$IF not Defined(TokyoOrBetter)}
|
||||
lFieldValue: string;
|
||||
{$ENDIF}
|
||||
@ -1023,7 +1024,35 @@ begin
|
||||
case AField.DataType of
|
||||
ftString, ftWideString:
|
||||
begin
|
||||
aRTTIField.SetValue(AObject, AField.AsString);
|
||||
// mysql tinytext is identified as string, but raises an Invalid Class Cast
|
||||
// so we need to do some more checks...
|
||||
case aRTTIField.FieldType.TypeKind of
|
||||
tkString, tkUString:
|
||||
begin
|
||||
aRTTIField.SetValue(AObject, AField.AsString);
|
||||
end;
|
||||
tkClass: { mysql - maps a tiny field, identified as string, into a TStream }
|
||||
begin
|
||||
lInternalStream := aRTTIField.GetValue(AObject).AsObject as TStream;
|
||||
if lInternalStream = nil then
|
||||
begin
|
||||
raise EMVCException.CreateFmt('Property target for %s field is nil. [HINT] Initialize the stream before load data', [AField.FieldName]);
|
||||
end;
|
||||
lInternalStream.Size := 0;
|
||||
lStrValue := AField.AsString;
|
||||
if not lStrValue.IsEmpty then
|
||||
begin
|
||||
lInternalStream.Write(lStrValue, Length(lStrValue));
|
||||
lInternalStream.Position := 0;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
raise EMVCException.CreateFmt('Unsupported FieldType (%d) for field %s',
|
||||
[Ord(AField.DataType), AField.FieldName]);
|
||||
end;
|
||||
end;
|
||||
// aRTTIField.SetValue(AObject, AField.AsString);
|
||||
end;
|
||||
ftLargeint, ftAutoInc:
|
||||
begin
|
||||
|
Loading…
Reference in New Issue
Block a user