Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: |
|
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
aa4b00652c8ee0035935a7770fc17098 |
User & Date: | ab 2011-03-21 09:09:26 |
2011-03-21
| ||
10:09 | fixed issue in TSQLRestClientDB.URI: wrong InternalState returned check-in: eac035a226 user: ab tags: trunk | |
09:09 |
| |
2011-03-19
| ||
08:41 | Delphi 2009/2010/XE compatibility fix + UI compilation with TMS check-in: 6834918946 user: ab tags: trunk | |
Changes to SQLite3/SQLite3Commons.pas.
324 325 326 327 328 329 330 331 332 333 334 335 336 337 ... 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 .... 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 .... 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 .... 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 .... 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 .... 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 ..... 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 ..... 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 ..... 12261 12262 12263 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 ..... 12296 12297 12298 12299 12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 ..... 14411 14412 14413 14414 14415 14416 14417 14418 14419 14420 14421 14422 14423 14424 14425 14426 14427 ..... 14648 14649 14650 14651 14652 14653 14654 14655 14656 14657 14658 14659 14660 14661 14662 |
- enhanced TPropInfo.GetLongStrValue/SetLongStrValue methods, now converting RawUnicode, WinAnsiString, TSQLRawBlob and AnsiString properties - now ensure that no published property named ID or RowID was defined (this unique primary key field must be handled directly by TSQLRecord) - TSQLModel implementation speed up, in case of a huge number of registered TSQLRecord in the database Model - added a magic pattern check to ignore broadcasted WM_COPYDATA message String usage in the Synopse SQLite3 database framework: - RawUTF8 is used for every internal data usage, since both SQLite3 and JSON do expect UTF-8 encoding - WinAnsiString where WinAnsi-encoded AnsiString (code page 1252) are needed - generic string for i18n (in unit SQLite3i18n), i.e. text ready to be ................................................................................ { ************ classes to access SQLite3 database data } const /// maximum number of Tables in a Database Model // - this constant is used internaly to optimize memory usage in the // generated asm code MAX_SQLTABLES = 64; type /// used to store bit set for all available Tables in a Database Model // - with current MAX_SQLTABLES value, uses 8 bytes of memory, therefore is // sometimes mapped to an Int64 TSQLFieldTables = set of 0..MAX_SQLTABLES-1; /// a String used to store the BLOB content // - equals RawByteString for byte storage, to force no implicit charset // conversion, thatever the codepage of the resulting string is // - will identify a sftBlob field type, if used to define such a published // property ................................................................................ /// special comparaison function for sorting sftDateTime // UTF-8 encoded values in the SQLite3 database or JSON content function UTF8CompareISO8601(P1,P2: PUTF8Char): PtrInt; const /// Supervisor Table access right, i.e. alllmighty over all fields ALL_ACCESS_RIGHTS = [0..MAX_SQLFIELDS-1]; /// Supervisor Database access right, i.e. allmighty over all Tables SUPERVISOR_ACCESS_RIGHTS: TSQLAccessRights = (GET: ALL_ACCESS_RIGHTS; POST: ALL_ACCESS_RIGHTS; PUT: ALL_ACCESS_RIGHTS; DELETE: ALL_ACCESS_RIGHTS); /// special TSQLFieldBits value containing all field bits set to 1 ................................................................................ end; P[-1] := ')'; end; // Assert(P-pointer(result)=length(result)); end; var F: integer; FU: RawUTF8; Fields2, Values: array[0..MAX_SQLFIELDS] of RawUTF8; begin result := ''; if P=nil then exit; Len := 0; if pointer(Fields)=nil then begin // get "COL1"="VAL1" pairs, stopping at '}' or ']' ................................................................................ result := fID; end; function TSQLRecord.GetHasBlob: boolean; begin if Self=nil then result := false else result := Int64(RecordProps.BlobFieldsBits)<>0; end; function TSQLRecord.GetSimpleFieldCount: integer; begin if Self=nil then result := 0 else result := length(RecordProps.SimpleFields); ................................................................................ end; {$endif} function TSQLRecord.Filter(const aFields: TSQLFieldBits): boolean; var f, i: integer; Value, Old: RawUTF8; begin result := (Int64(aFields)=0); if (self=nil) or result then // avoid GPF and handle case if no field was selected exit; with RecordProps do if Filters=nil then // no filter set yet -> process OK result := true else begin ................................................................................ aInvalidFieldIndex: PInteger): string; var f, i: integer; Value: RawUTF8; Validate: TSynValidate; ValidateRest: TSynValidateRest absolute Validate; begin result := ''; if (self=nil) or (Int64(aFields)=0) then // avoid GPF and handle case if no field was selected exit; with RecordProps do for f := 0 to high(Fields) do if not(FieldType[f] in [sftUnknown,sftMany]) then begin if (Filters<>nil) and (Filters[f]<>nil) then for i := 0 to Filters[f].Count-1 do begin ................................................................................ BlobData: RawByteString; i: integer; begin result := false; if (Self=nil) or (Value=nil) or (Value.fID<=0) then exit; with Value.RecordProps do if Int64(BlobFieldsBits)<>0 then begin URL := FormatUTF8('%/%/%/',[Model.Root,SQLTableName,Value.fID]); for i := 0 to high(Fields) do if i in BlobFieldsBits then // URI is 'ModelRoot/TableName/ID/BlobFieldName' with GET method if URI(FormatUTF8('%%',[URL,FieldsName[i]]),'GET',@BlobData).Lo=200 then SetLongStrProp(Value,Fields[i],BlobData) else exit; ................................................................................ BlobData: RawByteString; i: integer; begin result := false; if (Self=nil) or (Value=nil) or (Value.fID<=0) then exit; with Value.RecordProps do if Int64(BlobFieldsBits)<>0 then begin URL := FormatUTF8('%/%/%/',[Model.Root,SQLTableName,Value.fID]); for i := 0 to high(Fields) do if i in BlobFieldsBits then begin GetLongStrProp(Value,Fields[i],BlobData); // URI is 'ModelRoot/TableName/ID/BlobFieldName' with PUT method if URI(FormatUTF8('%%',[URL,FieldsName[i]]),'PUT',nil,nil,@BlobData).Lo<>200 then exit; ................................................................................ exit; end; if fValue.Count=0 then result := 1 else // default ID for a void table result := TSQLRecord(fValue.List[fValue.Count-1]).fID+1; // tricky new ID compute Rec := fStoredClass.Create; Rec.FillFrom(SentData); if (Int64(fIsUnique)<>0) and not AreUniqueFieldsOK(Rec) then begin result := 0; // mark error Rec.Free; exit; end; Rec.fID := result; fValue.Add(Rec); fModified := true; ................................................................................ Owner.OnUpdateEvent(self,seAdd,Rec.RecordClass,result); end; function TSQLRestServerStaticInMemory.AreUniqueFieldsOK(Rec: TSQLRecord): boolean; var F, i: integer; Val: RawUTF8; begin if Int64(fIsUnique)<>0 then // ensure UNIQUE fields correctness with fStoredClassProps do begin result := false; for F := 0 to High(Fields) do if F in fIsUnique then begin Val := Fields[F]^.GetValue(Rec,false); for i := 0 to fValue.Count-1 do ................................................................................ begin assert(aTable<>nil); // should not be called directly, but via PropsCreate() Table := aTable; SQLTableName := GetDisplayNameFromClass(aTable); ClassProp := InternalClassProp(aTable); assert(ClassProp<>nil); nProps := PClassProp(aTable)^.FieldCountWithParents; if nProps>=MAX_SQLFIELDS then // for now, we store Fields in an Int64 raise Exception.CreateFmt('%s has too many fields: %d>%d', [aTable.ClassName,nProps,MAX_SQLFIELDS-1]); SetLength(FieldType,nProps); SetLength(Fields,nProps); SetLength(FieldsName,nProps); SetLength(ManyFields,nProps); SetLength(SimpleFields,nProps); MainField[false] := -1; MainField[true] := -1; ................................................................................ inherited; end; function TSQLRecordProperties.FieldIndexsFromRawUTF8(const aFields: array of RawUTF8; var Bits: TSQLFieldBits): boolean; var f,ndx: integer; begin Int64(Bits) := 0; result := false; if self=nil then exit; for f := 0 to high(aFields) do begin ndx := FieldIndexFromRawUTF8(aFields[f]); if ndx<0 then exit; // invalid field name |
> > > > | < < | | | | | | | | | | | | |
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 ... 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 .... 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 .... 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 .... 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 .... 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 .... 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 ..... 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 ..... 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 ..... 12263 12264 12265 12266 12267 12268 12269 12270 12271 12272 12273 12274 12275 12276 12277 ..... 12298 12299 12300 12301 12302 12303 12304 12305 12306 12307 12308 12309 12310 12311 12312 ..... 14413 14414 14415 14416 14417 14418 14419 14420 14421 14422 14423 14424 14425 14426 14427 14428 14429 ..... 14650 14651 14652 14653 14654 14655 14656 14657 14658 14659 14660 14661 14662 14663 14664 |
- enhanced TPropInfo.GetLongStrValue/SetLongStrValue methods, now converting RawUnicode, WinAnsiString, TSQLRawBlob and AnsiString properties - now ensure that no published property named ID or RowID was defined (this unique primary key field must be handled directly by TSQLRecord) - TSQLModel implementation speed up, in case of a huge number of registered TSQLRecord in the database Model - added a magic pattern check to ignore broadcasted WM_COPYDATA message - MAX_SQLFIELDS default is still 64, but can now be set to any value (64, 128, and 256 are optimized) so that you can have any number of fields in a Table - MAX_SQLTABLES default is now 256, i.e. you can have up to 256 tables in a TSQLModel instance (you can set any other value, on need) String usage in the Synopse SQLite3 database framework: - RawUTF8 is used for every internal data usage, since both SQLite3 and JSON do expect UTF-8 encoding - WinAnsiString where WinAnsi-encoded AnsiString (code page 1252) are needed - generic string for i18n (in unit SQLite3i18n), i.e. text ready to be ................................................................................ { ************ classes to access SQLite3 database data } const /// maximum number of Tables in a Database Model // - this constant is used internaly to optimize memory usage in the // generated asm code MAX_SQLTABLES = 256; type /// used to store bit set for all available Tables in a Database Model TSQLFieldTables = set of 0..MAX_SQLTABLES-1; /// a String used to store the BLOB content // - equals RawByteString for byte storage, to force no implicit charset // conversion, thatever the codepage of the resulting string is // - will identify a sftBlob field type, if used to define such a published // property ................................................................................ /// special comparaison function for sorting sftDateTime // UTF-8 encoded values in the SQLite3 database or JSON content function UTF8CompareISO8601(P1,P2: PUTF8Char): PtrInt; const /// Supervisor Table access right, i.e. alllmighty over all fields ALL_ACCESS_RIGHTS = [0..MAX_SQLTABLES-1]; /// Supervisor Database access right, i.e. allmighty over all Tables SUPERVISOR_ACCESS_RIGHTS: TSQLAccessRights = (GET: ALL_ACCESS_RIGHTS; POST: ALL_ACCESS_RIGHTS; PUT: ALL_ACCESS_RIGHTS; DELETE: ALL_ACCESS_RIGHTS); /// special TSQLFieldBits value containing all field bits set to 1 ................................................................................ end; P[-1] := ')'; end; // Assert(P-pointer(result)=length(result)); end; var F: integer; FU: RawUTF8; Fields2, Values: array[0..MAX_SQLFIELDS-1] of RawUTF8; begin result := ''; if P=nil then exit; Len := 0; if pointer(Fields)=nil then begin // get "COL1"="VAL1" pairs, stopping at '}' or ']' ................................................................................ result := fID; end; function TSQLRecord.GetHasBlob: boolean; begin if Self=nil then result := false else result := not IsZero(@RecordProps.BlobFieldsBits); end; function TSQLRecord.GetSimpleFieldCount: integer; begin if Self=nil then result := 0 else result := length(RecordProps.SimpleFields); ................................................................................ end; {$endif} function TSQLRecord.Filter(const aFields: TSQLFieldBits): boolean; var f, i: integer; Value, Old: RawUTF8; begin result := IsZero(@aFields); if (self=nil) or result then // avoid GPF and handle case if no field was selected exit; with RecordProps do if Filters=nil then // no filter set yet -> process OK result := true else begin ................................................................................ aInvalidFieldIndex: PInteger): string; var f, i: integer; Value: RawUTF8; Validate: TSynValidate; ValidateRest: TSynValidateRest absolute Validate; begin result := ''; if (self=nil) or IsZero(@aFields) then // avoid GPF and handle case if no field was selected exit; with RecordProps do for f := 0 to high(Fields) do if not(FieldType[f] in [sftUnknown,sftMany]) then begin if (Filters<>nil) and (Filters[f]<>nil) then for i := 0 to Filters[f].Count-1 do begin ................................................................................ BlobData: RawByteString; i: integer; begin result := false; if (Self=nil) or (Value=nil) or (Value.fID<=0) then exit; with Value.RecordProps do if not IsZero(@BlobFieldsBits) then begin URL := FormatUTF8('%/%/%/',[Model.Root,SQLTableName,Value.fID]); for i := 0 to high(Fields) do if i in BlobFieldsBits then // URI is 'ModelRoot/TableName/ID/BlobFieldName' with GET method if URI(FormatUTF8('%%',[URL,FieldsName[i]]),'GET',@BlobData).Lo=200 then SetLongStrProp(Value,Fields[i],BlobData) else exit; ................................................................................ BlobData: RawByteString; i: integer; begin result := false; if (Self=nil) or (Value=nil) or (Value.fID<=0) then exit; with Value.RecordProps do if not IsZero(@BlobFieldsBits) then begin URL := FormatUTF8('%/%/%/',[Model.Root,SQLTableName,Value.fID]); for i := 0 to high(Fields) do if i in BlobFieldsBits then begin GetLongStrProp(Value,Fields[i],BlobData); // URI is 'ModelRoot/TableName/ID/BlobFieldName' with PUT method if URI(FormatUTF8('%%',[URL,FieldsName[i]]),'PUT',nil,nil,@BlobData).Lo<>200 then exit; ................................................................................ exit; end; if fValue.Count=0 then result := 1 else // default ID for a void table result := TSQLRecord(fValue.List[fValue.Count-1]).fID+1; // tricky new ID compute Rec := fStoredClass.Create; Rec.FillFrom(SentData); if not IsZero(@fIsUnique) and not AreUniqueFieldsOK(Rec) then begin result := 0; // mark error Rec.Free; exit; end; Rec.fID := result; fValue.Add(Rec); fModified := true; ................................................................................ Owner.OnUpdateEvent(self,seAdd,Rec.RecordClass,result); end; function TSQLRestServerStaticInMemory.AreUniqueFieldsOK(Rec: TSQLRecord): boolean; var F, i: integer; Val: RawUTF8; begin if not IsZero(@fIsUnique) then // ensure UNIQUE fields correctness with fStoredClassProps do begin result := false; for F := 0 to High(Fields) do if F in fIsUnique then begin Val := Fields[F]^.GetValue(Rec,false); for i := 0 to fValue.Count-1 do ................................................................................ begin assert(aTable<>nil); // should not be called directly, but via PropsCreate() Table := aTable; SQLTableName := GetDisplayNameFromClass(aTable); ClassProp := InternalClassProp(aTable); assert(ClassProp<>nil); nProps := PClassProp(aTable)^.FieldCountWithParents; if nProps>MAX_SQLFIELDS then raise Exception.CreateFmt('%s has too many fields: %d>%d', [aTable.ClassName,nProps,MAX_SQLFIELDS]); SetLength(FieldType,nProps); SetLength(Fields,nProps); SetLength(FieldsName,nProps); SetLength(ManyFields,nProps); SetLength(SimpleFields,nProps); MainField[false] := -1; MainField[true] := -1; ................................................................................ inherited; end; function TSQLRecordProperties.FieldIndexsFromRawUTF8(const aFields: array of RawUTF8; var Bits: TSQLFieldBits): boolean; var f,ndx: integer; begin fillchar(Bits,sizeof(TSQLFieldBits),0); result := false; if self=nil then exit; for f := 0 to high(aFields) do begin ndx := FieldIndexFromRawUTF8(aFields[f]); if ndx<0 then exit; // invalid field name |
Changes to SynBigTable.pas.
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
....
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
|
exit; // nothing new if Count=0 then begin // no data to refresh Table.AddedField.Free; // do it once Table.AddedField := nil; end else begin // some data to refresh: guess field added, and process Int64(Fields) := 0; for F := 0 to Table.FieldCount-1 do if Table.AddedField.IndexOf(Table.Field[F])<0 then Include(Fields,F); Table.AddedField.Free; // do it once Table.AddedField := nil; RecreateFileContent(Table.UpdateFieldEvent,Fields); end; ................................................................................ if (self=nil) or (Table.AddedField=nil) then exit; // nothing new if fMetaDataCount=0 then begin // no data to refresh Table.AddedField.Free; // do it once Table.AddedField:= nil; exit; end; Int64(AvailableFields) := 0; for F := 0 to Table.FieldCount-1 do if Table.AddedField.IndexOf(Table.Field[F])<0 then Include(AvailableFields,F); Table.AddedField.Free; // do it once Table.AddedField := nil; for i := 0 to Count-1 do fMetaDataRecords[i] := Table.UpdateFieldRecord(pointer(fMetaDataRecords[i]),AvailableFields); |
|
|
|
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
....
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
|
exit; // nothing new if Count=0 then begin // no data to refresh Table.AddedField.Free; // do it once Table.AddedField := nil; end else begin // some data to refresh: guess field added, and process fillchar(Fields,sizeof(Fields),0); for F := 0 to Table.FieldCount-1 do if Table.AddedField.IndexOf(Table.Field[F])<0 then Include(Fields,F); Table.AddedField.Free; // do it once Table.AddedField := nil; RecreateFileContent(Table.UpdateFieldEvent,Fields); end; ................................................................................ if (self=nil) or (Table.AddedField=nil) then exit; // nothing new if fMetaDataCount=0 then begin // no data to refresh Table.AddedField.Free; // do it once Table.AddedField:= nil; exit; end; fillchar(AvailableFields,sizeof(AvailableFields),0); for F := 0 to Table.FieldCount-1 do if Table.AddedField.IndexOf(Table.Field[F])<0 then Include(AvailableFields,F); Table.AddedField.Free; // do it once Table.AddedField := nil; for i := 0 to Count-1 do fMetaDataRecords[i] := Table.UpdateFieldRecord(pointer(fMetaDataRecords[i]),AvailableFields); |
Changes to SynCommons.pas.
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 ... 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 ... 666 667 668 669 670 671 672 673 674 675 676 677 678 679 .... 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 .... 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 .... 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 ..... 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 ..... 16615 16616 16617 16618 16619 16620 16621 16622 16623 16624 16625 16626 16627 16628 ..... 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 ..... 18928 18929 18930 18931 18932 18933 18934 18935 18936 18937 18938 18939 18940 18941 18942 ..... 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 |
- fixed issue in TSynTableFieldProperties: wrong constraint evaluation and index refresh at records update - faster implementation of Move() for Delphi versions with no FastCode inside - great performance improvement in TSynTableFieldProperties for update process - new ConvertCaseUTF8(), UpperCaseU(), LowerCaseU(), Int64ToUInt32(), GetCardinalDef(), IsValidEmail, IsValidIP4Address(), PatchCodePtrUInt(), GetCaptionFromClass(), GetDisplayNameFromClass(), DateTimeToIso8601Text() StrUInt32() and StringBufferToUtf8() procedures or functions (with associated tests) - new grep-like IsMatch() function for basic pattern matching - introducing direct content filtering and validation using TSynFilterOrValidate dedicated classes, for both TSQLRecord and Big Table - filtering is handled via some TSynFilter classes - TSynFilterUpperCase, TSynFilterUpperCaseU, TSynFilterLowerCase, TSynFilterLowerCaseU and TSynFilterTrim e.g. ................................................................................ - dedicated TSynTableFieldProperties.Validate method for validation (e.g. a TSynValidateTableUniqueField instance is created if tfoUnique is in Options) - dedicated TSynTableFieldProperties.Filter method for filtering (using common TSynFilter classes, working at UTF-8 Text content) - introducing the GarbageCollector TObjectList for handling a global garbage collector for instances which must live during the whole executable process (used e.g. to avoid a memory leak for "class var" or such variables) - new BinToBase64, Base64ToBin and IsBase64 conversion functions - new low-level RTTI functions for handling record types: RecordEquals, RecordSave, RecordSaveLength, RecordLoad - new TDynArray object, which is a wrapper around any dynamic array: you can now access to the dynamic array using TList-like properties and methods, e.g. Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort and some new methods like LoadFromStream, SaveToStream, LoadFrom and SaveTo which allow fast binary serialization of any dynamic array, even containing strings or records; a CreateOrderedIndex method is also available to create individual index according to the dynamic array content; and any dynamic array can be serialized as UTF-8 JSON via TTextWriter.AddDynArrayJSON and TDynArray.LoadFromJSON methods } {$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER interface ................................................................................ function Pos(const substr, str: RawUTF8): Integer; overload; inline; {$endif} {$endif LVCL} {$endif PUREPASCAL} {$endif ENHANCEDRTL} /// use our fast RawUTF8 version of IntToStr() // - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009 // - only usefull if our Enhanced Runtime (or LVCL) library is not installed function Int64ToUtf8(Value: Int64): RawUTF8; /// use our fast RawUTF8 version of IntToStr() ................................................................................ { ****************** text buffer and JSON functions and classes ********* } const /// maximum number of fields in a Table // - this constant is used internaly to optimize memory usage in the // generated asm code, and to map TSQLFieldBits into an Int64 MAX_SQLFIELDS = 64; /// number of entries in the TSynCache, 256 is big enough on practice // - code is somewhat faster and easier with a fixed cache size // - don't make the cache too big: may consume too much memory MAX_SYNCACHE = 256; ................................................................................ JSON_BASE64_MAGIC_QUOTE = ord('"')+cardinal(JSON_BASE64_MAGIC) shl 8; /// '"' + UTF-8 encoded \uFFF0 special code JSON_BASE64_MAGIC_QUOTE_VAR: cardinal = JSON_BASE64_MAGIC_QUOTE; type /// used to store bit set for all available fiels in a Table // - with current MAX_SQLFIELDS value, uses 8 bytes of memory, therefore is // sometimes mapped to an Int64 TSQLFieldBits = set of 0..MAX_SQLFIELDS-1; /// simple writer to a Stream, specialized for the TEXT format // - use an internal buffer, faster than string+string // - some dedicated methods is able to encode any data with JSON escape TTextWriter = class protected B, BEnd: PUTF8Char; ................................................................................ procedure SetBit64(var Bits: Int64; aIndex: PtrInt); {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif} /// unset/clear a particular bit into a Int64 bit array (max aIndex is 63) procedure UnSetBit64(var Bits: Int64; aIndex: PtrInt); {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif} { ************ fast ISO-8601 types and conversion routines } /// Date/Time conversion from ISO-8601 // - handle 'YYYYMMDDThhmmss' and 'YYYY-MM-DD hh:mm:ss' format function Iso8601ToDateTime(const S: RawUTF8): TDateTime; ................................................................................ var tmp: RawUTF8; err: integer; begin result := UrlDecodeValue(U, Upper, tmp, Next); if result then Value := GetExtended(pointer(tmp),err); end; function Hash32(const Text: RawByteString): cardinal; {$ifdef PUREPASCAL} // this code is quite as fast as the optimized asm below function SubHash(P: PCardinalArray): cardinal; {$ifdef HASINLINE}inline;{$endif} var s1,s2: cardinal; i, L: PtrInt; ................................................................................ Check(UpperCase(U)=RawUTF8(SysUtils.UpperCase(string(U)))); {$endif} Check(StringToUTF8(UTF8ToString(U))=U); if U='' then continue; Check(UnQuoteSQLString(pointer(QuotedStr(U,'"')),res)<>nil); Check(res=U); end; Check(UnQuoteSQLString('"one two"',U)<>nil); Check(U='one two'); Check(UnQuoteSQLString('one two',U)<>nil); Check(U='ne tw'); Check(UnQuoteSQLString('"one "" two"',U)<>nil); Check(U='one " two'); ................................................................................ AfterFieldModif; end; function TSynTable.CreateJSONWriter(JSON: TStream; Expand, withID: boolean; const Fields: TSQLFieldBits): TJSONWriter; var i, n: integer; begin if (self=nil) or ((Int64(Fields)=0) and not withID) then begin result := nil; // no data to retrieve exit; end; // get col max count if withID then n := 1 else n := 0; ................................................................................ function TSynTable.IterateJSONValues(Sender: TObject; Opaque: pointer; ID: integer; Data: pointer; DataLen: integer): boolean; var Statement: TSynTableStatement absolute Opaque; F: TSynTableFieldProperties; FIndex: cardinal; begin // note: we should have handled -2 (=COUNT) case already if (self=nil) or (Statement=nil) or (Data=nil) or (Statement.WhereValueSBF='') or (Int64(Statement.Fields)=0) then begin result := false; exit; end; result := true; FIndex := Statement.WhereField; if FIndex=0 then begin if ID<>Statement.WhereValueInteger then ................................................................................ GetFieldProp; TableName := Prop; WhereField := -2; // mark COUNT(*) WhereValue := 'COUNT'; // not void exit; end else begin withID := false; Int64(Fields) := 0; if not SetFields then exit else // we need at least one field name if P^=',' then repeat while P^ in [',',' '] do inc(P); // trim left until not SetFields; // add other CSV field names end; |
| | | > > | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | |
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 ... 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 ... 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 .... 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 .... 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 .... 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 ..... 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 10592 10593 ..... 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 ..... 18763 18764 18765 18766 18767 18768 18769 18770 18771 18772 18773 18774 18775 18776 18777 ..... 18973 18974 18975 18976 18977 18978 18979 18980 18981 18982 18983 18984 18985 18986 18987 ..... 20384 20385 20386 20387 20388 20389 20390 20391 20392 20393 20394 20395 20396 20397 20398 |
- fixed issue in TSynTableFieldProperties: wrong constraint evaluation and index refresh at records update - faster implementation of Move() for Delphi versions with no FastCode inside - great performance improvement in TSynTableFieldProperties for update process - new ConvertCaseUTF8(), UpperCaseU(), LowerCaseU(), Int64ToUInt32(), GetCardinalDef(), IsValidEmail, IsValidIP4Address(), PatchCodePtrUInt(), GetCaptionFromClass(), GetDisplayNameFromClass(), DateTimeToIso8601Text() StrUInt32(), StringBufferToUtf8() IsZero() procedures or functions (with associated tests) - new grep-like IsMatch() function for basic pattern matching - introducing direct content filtering and validation using TSynFilterOrValidate dedicated classes, for both TSQLRecord and Big Table - filtering is handled via some TSynFilter classes - TSynFilterUpperCase, TSynFilterUpperCaseU, TSynFilterLowerCase, TSynFilterLowerCaseU and TSynFilterTrim e.g. ................................................................................ - dedicated TSynTableFieldProperties.Validate method for validation (e.g. a TSynValidateTableUniqueField instance is created if tfoUnique is in Options) - dedicated TSynTableFieldProperties.Filter method for filtering (using common TSynFilter classes, working at UTF-8 Text content) - introducing the GarbageCollector TObjectList for handling a global garbage collector for instances which must live during the whole executable process (used e.g. to avoid a memory leak for "class var" or such variables) - new BinToBase64, Base64ToBin and IsBase64 *fast* conversion functions - new low-level RTTI functions for handling record types: RecordEquals, RecordSave, RecordSaveLength, RecordLoad - new TDynArray object, which is a wrapper around any dynamic array: you can now access to the dynamic array using TList-like properties and methods, e.g. Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort and some new methods like LoadFromStream, SaveToStream, LoadFrom and SaveTo which allow fast binary serialization of any dynamic array, even containing strings or records; a CreateOrderedIndex method is also available to create individual index according to the dynamic array content; and any dynamic array can be serialized as UTF-8 JSON via TTextWriter.AddDynArrayJSON and TDynArray.LoadFromJSON methods } {$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER interface ................................................................................ function Pos(const substr, str: RawUTF8): Integer; overload; inline; {$endif} {$endif LVCL} {$endif PUREPASCAL} {$endif ENHANCEDRTL} /// use our fast RawUTF8 version of IntToStr() // - without any slow UnicodeString=String->AnsiString conversion for Delphi 2009 // - only usefull if our Enhanced Runtime (or LVCL) library is not installed function Int64ToUtf8(Value: Int64): RawUTF8; /// use our fast RawUTF8 version of IntToStr() ................................................................................ { ****************** text buffer and JSON functions and classes ********* } const /// maximum number of fields in a Table // - default is 64, but can be set to any value (64, 128 and 256 are optimized) // - this constant is used internaly to optimize memory usage in the // generated asm code MAX_SQLFIELDS = 64; /// number of entries in the TSynCache, 256 is big enough on practice // - code is somewhat faster and easier with a fixed cache size // - don't make the cache too big: may consume too much memory MAX_SYNCACHE = 256; ................................................................................ JSON_BASE64_MAGIC_QUOTE = ord('"')+cardinal(JSON_BASE64_MAGIC) shl 8; /// '"' + UTF-8 encoded \uFFF0 special code JSON_BASE64_MAGIC_QUOTE_VAR: cardinal = JSON_BASE64_MAGIC_QUOTE; type /// used to store bit set for all available fiels in a Table // - with current MAX_SQLFIELDS value, 256 bits uses 64 bytes of memory TSQLFieldBits = set of 0..MAX_SQLFIELDS-1; PSQLFieldBits = ^TSQLFieldBits; /// simple writer to a Stream, specialized for the TEXT format // - use an internal buffer, faster than string+string // - some dedicated methods is able to encode any data with JSON escape TTextWriter = class protected B, BEnd: PUTF8Char; ................................................................................ procedure SetBit64(var Bits: Int64; aIndex: PtrInt); {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif} /// unset/clear a particular bit into a Int64 bit array (max aIndex is 63) procedure UnSetBit64(var Bits: Int64; aIndex: PtrInt); {$ifdef PUREPASCAL} {$ifdef HASINLINE}inline;{$endif} {$endif} /// returns TRUE if all bytes equal zero function IsZero(P: pointer; Length: integer): boolean; overload; /// returns TRUE if no bit inside this TSQLFieldBits is set function IsZero(Fields: PSQLFieldBits): boolean; overload; {$ifdef HASINLINE}inline;{$endif} { ************ fast ISO-8601 types and conversion routines } /// Date/Time conversion from ISO-8601 // - handle 'YYYYMMDDThhmmss' and 'YYYY-MM-DD hh:mm:ss' format function Iso8601ToDateTime(const S: RawUTF8): TDateTime; ................................................................................ var tmp: RawUTF8; err: integer; begin result := UrlDecodeValue(U, Upper, tmp, Next); if result then Value := GetExtended(pointer(tmp),err); end; function IsZero(P: pointer; Length: integer): boolean; var i: integer; begin result := false; for i := 1 to Length shr 4 do // 16 bytes (4 DWORD) by loop - aligned read if (PCardinalArray(P)^[0]<>0) or (PCardinalArray(P)^[1]<>0) or (PCardinalArray(P)^[2]<>0) or (PCardinalArray(P)^[3]<>0) then exit else inc(PtrUInt(P),16); for i := 1 to Length and 15 do if PByte(P)^<>0 then exit else inc(PtrUInt(P)); result := true; end; {$warnings off} function IsZero(Fields: PSQLFieldBits): boolean; overload; begin result := (Fields<>nil) and {$if MAX_SQLFIELDS=64} (PInt64(Fields)^=0) {$elseif MAX_SQLFIELDS=128} (PInt64Array(Fields)^[0]=0) and (PInt64Array(Fields)^[1]=0) {$elseif MAX_SQLFIELDS=256} (PInt64Array(Fields)^[0]=0) and (PInt64Array(Fields)^[1]=0) and (PInt64Array(Fields)^[2]=0) and (PInt64Array(Fields)^[3]=0) {$else} IsZero(Fields,sizeof(TSQLFieldBits)) {$ifend} end; {$warnings on} function Hash32(const Text: RawByteString): cardinal; {$ifdef PUREPASCAL} // this code is quite as fast as the optimized asm below function SubHash(P: PCardinalArray): cardinal; {$ifdef HASINLINE}inline;{$endif} var s1,s2: cardinal; i, L: PtrInt; ................................................................................ Check(UpperCase(U)=RawUTF8(SysUtils.UpperCase(string(U)))); {$endif} Check(StringToUTF8(UTF8ToString(U))=U); if U='' then continue; Check(UnQuoteSQLString(pointer(QuotedStr(U,'"')),res)<>nil); Check(res=U); Check(not IsZero(pointer(W),length(W))); fillchar(pointer(W)^,length(W),0); Check(IsZero(pointer(W),length(W))); end; Check(UnQuoteSQLString('"one two"',U)<>nil); Check(U='one two'); Check(UnQuoteSQLString('one two',U)<>nil); Check(U='ne tw'); Check(UnQuoteSQLString('"one "" two"',U)<>nil); Check(U='one " two'); ................................................................................ AfterFieldModif; end; function TSynTable.CreateJSONWriter(JSON: TStream; Expand, withID: boolean; const Fields: TSQLFieldBits): TJSONWriter; var i, n: integer; begin if (self=nil) or (IsZero(@Fields) and not withID) then begin result := nil; // no data to retrieve exit; end; // get col max count if withID then n := 1 else n := 0; ................................................................................ function TSynTable.IterateJSONValues(Sender: TObject; Opaque: pointer; ID: integer; Data: pointer; DataLen: integer): boolean; var Statement: TSynTableStatement absolute Opaque; F: TSynTableFieldProperties; FIndex: cardinal; begin // note: we should have handled -2 (=COUNT) case already if (self=nil) or (Statement=nil) or (Data=nil) or (Statement.WhereValueSBF='') or IsZero(@Statement.Fields) then begin result := false; exit; end; result := true; FIndex := Statement.WhereField; if FIndex=0 then begin if ID<>Statement.WhereValueInteger then ................................................................................ GetFieldProp; TableName := Prop; WhereField := -2; // mark COUNT(*) WhereValue := 'COUNT'; // not void exit; end else begin withID := false; fillchar(Fields,sizeof(Fields),0); if not SetFields then exit else // we need at least one field name if P^=',' then repeat while P^ in [',',' '] do inc(P); // trim left until not SetFields; // add other CSV field names end; |