forked from synopse/mORMot2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmormot.orm.base.pas
11539 lines (10723 loc) · 401 KB
/
mormot.orm.base.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// Object-Relational-Mapping (ORM) Low-Level Process
// - this unit is a part of the Open Source Synopse mORMot framework 2,
// licensed under a MPL/GPL/LGPL three license - see LICENSE.md
unit mormot.orm.base;
{
*****************************************************************************
Low-Level Basic Types and Definitions for our RESTful ORM
- Shared ORM/JSON Fields and Values Definitions
- ORM Ready UTF-8 Comparison Functions
- TOrmWriter Class for TOrm Serialization
- TOrmPropInfo ORM / RTTI Classes
- Abstract TOrmTableAbstract Parent Class
- TOrmTableRowVariant Custom Variant Type
- TOrmLocks and TOrmCacheTable Basic Structures
- Abstract TOrmPropertiesAbstract Parent Class
*****************************************************************************
}
interface
{$I ..\mormot.defines.inc}
uses
sysutils,
classes,
variants,
contnrs,
{$ifdef ISDELPHI}
typinfo, // for proper Delphi inlining
{$endif ISDELPHI}
mormot.core.base,
mormot.core.os,
mormot.core.buffers,
mormot.core.unicode,
mormot.core.text,
mormot.core.datetime,
mormot.core.variants,
mormot.core.data,
mormot.core.rtti,
mormot.core.json,
mormot.core.threads,
mormot.core.perf,
mormot.core.zip, // for ODS export
mormot.crypt.secure, // for TSynUniqueIdentifierBits
mormot.db.core;
{ ************ Shared ORM/JSON Fields and Values Definitions }
const
/// maximum number of Tables in a Database Model
// - this constant is used internally to optimize memory usage in the
// generated asm code
// - you should not change it to a value lower than expected in an existing
// database (e.g. as expected by TOrmAccessRights or such)
MAX_TABLES = 256;
/// after how many parameters inlining is not worth it
INLINED_MAX = 10;
/// the used TAuthSession.IDCardinal value if the session not started yet
// - i.e. if the session handling is still in its handshaking phase
CONST_AUTHENTICATION_SESSION_NOT_STARTED = 0;
/// the used TAuthSession.IDCardinal value if authentication mode is not set
// - i.e. if TRest.HandleAuthentication equals FALSE
CONST_AUTHENTICATION_NOT_USED = 1;
/// maximum handled dimension for TOrmRTree
// - this value is the one used by SQLite3 R-Tree virtual table
RTREE_MAX_DIMENSION = 5;
type
/// generic parent class of all custom Exception types of this unit
// - will also call SetDbError() with the resulting UTF-8 message text
EOrmException = class(ECoreDBException);
/// used to store bit set for all available Tables in a Database Model
// - with default MAX_TABLES=256, consumes 32 bytes
TOrmTableBits = set of 0 .. MAX_TABLES - 1;
/// a reference to another record in any table in the database Model
// - stored as a 64-bit signed integer (just like the TID type)
// - type cast any value of TRecordReference with the RecordRef object below
// for easy access to its content
// - use TRest.Retrieve(Reference) to get a record value
// - don't change associated TOrmModel tables order, since TRecordReference
// depends on it to store the Table type in its highest bits
// - when the pointed record will be deleted, this property will be set to 0 by
// TRestOrmServer.AfterDeleteForceCoherency(), as SQL 'ON DELETE SET DEFAULT'
// - could be defined as value in a TOrm property as such:
// ! property AnotherRecord: TRecordReference read fAnotherRecord write fAnotherRecord;
TRecordReference = type Int64;
/// a reference to another record in any table in the database Model
// - stored as a 64-bit signed integer (just like the TID type)
// - type cast any value of TRecordReference with the RecordRef object below
// for easy access to its content
// - use TRest.Retrieve(Reference) to get a record value
// - don't change associated TOrmModel tables order, since TRecordReference
// depends on it to store the Table type in its highest bits
// - when the pointed record will be deleted, any record containg a matching
// property will be deleted by TRestOrmServer.AfterDeleteForceCoherency(),
// as SQL 'ON DELETE CASCADE'
// - could be defined as value in a TOrm property as such:
// ! property AnotherRecord: TRecordReferenceToBeDeleted
// ! read fAnotherRecord write fAnotherRecord;
TRecordReferenceToBeDeleted = type TRecordReference;
/// an Int64-encoded date and time of the latest update of a record
// - can be used as published property field in TOrm for oftModTime:
// if any such property is defined in the table, it will be auto-filled with
// the server timestamp corresponding to the latest record update
// - use internally for computation an abstract "year" of 16 months of 32 days
// of 32 hours of 64 minutes of 64 seconds - faster than TDateTime
// - use TimeLogFromDateTime/TimeLogToDateTime/TimeLogNow/Iso8601ToTimeLog
// functions, or type-cast the value with a TTimeLogBits memory structure for
// direct access to its bit-oriented content (or via PTimeLogBits pointer)
// - could be defined as value in a TOrm property as such:
// ! property LastModif: TModTime read fLastModif write fLastModif;
TModTime = type TTimeLog;
/// an Int64-encoded date and time of the record creation
// - can be used as published property field in TOrm for oftCreateTime:
// if any such property is defined in the table, it will be auto-filled with
// the server timestamp corresponding to the record creation
// - use internally for computation an abstract "year" of 16 months of 32 days
// of 32 hours of 64 minutes of 64 seconds - faster than TDateTime
// - use TimeLogFromDateTime/TimeLogToDateTime/TimeLogNow/Iso8601ToTimeLog
// functions, or type-cast the value with a TTimeLogBits memory structure for
// direct access to its bit-oriented content (or via PTimeLogBits pointer)
// - could be defined as value in a TOrm property as such:
// ! property CreatedAt: TModTime read fCreatedAt write fCreatedAt;
TCreateTime = type TTimeLog;
/// the Int64/TID of the TAuthUser currently logged
// - can be used as published property field in TOrm for oftSessionUserID:
// if any such property is defined in the table, it will be auto-filled with
// the current TAuthUser.ID value at update, or 0 if no session is running
// - could be defined as value in a TOrm property as such:
// ! property User: TSessionUserID read fUser write fUser;
TSessionUserID = type TID;
/// a monotonic version number, used to track changes on a table
// - add such a published field to any TOrm will allow tracking of
// record modifications - note that only a single field of this type should
// be defined for a given record
// - note that this published field is NOT part of the record "simple fields":
// by default, the version won't be retrieved from the DB, nor will be sent
// from a client - the Engine*() CRUD method will take care of computing the
// monotonic version number, just before storage to the persistence engine
// - such a field will use a separated TOrmTableDeletion table to
// track the deleted items
// - could be defined as value in a TOrm property as such:
// ! property TrackedVersion: TRecordVersion read fVersion write fVersion;
TRecordVersion = type Int64;
/// the available types for any SQL field property, as managed with the
// database driver
// - oftUnknown: unknown or not defined field type
// - oftAnsiText: a WinAnsi encoded TEXT, forcing a NOCASE collation
// (TOrm Delphi property was declared as AnsiString or string before
// Delphi 2009)
// - oftUtf8Text is UTF-8 encoded TEXT, forcing a SYSTEMNOCASE collation,
// i.e. using Utf8IComp() (TOrm property was declared as RawUtf8,
// RawUnicode or WideString - or string in Delphi 2009+) - you may inherit
// from TOrmNoCase to use the NOCASE standard SQLite3 collation
//- oftEnumerate is an INTEGER value corresponding to an index in any
// enumerate Delphi type; storage is an INTEGER value (fast, easy and size
// efficient); at display, this integer index will be converted into the
// left-trimed lowercased chars of the enumerated type text conversion:
// TOpenType(1) = otDone -> 'Done'
/// - oftSet is an INTEGER value corresponding to a bitmapped set of
// enumeration; storage is an INTEGER value (fast, easy and size efficient);
// displayed as an integer by default, sets with an enumeration type with
// up to 64 elements is allowed yet (stored as an Int64)
// - oftInteger is an INTEGER (Int64 precision, as expected by SQLite3) field
// - oftID is an INTEGER field pointing to the ID/RowID of another record of
// a table, defined by the class type of the TOrm inherited property;
// coherency is always ensured: after a delete, all values pointing to
// it is reset to 0
// - oftRecord is an INTEGER field pointing to the ID/RowID of another
// record: TRecordReference=Int64 Delphi property which can be typecasted to
// RecordRef; coherency is always ensured: after a delete, all values
// pointing to it are reset to 0 by the ORM
// - oftBoolean is an INTEGER field for a boolean value: 0 is FALSE,
// anything else TRUE (encoded as JSON 'true' or 'false' constants)
// - oftFloat is a FLOAT (floating point double precision, cf. SQLite3)
// field, defined as double (or single) published properties definition
// - oftDateTime is a ISO 8601 encoded (SQLite3 compatible) TEXT field,
// corresponding to a TDateTime Delphi property: a ISO8601 collation is
// forced for such column, for proper date/time sorting and searching
// - oftDateTimeMS is a ISO 8601 encoded (SQLite3 compatible) TEXT field,
// corresponding to a TDateTimeMS Delphi property, i.e. a TDateTime with
// millisecond resolution, serialized with '.sss' suffix: a ISO8601 collation
// is forced for such column, for proper date/time sorting and searching
// - oftTimeLog is an INTEGER field for coding a date and time (not SQLite3
// compatible), which should be defined as TTimeLog=Int64 Delphi property,
// ready to be typecasted to the TTimeLogBits optimized type for efficient
// timestamp storage, with a second resolution
// - oftCurrency is a FLOAT containing a 4 decimals floating point value,
// compatible with the Currency Delphi type, which minimizes rounding errors
// in monetary calculations which may occur with oftFloat type
// - oftObject is a TEXT containing an ObjectToJson serialization, able to
// handle published properties of any not TPersistent as JSON object,
// TStrings or TRawUtf8List as JSON arrays of strings, TCollection or
// TObjectList as JSON arrays of JSON objects
// - oftVariant is a TEXT containing a variant value encoded as JSON:
// string values are stored between quotes, numerical values directly stored,
// and JSON objects or arrays will be handled as TDocVariant custom types
// - oftNullable is a INTEGER/DOUBLE/TEXT field containing a NULLable value,
// stored as a local variant property, identifying TNullableInteger,
// TNullableBoolean, TNullableFloat, TNullableCurrency,
// TNullableDateTime, TNullableTimeLog and TNullableUtf8Text types
// - oftBlob is a BLOB field (RawBlob Delphi property), and won't be
// retrieved by default (not part of ORM "simple types"), to save bandwidth
// - oftBlobDynArray is a dynamic array, stored as BLOB field: this kind of
// property will be retrieved by default, i.e. is recognized as a "simple
// field", and will use Base64 encoding during JSON transmission, or a true
// JSON array, depending on the database back-end (e.g. MongoDB)
// - oftBlobCustom is a custom property, stored as BLOB field: such
// properties are defined by adding a TOrmPropInfoCustom instance, overriding
// TOrm.InternalRegisterCustomProperties virtual method - they will
// be retrieved by default, i.e. recognized as "simple fields"
// - oftUtf8Custom is a custom property, stored as JSON in a TEXT field,
// defined by overriding TOrm.InternalRegisterCustomProperties
// virtual method, and adding a TOrmPropInfoCustom instance, e.g. via
// RegisterCustomPropertyFromTypeName() or RegisterCustomPropertyFromRtti();
// they will be retrieved by default, i.e. recognized as "simple fields"
// - oftMany is a 'many to many' field (TOrmMany Delphi property);
// nothing is stored in the table row, but in a separate pivot table: so
// there is nothing to retrieve here; in contrast to other TOrm
// published properties, which contains an INTEGER ID, the TOrm.Create
// will instanciate a true TOrmMany instance to handle this pivot table
// via its dedicated ManyAdd/FillMany/ManySelect methods - as a result, such
// properties won't be retrieved by default, i.e. not recognized as "simple
// fields" unless you used the dedicated methods
// - oftModTime is an INTEGER field containing the TModTime value, aka time
// of the record latest update; TModTime (just like TTimeLog or TCreateTime)
// published property can be typecasted to the TTimeLogBits memory structure;
// the value of this field is automatically updated with the current
// date and time each time a record is updated (with external DB, it will
// use the Server time, as retrieved by TSqlDBConnection.ServerTimestamp
// from mormot.db.sql.pas) - see ComputeFieldsBeforeWrite
// virtual method of TOrm; note also that only RESTful PUT/POST access
// will change this field value: manual SQL statements (like
// 'UPDATE Table SET Column=0') won't change its content; note also that
// this is automated on Delphi client side, so only within TOrm ORM use
// (a pure AJAX application should fill such fields explicitly before sending)
// - oftCreateTime is an INTEGER field containing the TCreateTime time
// of the record creation; TCreateTime (just like TTimeLog or TModTime)
// published property can be typecasted to the TTimeLogBits memory structure;
// the value of this field is automatically updated with the current
// date and time when the record is created (with external DB, it will
// use the Server time, as retrieved by TSqlDBConnection.ServerTimestamp
// from mormot.db.sql.pas) - see ComputeFieldsBeforeWrite
// virtual method of TOrm; note also that only RESTful PUT/POST access
// will set this field value: manual SQL statements (like
// 'INSERT INTO Table ...') won't set its content; note also that this is
// automated on Delphi client side, so only within TOrm ORM use (a
// pure AJAX application should fill such fields explicitly before sending)
// - oftTID is an INTEGER field containing a TID pointing to another record;
// since regular TOrm published properties (i.e. oftID kind of field)
// can not be greater than 2,147,483,647 (i.e. a signed 32-bit value) under
// Win32, defining TID published properties will allow to store the ID
// as signed 64-bit, e.g. up to 9,223,372,036,854,775,808; despite to
// oftID kind of record, coherency is NOT ensured: after a deletion, all
// values pointing to are NOT reset to 0 - it is up to your business logic
// to ensure data coherency as expected
// - oftRecordVersion is an INTEGER field containing a TRecordVersion
// monotonic number: adding such a published field to any TOrm will
// allow tracking of record modifications, at storage level; by design,
// such a field won't be part of "simple types", so won't be transmitted
// between the clients and the server, but will be updated at any write
// operation by the low-level Engine*() storage methods - such a field
// will use a TOrmTableDeletion table to track the deleted items
// - oftSessionUserID is an INTEGER field containing the TAuthUser.ID
// of the record modification; the value of this field is automatically
// updated with the current User ID of the active session; note also that
// only RESTful PUT/POST access will change this field value: manual SQL
// statements (like 'UPDATE Table SET Column=0') won't change its content;
// this is automated on Delphi client side, so only within TOrm ORM use
// (a pure AJAX application should fill such fields explicitly before sending)
// - oftUnixTime is an INTEGER field for coding a date and time as second-based
// Unix Time (SQLite3 compatible), which should be defined as TUnixTime=Int64
// TOrm property
// - oftUnixMSTime is an INTEGER field for coding a date and time as
// millisecond-based Unix Time (JavaScript compatible), which should be
// defined as TUnixMSTime=Int64 TOrm property
// - WARNING: do not change the order of items below, otherwise some methods
// (like TOrmProperties.CheckBinaryHeader) may be broken and fail
TOrmFieldType = (
oftUnknown,
oftAnsiText,
oftUtf8Text,
oftEnumerate,
oftSet,
oftInteger,
oftID,
oftRecord,
oftBoolean,
oftFloat,
oftDateTime,
oftTimeLog,
oftCurrency,
oftObject,
oftVariant,
oftNullable,
oftBlob,
oftBlobDynArray,
oftBlobCustom,
oftUtf8Custom,
oftMany,
oftModTime,
oftCreateTime,
oftTID,
oftRecordVersion,
oftSessionUserID,
oftDateTimeMS,
oftUnixTime,
oftUnixMSTime);
/// set of available SQL field property types
TOrmFieldTypes = set of TOrmFieldType;
//// a fixed array of SQL field property types
TOrmFieldTypeArray = array[0..MAX_SQLFIELDS] of TOrmFieldType;
/// contains the parameters used for sorting
// - FieldCount is 0 if was never sorted
// - used to sort data again after a successful data update with
// TOrmTableJson.FillFrom()
TOrmTableSortParams = record
Comp: TUtf8Compare;
FieldCount, FieldIndex: integer;
FieldType: TOrmFieldType;
Asc: boolean;
end;
/// used to define the triggered Event types for TOnOrmEvent
// - some Events can be triggered via TRestServer.OnUpdateEvent when
// a Table is modified, and actions can be authorized via overriding the
// TRest.RecordCanBeUpdated method
// - OnUpdateEvent is called BEFORE deletion, and AFTER insertion or update; it
// should be used only server-side, not to synchronize some clients: the framework
// is designed around a stateless RESTful architecture (like HTTP/1.1), in which
// clients ask the server for refresh (see TRestClientUri.UpdateFromServer)
// - is used also by TOrm.ComputeFieldsBeforeWrite virtual method
TOrmEvent = (
oeAdd,
oeUpdate,
oeDelete,
oeUpdateBlob);
/// used to define the triggered Event types for TOrmHistory
// - TOrmHistory.History will be used for heArchiveBlob
// - TOrmHistory.SentDataJson will be used for other kind of events
TOrmHistoryEvent = (
heAdd,
heUpdate,
heDelete,
heArchiveBlob);
/// used to defined the CRUD associated SQL statement of a command
// - used e.g. by TOrm.GetJsonValues methods and SimpleFieldsBits[] array
// (in this case, ooDelete is never used, since deletion is global for all fields)
// - also used for cache content notification
TOrmOccasion = (
ooSelect,
ooInsert,
ooUpdate,
ooDelete);
/// used to defined a set of CRUD associated SQL statement of a command
TOrmOccasions = set of TOrmOccasion;
/// the possible options for IRestOrmServer.CreateMissingTables and
// TOrm.InitializeTable methods
// - itoNoAutoCreateGroups and itoNoAutoCreateUsers will avoid
// TAuthGroup.InitializeTable to fill the TAuthGroup and TAuthUser
// tables with default records
// - itoNoCreateMissingField will avoid to create the missing fields on a table
// - itoNoIndex4ID won't create the index for the main ID field (do nothing
// on SQLite3, by design - but may be used for tables on external databases)
// - itoNoIndex4UniqueField won't create indexes for "stored AS_UNIQUE" fields
// - itoNoIndex4NestedRecord won't create indexes for TOrm fields
// - itoNoIndex4RecordReference won't create indexes for TRecordReference fields
// - itoNoIndex4TID won't create indexes for TID fields
// - itoNoIndex4RecordVersion won't create indexes for TRecordVersion fields
// - INITIALIZETABLE_NOINDEX constant contain all itoNoIndex* items
TOrmInitializeTableOption = (
itoNoAutoCreateGroups,
itoNoAutoCreateUsers,
itoNoCreateMissingField,
itoNoIndex4ID,
itoNoIndex4UniqueField,
itoNoIndex4NestedRecord,
itoNoIndex4RecordReference,
itoNoIndex4TID,
itoNoIndex4RecordVersion);
/// the options to be specified for IRestOrmServer.CreateMissingTables and
// TOrm.InitializeTable methods
TOrmInitializeTableOptions = set of TOrmInitializeTableOption;
const
/// used as "stored AS_UNIQUE" published property definition in TOrm
AS_UNIQUE = false;
/// options to specify no index createon for IRestOrmServer.CreateMissingTables
// and TOrm.InitializeTable methods
INITIALIZETABLE_NOINDEX: TOrmInitializeTableOptions =
[itoNoIndex4ID.. itoNoIndex4RecordVersion];
EVENT2OCCASION: array[TOrmEvent] of TOrmOccasion = (
ooInsert, // oeAdd
ooUpdate, // oeUpdate
ooDelete, // oeDelete
ooUpdate); // oeUpdateBlob
// backward compatibility types redirections
{$ifndef PUREMORMOT2}
type
TSqlEvent = TOrmEvent;
TSqlHistoryEvent = TOrmHistoryEvent;
TSqlOccasion = TOrmOccasion;
TSqlOccasions = TOrmOccasions;
const
// TOrmFieldType into TSqlFieldType
sftUnknown = oftUnknown;
sftAnsiText = oftAnsiText;
sftUtf8Text = oftUtf8Text;
sftEnumerate = oftEnumerate;
sftSet = oftSet;
sftInteger = oftInteger;
sftID = oftID;
sftRecord = oftRecord;
sftBoolean = oftBoolean;
sftFloat = oftFloat;
sftDateTime = oftDateTime;
sftTimeLog = oftTimeLog;
sftCurrency = oftCurrency;
sftObject = oftObject;
sftVariant = oftVariant;
sftNullable = oftNullable;
sftBlob = oftBlob;
sftBlobDynArray = oftBlobDynArray;
sftBlobCustom = oftBlobCustom;
sftUtf8Custom = oftUtf8Custom;
sftMany = oftMany;
sftModTime = oftModTime;
sftCreateTime = oftCreateTime;
sftTID = oftTID;
sftRecordVersion = oftRecordVersion;
sftSessionUserID = oftSessionUserID;
sftDateTimeMS = oftDateTimeMS;
sftUnixTime = oftUnixTime;
sftUnixMSTime = oftUnixMSTime;
// TOrmEvent/TOrmOccasion into TSqlEvent/TSqlOccasion
seAdd = oeAdd;
seUpdate = oeUpdate;
seDelete = oeDelete;
seUpdateBlob = oeUpdateBlob;
soSelect = ooSelect;
soInsert = ooInsert;
soUpdate = ooUpdate;
soDelete = ooDelete;
{$endif PUREMORMOT2}
const
/// kind of fields not retrieved during normal query, update or adding
// - by definition, BLOB are excluded to save transmission bandwidth
// - by design, TOrmMany properties are stored in an external pivot table
// - by convenience, the TRecordVersion number is for internal use only
NOT_SIMPLE_FIELDS: TOrmFieldTypes =
[oftUnknown, oftBlob, oftMany, oftRecordVersion];
/// kind of fields which can be copied from one TOrm instance to another
COPIABLE_FIELDS: TOrmFieldTypes =
[low(TOrmFieldType)..high(TOrmFieldType)] - [oftUnknown, oftMany];
/// kind of DB fields which will contain TEXT content when converted to JSON
TEXT_DBFIELDS: TSqlDBFieldTypes =
[ftUtf8, ftDate];
/// kind of fields which will contain pure TEXT values
// - independently from the actual storage level
// - i.e. will match RawUtf8, string, UnicodeString, WideString properties
RAWTEXT_FIELDS: TOrmFieldTypes =
[oftAnsiText, oftUtf8Text];
/// kind of fields which will be stored as TEXT values
// - i.e. RAWTEXT_FIELDS and TDateTime/TDateTimeMS
STRING_FIELDS: TOrmFieldTypes =
[oftAnsiText, oftUtf8Text, oftUtf8Custom, oftDateTime, oftDateTimeMS];
/// the SQL field property types with their TNullable* equivalency
// - those types may be stored in a variant published property, e.g.
// ! property Int: TNullableInteger
// ! read fInt write fInt;
// ! property Txt: TNullableUtf8Text
// ! read fTxt write fTxt;
// ! property Txt: TNullableUtf8Text
// ! index 32 read fTxt write fTxt;
NULLABLE_TYPES =
[oftInteger, oftBoolean, oftEnumerate, oftFloat, oftCurrency,
oftDateTime, oftTimeLog, oftUtf8Text];
/// detect ORM equivalency from TypeInfo(TNullable*) RTTI
function NullableTypeToOrmFieldType(aType: PRttiInfo): TOrmFieldType;
/// convert a ORM field type and associated RTTI into a DB data type
function OrmFieldTypeToDBField(aOrmFieldType: TOrmFieldType;
aTypeInfo: PRttiInfo): TSqlDBFieldType;
function ToText(ft: TOrmFieldType): PShortString; overload;
function ToText(e: TOrmEvent): PShortString; overload;
function ToText(he: TOrmHistoryEvent): PShortString; overload;
function ToText(o: TOrmOccasion): PShortString; overload;
/// guess the content type of an UTF-8 encoded field value, as used in TOrmTable.Get()
// - if P if nil or 'null', return oftUnknown
// - otherwise, guess its type from its value characters
// - oftBlob is returned if the field is encoded as SQLite3 BLOB literals
// (X'53514C697465' e.g.) or with '\uFFF0' magic code
// - since P is PUtf8Char, string type is oftUtf8Text only
// - oftFloat is returned for any floating point value, even if it was
// declared as oftCurrency type
// - oftInteger is returned for any INTEGER stored value, even if it was declared
// as oftEnumerate, oftSet, oftID, oftTID, oftRecord, oftRecordVersion,
// oftSessionUserID, oftBoolean, oftModTime/oftCreateTime/oftTimeLog or
// oftUnixTime/oftUnixMSTime type
function Utf8ContentType(P: PUtf8Char): TOrmFieldType;
/// guess the number type of an UTF-8 encoded field value, as used in TOrmTable.Get()
// - if P if nil or 'null', return oftUnknown
// - will return oftInteger or oftFloat if the supplied text is a number
// - will return oftUtf8Text for any non numerical content
function Utf8ContentNumberType(P: PUtf8Char): TOrmFieldType;
{$ifdef HASINLINE}inline;{$endif}
type
/// internal kind of encoding for one TRestBatch.Add/Update/Delete action
// - encPost/encPut/enDelete as {"Table":[...,"POST/PUT/DELETE",{object},...]}
// or [...,"POST/PUT/DELETE@Table",{object},...]
// - encSimple as {"Table":[...,"SIMPLE",[values],... or [...,"SIMPLE@Table"...
// - encPostHex as "hex",[values],... or "hex@Table",[values],...
// - encPostHexID as "ihex",[id,values],... or "ihex@Table",[id,values],...
// - encPutHexID as "uhex",[id,values],... or "uhex@Table",[id,values],...
TRestBatchEncoding = (
encPost,
encSimple,
encPostHex,
encPostHexID,
encPut,
encPutHexID,
encDelete);
/// how TOrmModel.UriMatch() will compare an URI
// - will allow to make a difference about case-sensitivity
TRestModelMatch = (
rmNoMatch,
rmMatchExact,
rmMatchWithCaseChange);
/// the kind of SQlite3 (virtual) table
// - TOrmFts3/4/5 will be associated with vFTS3/vFTS4/vFTS5 values,
// TOrmRTree/TOrmRTreeInteger with rRTree/rRTreeInteger, any native
// SQlite3 table as vSQLite3, and a TOrmVirtualTable*ID as
// rCustomForcedID/rCustomAutoID
// - a plain TOrm class can be defined as rCustomForcedID (e.g. for
// TOrmMany) after registration for an external DB via a call to
// OrmMapExternal() from mormot.orm.sql unit
TOrmVirtualKind = (
ovkSQLite3,
ovkFts3,
ovkFts4,
ovkFts5,
ovkRTree,
ovkRTreeInteger,
ovkCustomForcedID,
ovkCustomAutoID);
/// the kind of fields to be available in a Table resulting of
// a TOrmMany.DestGetJoinedTable() method call
// - Source fields are not available, because they will be always the same for
// a same SourceID, and they should be available from the TOrm which
// hold the TOrmMany instance
// - jkDestID and jkPivotID will retrieve only DestTable.ID and PivotTable.ID
// - jkDestFields will retrieve DestTable.* simple fields, or the fields
// specified by FieldsCsv (the Dest table name will be added: e.g.
// for FieldsCsv='One,Two', will retrieve DestTable.One, DestTable.Two)
// - jkPivotFields will retrieve PivotTable.* simple fields, or the fields
// specified by FieldsCsv (the Pivot table name will be added: e.g.
// for FieldsCsv='One,Two', will retrieve PivotTable.One, PivotTable.Two)
// - jkPivotAndDestAllFields for PivotTable.* and DestTable.* simple fields,
// or will retrieve the specified FieldsCsv fields (with
// the table name associated: e.g. 'PivotTable.One, DestTable.Two')
TOrmManyJoinKind = (
jkDestID,
jkPivotID,
jkDestFields,
jkPivotFields,
jkPivotAndDestFields);
/// pre-computed SQL statements for ORM operations for a given
// TOrmModelProperties instance
// - those statements will work for internal tables, not for external DB with
// mapped table or fields names, which needs proper adaptation
TOrmModelPropertiesSql = record
/// the simple field names in a SQL SELECT compatible format: 'COL1,COL2' e.g.
// - format is
// ! Sql.TableSimpleFields[withID: boolean; withTableName: boolean]
// - returns '*' if no field is of RawBlob/TOrmMany kind
// - returns 'COL1,COL2' with all COL* set to simple field names if withID is false
// - returns 'ID,COL1,COL2' with all COL* set to simple field names if withID is true
// - returns 'Table.ID,Table.COL1,Table.COL2' if withTableName and withID are true
TableSimpleFields: array[boolean, boolean] of RawUtf8;
/// the SQL statement for reading all simple fields and RowID of all rows
// - to be checked if we may safely call EngineList()
SelectAllWithRowID: RawUtf8;
/// the SQL statement for reading all simple fields with ID of all rows
// - to be checked if we may safely call EngineList()
SelectAllWithID: RawUtf8;
/// the SQL statement for reading all simple fields of a given ID
// - as used e.g. by TRestOrmServerDB.MainEngineRetrieve
SelectOneWithID: RawUtf8;
/// the JOINed SQL statement for reading all fields with ID, including
// nested TOrm pre-allocated instances
// - is '' if there is no nested TOrm
SelectAllJoined: RawUtf8;
/// the updated simple fields exposed as 'COL1=?,COL2=?'
// - excluding ID (but including TCreateTime fields - as used in
// TOrmVirtualTableExternal.Update method)
// - to be used e.g. for UPDATE statements
UpdateSetSimple: RawUtf8;
/// all updated fields exposed as 'COL1=?,COL2=?'
// - excluding ID (but including TCreateTime fields - as used in
// TOrmVirtualTableExternal.Update method)
// - to be used e.g. for UPDATE statements
UpdateSetAll: RawUtf8;
/// all fields, excluding the ID field, exposed as 'COL1,COL2'
// - to be used e.g. in TOrmVirtualTableExternal.Insert()
InsertSet: RawUtf8;
end;
/// used by TOrmMapping.Options for custom field mapping
// of a TOrm on an external database process
// - rpmAutoMapKeywordFields is set if MapAutoKeywordFields has been defined,
// i.e. if field names which may conflict with a keyword should be
// automatically mapped to a harmless symbol name
// - rpmNoCreateMissingTable will bypass the existing table check, e.g.
// to circumvent some specific DB provider or case sensitivity issue on tables
// - rpmNoCreateMissingField will bypass the existing field check, e.g.
// to circumvent some specific DB provider or case sensitivity issue on fields
// - by default, check of missing field name will be case insensitive, unless
// the rpmMissingFieldNameCaseSensitive option is set
// - rpmQuoteFieldName will quote the field names - to be used e.g. with
// FireBird in its Dialect 3
// - rpmClearPoolOnConnectionIssue will enable detecting connection loss
TOrmMappingOptions = set of (
rpmAutoMapKeywordFields,
rpmNoCreateMissingTable,
rpmNoCreateMissingField,
rpmMissingFieldNameCaseSensitive,
rpmQuoteFieldName,
rpmClearPoolOnConnectionIssue);
const
/// if the TOrmVirtual table kind is a FTS virtual table
IS_FTS = [ovkFts3, ovkFts4, ovkFts5];
/// if the TOrmVirtual table kind is not an embedded type
// - can be set for a TOrm after a OrmMapExternal call
IS_CUSTOM_VIRTUAL = [ovkCustomForcedID, ovkCustomAutoID];
/// if the TOrmVirtual table kind expects the ID to be set on INSERT
INSERT_WITH_ID =
[ovkFts3, ovkFts4, ovkFts5, ovkRTree, ovkRTreeInteger, ovkCustomForcedID];
/// if a TOrmVirtualTablePreparedConstraint.Column is to be ignored
VIRTUAL_TABLE_IGNORE_COLUMN = -2;
/// if a TOrmVirtualTablePreparedConstraint.Column points to the RowID
VIRTUAL_TABLE_ROWID_COLUMN = -1;
/// kind of encodings generated by TRestBatch.Add
BATCH_INSERT = [encPost, encSimple, encPostHex, encPostHexID];
/// kind of encodings generated by TRestBatch which result in a JSON array
BATCH_DIRECT = [encSimple, encPostHex, encPostHexID, encPutHexID];
/// kind of encodings generated by TRestBatch.Add which result in a JSON array
BATCH_DIRECT_ADD = [encSimple, encPostHex, encPostHexID];
/// client-side only options for TRestBatch, not transmitted to server
BATCH_OPTIONS_CLIENTONLY = [boExtendedJson, boPostNoSimpleFields,
boNoModelEncoding, boOnlyObjects];
var
/// late-binding return of TOrmVirtualTableClass.ModuleName
// - link mormot.orm.storage.pas unit for properly set this value
GetVirtualTableModuleName: function(VirtualTableClass: TClass): RawUtf8;
function ToText(enc: TRestBatchEncoding): PShortString; overload;
function ToText(vk: TOrmVirtualKind): PShortString; overload;
/// encode as a SQL-ready INSERT or UPDATE statement with ? as values
// - after a successful call to Decode()
// - FieldValues[] content will be ignored
// - Occasion can be only ooInsert or ooUpdate
// - for ooUpdate, will create UPDATE ... SET ... where UpdateIDFieldName=?
// - you can specify some options, e.g. boInsertOrIgnore for ooInsert
// - MultiInsertRowCount would generate INSERT .. VALUES (..),(..),(..),..
function EncodeAsSqlPrepared(const Decoder: TJsonObjectDecoder;
const TableName: RawUtf8; Occasion: TOrmOccasion;
const UpdateIDFieldName: RawUtf8; BatchOptions: TRestBatchOptions;
DB: TSqlDBDefinition; MultiInsertRowCount: integer = 1): RawUtf8;
/// low-level function used to convert a JSON Value into a variant,
// according to the property type
// - for oftObject, oftVariant, oftBlobDynArray and oftUtf8Custom, the
// JSON buffer may be an array or an object, so createValueTempCopy can
// create a temporary copy before parsing it in-place, to preserve the buffer
// - oftUnknown and oftMany will set a varEmpty (Unassigned) value
// - typeInfo may be used for oftBlobDynArray conversion to a TDocVariant array
procedure ValueVarToVariant(Value: PUtf8Char; ValueLen: integer;
fieldType: TOrmFieldType; var result: TVarData; createValueTempCopy: boolean;
typeInfo: PRttiInfo; options: TDocVariantOptions = JSON_FAST);
/// check if P^ is a known SQL function name (max/min/avg/sum/jsonget/jsonhas)
function IsSqlFunction(P: PUtf8Char): boolean;
{ ****************** ORM Ready UTF-8 Comparison Functions }
/// special comparison function for sorting ftRecord (TRecordReference/RecordRef)
// UTF-8 encoded values in the SQLite3 database or JSON content
function Utf8CompareRecord(P1, P2: PUtf8Char): PtrInt;
/// special comparison function for sorting oftBoolean
// UTF-8 encoded values in the SQLite3 database or JSON content
function Utf8CompareBoolean(P1, P2: PUtf8Char): PtrInt;
/// special comparison function for sorting oftEnumerate, oftSet or oftID
// UTF-8 encoded values in the SQLite3 database or JSON content
function Utf8CompareUInt32(P1, P2: PUtf8Char): PtrInt;
/// special comparison function for sorting oftInteger, oftTID, oftRecordVersion
// oftTimeLog/oftModTime/oftCreateTime or oftUnixTime/oftUnixMSTime UTF-8 encoded
// values in the SQLite3 database or JSON content
function Utf8CompareInt64(P1, P2: PUtf8Char): PtrInt;
/// special comparison function for sorting oftCurrency
// UTF-8 encoded values in the SQLite3 database or JSON content
function Utf8CompareCurr64(P1, P2: PUtf8Char): PtrInt;
/// special comparison function for sorting oftFloat
// UTF-8 encoded values in the SQLite3 database or JSON content
function Utf8CompareDouble(P1, P2: PUtf8Char): PtrInt;
/// special comparison function for sorting oftDateTime or oftDateTimeMS
// UTF-8 encoded values in the SQLite3 database or JSON content
function Utf8CompareIso8601(P1, P2: PUtf8Char): PtrInt;
var
/// simple wrapper to UTF-8 compare function for the SQLite3 field datatypes
// - used internally for field sorting (see TOrmTable.SortFields() method)
// and for default User Interface Query (see TRest.QueryIsTrue() method)
// - some functions do not match exactly the TUtf8Compare signature, so will
// be set in the initialization section of this unit
OrmFieldTypeComp: array[TOrmFieldType] of TUtf8Compare = (
nil, // unknown
nil, // AnsiText = AnsiIComp (in initialization below)
Utf8IComp, // Utf8Text, 8-bit case insensitive compared
Utf8CompareUInt32, // Enumerate
Utf8CompareUInt32, // Set
Utf8CompareInt64, // integer
Utf8CompareInt64, // ID
Utf8CompareRecord, // Record
Utf8CompareBoolean, // boolean
Utf8CompareDouble, // Float
Utf8CompareIso8601, // TDateTime
Utf8CompareInt64, // TTimeLog
Utf8CompareCurr64, // Currency
nil, // Object (TEXT serialization) = StrComp
nil, // Variant (TEXT serialization) = StrComp
nil, // TNullable* = StrComp
nil, // Blob
nil, // BlobDynArray
nil, // BlobCustom
nil, // Utf8Custom
nil, // Many
Utf8CompareInt64, // TModTime
Utf8CompareInt64, // TCreateTime
Utf8CompareInt64, // TID
Utf8CompareInt64, // TRecordVersion
Utf8CompareInt64, // TSessionUserID
Utf8CompareIso8601, // TDateTimeMS
Utf8CompareInt64, // TUnixTime
Utf8CompareInt64); // TUnixMSTime
{ ************ TOrmWriter Class for TOrm Serialization }
type
/// several options to customize how TOrm will be serialized
// - e.g. if properties storing JSON should be serialized as an object, and not
// escaped as a string (which is the default, matching ORM column storage)
// - if an additional "ID_str":"12345" field should be added to the standard
// "ID":12345 field, which may exceed 53-bit integer precision of JavaScript
// - to generate JS-friendly "id" (and "idStr") instead of "ID" or "RowID"
// - to generate JS-friendly "propName": from a PropName pascal property
TOrmWriterOption = (
owoAsJsonNotAsString,
owoID_str,
owoLowCaseID,
owoLowCaseFirstPropChar);
/// options to customize how TOrm will be written by TOrmWriter
TOrmWriterOptions = set of TOrmWriterOption;
/// simple writer to a Stream, specialized for writing TOrm as JSON
// - in respect to the standard TResultsWriter as defined in mormot.db.core,
// this class has some options dedicated to our TOrm serialization
TOrmWriter = class(TResultsWriter)
protected
fOrmOptions: TOrmWriterOptions;
procedure SetOrmOptions(Value: TOrmWriterOptions);
public
/// customize TOrm.GetJsonValues serialization process
// - owoAsJsonNotAsString will force TOrm.GetJsonValues to serialize
// nested property instances as a JSON object/array, not a JSON string:
// i.e. root/table/id REST will be ready-to-be-consumed from AJAX clients
// (e.g. TOrmPropInfoRttiObject.GetJsonValues as a JSON object, and
// TOrmPropInfoRttiDynArray.GetJsonValues as a JSON array)
// - owoID_str will add an "ID_str":"12345" property to the default
// "ID":12345 field to circumvent JavaScript's limitation of 53-bit for
// integer numbers, which is easily reached with our 64-bit TID values, e.g.
// if TSynUniqueIdentifier are used to generate the IDs: AJAX clients should
// better use this "ID_str" string value to identify each record, and ignore
// the "id" fields
property OrmOptions: TOrmWriterOptions
read fOrmOptions write SetOrmOptions;
end;
// for backward compatibility - use Rtti/TRttiJson.Register* methods instead
{$ifndef PUREMORMOT2}
type
TJsonSerializer = class(TOrmWriter)
public
class procedure RegisterClassForJson(aItemClass: TClass); overload;
class procedure RegisterClassForJson(const aItemClass: array of TClass); overload;
class procedure RegisterCollectionForJson(aCollection: TCollectionClass;
aItem: TCollectionItemClass);
class procedure RegisterObjArrayForJson(aDynArray: PRttiInfo; aItem: TClass); overload;
class procedure RegisterObjArrayForJson(const aDynArrayClassPairs: array of const); overload;
class function RegisterCustomJSONSerializerFromText(aTypeInfo: pointer;
const aRTTIDefinition: RawUtf8): TRttiCustom; overload;
class procedure RegisterCustomJSONSerializerFromText(
const aTypeInfoTextDefinitionPairs: array of const); overload;
class procedure RegisterCustomJSONSerializerFromTextSimpleType(
aTypeInfo: PRttiInfo); overload;
class procedure RegisterCustomJSONSerializerFromTextBinaryType(
aTypeInfo: PRttiInfo; aDataSize: integer); overload;
class procedure RegisterCustomJSONSerializerFromTextBinaryType(
const InfoBinarySize: array of const); overload;
// warning: Reader/Writer have changed signature in mORMot 2
class function RegisterCustomJsonSerializer(Info: PRttiInfo;
const Reader: TOnRttiJsonRead; const Writer: TOnRttiJsonWrite): TRttiJson;
end;
const
jwoAsJsonNotAsString = owoAsJsonNotAsString;
jwoID_str = owoID_str;
{$endif PUREMORMOT2}
{ ************ TOrmPropInfo ORM / RTTI Classes }
type
/// ORM attributes for a TOrmPropInfo definition
TOrmPropInfoAttribute = (
aIsUnique,
aAuxiliaryRTreeField,
aBinaryCollation,
aUnicodeNoCaseCollation);
/// set of ORM attributes for a TOrmPropInfo definition
TOrmPropInfoAttributes = set of TOrmPropInfoAttribute;
/// allow a quick detection of some particular TOrmPropInfo classes
// - picInt32 match TOrmPropInfoRttiInt32
// - picInt64 match TOrmPropInfoRttiInt64
TOrmPropInfoClassType = (
picOther,
picInt32,
picInt64);
/// abstract parent class to store information about a published property
// - property information could be retrieved from RTTI (TOrmPropInfoRtti*),
// or be defined by code (TOrmPropInfoCustom derivated classes) when RTTI
// is not available
TOrmPropInfo = class
protected
fName: RawUtf8;
fNameUnflattened: RawUtf8;
fOrmFieldType: TOrmFieldType;
fOrmFieldTypeStored: TOrmFieldType;
fSqlDBFieldType: TSqlDBFieldType;
fAttributes: TOrmPropInfoAttributes;
fPropInfoClass: TOrmPropInfoClassType;
fFieldWidth: integer;
fPropertyIndex: integer;
fJsonName: RawUtf8;
function GetNameDisplay: string; virtual;
/// those two protected methods allow custom storage of binary content as text
// - default implementation is to use hexa (ToSql=true) or Base64 encodings
procedure BinaryToText(var Value: RawUtf8; ToSql: boolean;
wasSqlString: PBoolean); virtual;
procedure TextToBinary(Value: PUtf8Char; var result: RawByteString); virtual;
function GetOrmFieldTypeName: PShortString;
function GetSqlFieldRttiTypeName: RawUtf8; virtual;
// overriden method shall use direct copy of the low-level binary content,
// to be faster than a DestInfo.SetValue(Dest,GetValue(Source)) call
procedure CopySameClassProp(Source: TObject; DestInfo: TOrmPropInfo;
Dest: TObject); virtual;
public
/// initialize the internal fields
// - not called directly, but from TOrmPropInfoRtti.CreateFrom() factory
constructor Create(const aName: RawUtf8; aOrmFieldType: TOrmFieldType;
aAttributes: TOrmPropInfoAttributes; aFieldWidth, aPropertyIndex: integer);
reintroduce; virtual;
/// the property definition Name
property Name: RawUtf8
read fName;
/// the property definition Name, for TOrm.RttiJsonWrite serialization
property JsonName: RawUtf8
read fJsonName;
/// the property definition Name, after un-camelcase and translation
property NameDisplay: string
read GetNameDisplay;
/// the property definition Name, with full path name if has been flattened
// - if the property has been flattened (for a TOrmPropInfoRtti), the real
// full nested class will be returned, e.g. 'Address.Country.Iso' for
// the 'Address_Country' flattened property name
property NameUnflattened: RawUtf8
read fNameUnflattened;
/// the property index in the RTTI
property PropertyIndex: integer
read fPropertyIndex;
/// the corresponding column type, as managed by the ORM layer
property OrmFieldType: TOrmFieldType
read fOrmFieldType;
/// the corresponding column type, as stored by the ORM layer
// - match OrmFieldType, unless for OrmFieldType=oftNullable, in which this
// field will contain the simple type eventually stored in the database
property OrmFieldTypeStored: TOrmFieldType
read fOrmFieldTypeStored;
/// the corresponding column type name, as managed by the ORM layer and
// retrieved by the RTTI
// - returns e.g. 'oftTimeLog'
property OrmFieldTypeName: PShortString
read GetOrmFieldTypeName;
/// the type name, as defined in the RTTI
// - returns e.g. 'RawUtf8'
// - will return the TOrmPropInfo class name if it is not a TOrmPropInfoRtti
property SqlFieldRttiTypeName: RawUtf8
read GetSqlFieldRttiTypeName;
/// the corresponding column type, as managed for abstract database access
// - TNullable* fields will report here the corresponding simple DB type,
// e.g. ftInt64 for TNullableInteger (following OrmFieldTypeStored value)
property SqlDBFieldType: TSqlDBFieldType
read fSqlDBFieldType;
/// the corresponding column type name, as managed for abstract database access
function SqlDBFieldTypeName: PShortString;
/// allow quick detection of some TOrmPropInfo classes
property PropInfoClass: TOrmPropInfoClassType
read fPropInfoClass;
/// the ORM attributes of this property
// - contains aIsUnique e.g for TOrm published properties marked as
// ! property MyProperty: RawUtf8 stored AS_UNIQUE;
// (i.e. "stored false")
property Attributes: TOrmPropInfoAttributes
read fAttributes write fAttributes;
/// the optional width of this field, in external databases
// - is set e.g. by index attribute of TOrm published properties as
// ! property MyProperty: RawUtf8