-
-
Notifications
You must be signed in to change notification settings - Fork 326
/
SynDB.pas
9057 lines (8509 loc) · 374 KB
/
SynDB.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
/// abstract database direct access classes
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynDB;
{
This file is part of Synopse framework.
Synopse framework. Copyright (c) Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse mORMot framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (c)
the Initial Developer. All Rights Reserved.
Contributor(s):
- Adam Siwon (asiwon)
- Alexander (volax)
- Alfred Glaenzer (alf)
- delphinium
- dominikcz
- Esteban Martin (EMartin)
- Joe (at jokusoftware)
- Maciej Izak (hnb)
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
}
{$I Synopse.inc} // define HASINLINE CPU32 CPU64 OWNNORMTOUPPER
interface
/// if defined, a TQuery class will be defined to emulate the BDE TQuery class
{$define EMULATES_TQUERY}
/// if defined, a set of classes will be defined to implement remote access
{$define WITH_PROXY}
{$ifdef LVCL}
{$undef EMULATES_TQUERY}
{$endif}
uses
{$ifdef MSWINDOWS}
Windows,
{$else}
{$ifdef KYLIX3}
LibC,
Types,
SynKylix,
{$endif}
{$ifdef FPC}
SynFPCLinux,
{$endif}
{$endif}
{$ifdef FPC}
dynlibs,
{$endif}
{$ifdef ISDELPHIXE2}System.SysUtils,{$else}SysUtils,{$endif}
Classes,
{$ifndef LVCL}
Contnrs,
{$endif}
{$ifndef DELPHI5OROLDER}
Variants,
{$endif}
SynCommons,
SynTable, // for TSynTableStatement
SynLog;
{ -------------- TSQLDB* generic classes and types }
type
// NOTE: TSQLDBFieldType is defined in SynCommons.pas (used by TSQLVar)
/// an array of RawUTF8, for each existing column type
// - used e.g. by SQLCreate method
// - ftUnknown maps int32 field (e.g. boolean), ftNull maps RawUTF8 index # field,
// ftUTF8 maps RawUTF8 blob field, other types map their default kind
// - for UTF-8 text, ftUTF8 will define the BLOB field, whereas ftNull will
// expect to be formated with an expected field length in ColumnAttr
// - the RowID definition will expect the ORM to create an unique identifier,
// and will use the ftInt64 type definition for this
// and send it with the INSERT statement (some databases, like Oracle, do not
// support standard's IDENTITY attribute) - see http://troels.arvin.dk/db/rdbms
TSQLDBFieldTypeDefinition = array[TSQLDBFieldType] of RawUTF8;
/// the diverse type of bound parameters during a statement execution
// - will be paramIn by default, which is the case 90% of time
// - could be set to paramOut or paramInOut if must be refereshed after
// execution (for calling a stored procedure expecting such parameters)
TSQLDBParamInOutType =
(paramIn, paramOut, paramInOut);
/// used to define a field/column layout in a table schema
// - for TSQLDBConnectionProperties.SQLCreate to describe the new table
// - for TSQLDBConnectionProperties.GetFields to retrieve the table layout
TSQLDBColumnDefine = packed record
/// the Column name
ColumnName: RawUTF8;
/// the Column type, as retrieved from the database provider
// - returned as plain text by GetFields method, to be used e.g. by
// TSQLDBConnectionProperties.GetFieldDefinitions method
// - SQLCreate will check for this value to override the default type
ColumnTypeNative: RawUTF8;
/// the Column default width (in chars or bytes) of ftUTF8 or ftBlob
// - can be set to value <0 for CLOB or BLOB column type, i.e. for
// a value without any maximal length
ColumnLength: PtrInt;
/// the Column data precision
// - used e.g. for numerical values
ColumnPrecision: PtrInt;
/// the Column data scale
// - used e.g. for numerical values
// - may be -1 if the metadata SQL statement returned NULL
ColumnScale: PtrInt;
/// the Column type, as recognized by our SynDB classes
// - should not be ftUnknown nor ftNull
ColumnType: TSQLDBFieldType;
/// specify if column is indexed
ColumnIndexed: boolean;
end;
/// used to define the column layout of a table schema
// - e.g. for TSQLDBConnectionProperties.GetFields
TSQLDBColumnDefineDynArray = array of TSQLDBColumnDefine;
/// used to describe extended Index definition of a table schema
TSQLDBIndexDefine = packed record
/// name of the index
IndexName: RawUTF8;
/// description of the index type
// - for MS SQL possible values are:
// $ HEAP | CLUSTERED | NONCLUSTERED | XML |SPATIAL
// - for Oracle:
// $ NORMAL | BITMAP | FUNCTION-BASED NORMAL | FUNCTION-BASED BITMAP | DOMAIN
// see @http://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_1069.htm
TypeDesc: RawUTF8;
/// Expression for the subset of rows included in the filtered index
// - only set for MS SQL - not retrieved for other DB types yet
Filter: RawUTF8;
/// comma separated list of indexed column names, in order of their definition
KeyColumns: RawUTF8;
/// comma separaded list of a nonkey column added to the index by using the CREATE INDEX INCLUDE clause
// - only set for MS SQL - not retrieved for other DB types yet
IncludedColumns: RawUTF8;
/// if Index is unique
IsUnique: boolean;
/// if Index is part of a PRIMARY KEY constraint
// - only set for MS SQL - not retrieved for other DB types yet
IsPrimaryKey: boolean;
/// if Index is part of a UNIQUE constraint
// - only set for MS SQL - not retrieved for other DB types yet
IsUniqueConstraint: boolean;
end;
/// used to describe extended Index definition of a table schema
// - e.g. for TSQLDBConnectionProperties.GetIndexes
TSQLDBIndexDefineDynArray = array of TSQLDBIndexDefine;
/// used to define a parameter/column layout in a stored procedure schema
// - for TSQLDBConnectionProperties.GetProcedureParameters to retrieve the stored procedure parameters
// - can be extended according to https://msdn.microsoft.com/en-us/library/ms711701(v=vs.85).aspx
TSQLDBProcColumnDefine = packed record
/// the Column name
ColumnName: RawUTF8;
/// the Column type, as retrieved from the database provider
// - used e.g. by TSQLDBConnectionProperties.GetProcedureParameters method
ColumnTypeNative: RawUTF8;
/// the Column default width (in chars or bytes) of ftUTF8 or ftBlob
// - can be set to value <0 for CLOB or BLOB column type, i.e. for
// a value without any maximal length
ColumnLength: PtrInt;
/// the Column data precision
// - used e.g. for numerical values
ColumnPrecision: PtrInt;
/// the Column data scale
// - used e.g. for numerical values
// - may be -1 if the metadata SQL statement returned NULL
ColumnScale: PtrInt;
/// the Column type, as recognized by our SynDB classes
// - should not be ftUnknown nor ftNull
ColumnType: TSQLDBFieldType;
/// defines the procedure column as a parameter or a result set column
ColumnParamType: TSQLDBParamInOutType;
end;
/// used to define the parameter/column layout of a stored procedure schema
// - e.g. for TSQLDBConnectionProperties.GetProcedureParameters
TSQLDBProcColumnDefineDynArray = array of TSQLDBProcColumnDefine;
/// possible column retrieval patterns
// - used by TSQLDBColumnProperty.ColumnValueState
TSQLDBStatementGetCol = (colNone, colNull, colWrongType, colDataFilled, colDataTruncated);
/// used to define a field/column layout
// - for TSQLDBConnectionProperties.SQLCreate to describe the table
// - for T*Statement.Execute/Column*() methods to map the IRowSet content
TSQLDBColumnProperty = packed record
/// the Column name
ColumnName: RawUTF8;
/// a general purpose integer value
// - for SQLCreate: default width (in WideChars or Bytes) of ftUTF8 or ftBlob;
// if set to 0, a CLOB or BLOB column type will be created - note that
// UTF-8 encoding is expected when calculating the maximum column byte size
// for the CREATE TABLE statement (e.g. for Oracle 1333=4000/3 is used)
// - for TOleDBStatement: the offset of this column in the IRowSet data,
// starting with a DBSTATUSENUM, the data, then its length (for inlined
// sftUTF8 and sftBlob only)
// - for TSQLDBOracleStatement: contains an offset to this column values
// inside fRowBuffer[] internal buffer
// - for TSQLDBDatasetStatement: maps TField pointer value
// - for TSQLDBPostgresStatement: contains the column type OID
ColumnAttr: PtrUInt;
/// the Column type, used for storage
// - for SQLCreate: should not be ftUnknown nor ftNull
// - for TOleDBStatement: should not be ftUnknown
// - for SynDBOracle: never ftUnknown, may be ftNull (for SQLT_RSET)
ColumnType: TSQLDBFieldType;
/// set if the Column must exists (i.e. should not be null)
ColumnNonNullable: boolean;
/// set if the Column shall have unique value (add the corresponding constraint)
ColumnUnique: boolean;
/// set if the Column data is inlined within the main rows buffer
// - for TOleDBStatement: set if column was NOT defined as DBTYPE_BYREF
// which is the most common case, when column data < 4 KB
// - for TSQLDBOracleStatement: FALSE if column is an array of
// POCILobLocator (SQLT_CLOB/SQLT_BLOB) or POCIStmt (SQLT_RSET)
// - for TSQLDBODBCStatement: FALSE if bigger than 255 WideChar (ftUTF8) or
// 255 bytes (ftBlob)
ColumnValueInlined: boolean;
/// expected column data size
// - for TSQLDBOracleStatement/TOleDBStatement/TODBCStatement: used to store
// one column size (in bytes)
ColumnValueDBSize: cardinal;
/// optional character set encoding for ftUTF8 columns
// - for SQLT_STR/SQLT_CLOB (SynDBOracle): equals to the OCI char set
ColumnValueDBCharSet: integer;
/// internal DB column data type
// - for TSQLDBOracleStatement: used to store the DefineByPos() TypeCode,
// can be SQLT_STR/SQLT_CLOB, SQLT_FLT, SQLT_INT, SQLT_DAT, SQLT_BLOB,
// SQLT_BIN and SQLT_RSET
// - for TSQLDBODBCStatement: used to store the DataType as returned
// by ODBC.DescribeColW() - use private ODBC_TYPE_TO[ColumnType] to
// retrieve the marshalled type used during column retrieval
// - for TSQLDBFirebirdStatement: used to store XSQLVAR.sqltype
// - for TSQLDBDatasetStatement: indicates the TField class type, i.e.
// 0=TField, 1=TLargeIntField, 2=TWideStringField
ColumnValueDBType: smallint;
/// driver-specific encoding information
// - for SynDBOracle: used to store the ftUTF8 column encoding, i.e. for
// SQLT_CLOB, equals either to SQLCS_NCHAR or SQLCS_IMPLICIT
ColumnValueDBForm: byte;
/// may contain the current status of the column value
// - for SynDBODBC: state of the latest SQLGetData() call
ColumnDataState: TSQLDBStatementGetCol;
/// may contain the current column size for not FIXEDLENGTH_SQLDBFIELDTYPE
// - for SynDBODBC: size (in bytes) in corresponding fColData[]
// - TSQLDBProxyStatement: the actual maximum column size
ColumnDataSize: integer;
end;
PSQLDBColumnProperty = ^TSQLDBColumnProperty;
/// used to define a table/field column layout
TSQLDBColumnPropertyDynArray = array of TSQLDBColumnProperty;
/// used to define how a column to be created
TSQLDBColumnCreate = record
/// the data type
// - here, ftUnknown is used for Int32 values, ftInt64 for Int64 values,
// as expected by TSQLDBFieldTypeDefinition
DBType: TSQLDBFieldType;
/// the column name
Name: RawUTF8;
/// the width, e.g. for VARCHAR() types
Width: cardinal;
/// if the column should be unique
Unique: boolean;
/// if the column should be non null
NonNullable: boolean;
/// if the column is the ID primary key
PrimaryKey: boolean;
end;
/// used to define how a table is to be created
TSQLDBColumnCreateDynArray = array of TSQLDBColumnCreate;
/// identify a CRUD mode of a statement
// - in addition to CRUD states, cPostgreBulkArray would identify if the ORM
// should generate unnested/any bound array statements - currently only
// supported by SynDBPostgres for bulk insert/update/delete
TSQLDBStatementCRUD = (
cCreate, cRead, cUpdate, cDelete, cPostgreBulkArray);
/// identify the CRUD modes of a statement
// - used e.g. for batch send abilities of a DB engine
TSQLDBStatementCRUDs = set of TSQLDBStatementCRUD;
/// the known database definitions
// - will be used e.g. for TSQLDBConnectionProperties.SQLFieldCreate(), or
// for OleDB/ODBC/ZDBC tuning according to the connected database engine
TSQLDBDefinition = (dUnknown, dDefault, dOracle, dMSSQL, dJet, dMySQL,
dSQLite, dFirebird, dNexusDB, dPostgreSQL, dDB2, dInformix);
/// set of the available database definitions
TSQLDBDefinitions = set of TSQLDBDefinition;
{$M+}
TSQLDBStatement = class;
{$M-}
{$ifndef LVCL}
{$ifndef DELPHI5OROLDER}
/// a custom variant type used to have direct access to a result row content
// - use ISQLDBRows.RowData method to retrieve such a Variant
TSQLDBRowVariantType = class(TSynInvokeableVariantType)
protected
function IntGet(var Dest: TVarData; const Instance: TVarData; Name: PAnsiChar; NameLen: PtrInt): boolean; override;
end;
{$endif}
{$endif}
/// generic interface to access a SQL query result rows
// - not all TSQLDBStatement methods are available, but only those to retrieve
// data from a statement result: the purpose of this interface is to make
// easy access to result rows, not provide all available features - therefore
// you only have access to the Step() and Column*() methods
ISQLDBRows = interface
['{11291095-9C15-4984-9118-974F1926DB9F}']
/// after a prepared statement has been prepared returning a ISQLDBRows
// interface, this method must be called one or more times to evaluate it
// - you shall call this method before calling any Column*() methods
// - return TRUE on success, with data ready to be retrieved by Column*()
// - return FALSE if no more row is available (e.g. if the SQL statement
// is not a SELECT but an UPDATE or INSERT command)
// - access the first or next row of data from the SQL Statement result:
// if SeekFirst is TRUE, will put the cursor on the first row of results,
// otherwise, it will fetch one row of data, to be called within a loop
// - should raise an Exception on any error
// - typical use may be:
// ! var Customer: Variant;
// ! begin
// ! with Props.Execute( 'select * from Sales.Customer where AccountNumber like ?',
// ! ['AW000001%'],@Customer) do begin
// ! while Step do // loop through all matching data rows
// ! assert(Copy(Customer.AccountNumber,1,8)='AW000001');
// ! ReleaseRows;
// ! end;
// ! end;
function Step(SeekFirst: boolean=false): boolean;
/// release cursor memory and resources once Step loop is finished
// - this method call is optional, but is better be used if the ISQLDBRows
// statement from taken from cache, and returned a lot of content which
// may still be in client (and server) memory
// - will also free all temporary memory used for optional logging
procedure ReleaseRows;
/// the column/field count of the current Row
function ColumnCount: integer;
/// the Column name of the current Row
// - Columns numeration (i.e. Col value) starts with 0
// - it's up to the implementation to ensure than all column names are unique
function ColumnName(Col: integer): RawUTF8;
/// returns the Column index of a given Column name
// - Columns numeration (i.e. Col value) starts with 0
// - returns -1 if the Column name is not found (via case insensitive search)
function ColumnIndex(const aColumnName: RawUTF8): integer;
/// the Column type of the current Row
// - FieldSize can be set to store the size in chars of a ftUTF8 column
// (0 means BLOB kind of TEXT column)
function ColumnType(Col: integer; FieldSize: PInteger=nil): TSQLDBFieldType;
/// returns TRUE if the column contains NULL
function ColumnNull(Col: integer): boolean;
/// return a Column integer value of the current Row, first Col is 0
function ColumnInt(Col: integer): Int64; overload;
/// return a Column floating point value of the current Row, first Col is 0
function ColumnDouble(Col: integer): double; overload;
/// return a Column floating point value of the current Row, first Col is 0
function ColumnDateTime(Col: integer): TDateTime; overload;
/// return a column date and time value of the current Row, first Col is 0
function ColumnTimestamp(Col: integer): TTimeLog; overload;
/// return a Column currency value of the current Row, first Col is 0
function ColumnCurrency(Col: integer): currency; overload;
/// return a Column UTF-8 encoded text value of the current Row, first Col is 0
function ColumnUTF8(Col: integer): RawUTF8; overload;
/// return a Column text value as generic VCL string of the current Row, first Col is 0
function ColumnString(Col: integer): string; overload;
/// return a Column as a blob value of the current Row, first Col is 0
function ColumnBlob(Col: integer): RawByteString; overload;
/// return a Column as a blob value of the current Row, first Col is 0
function ColumnBlobBytes(Col: integer): TBytes; overload;
/// read a blob Column into the Stream parameter
procedure ColumnBlobToStream(Col: integer; Stream: TStream); overload;
/// write a blob Column into the Stream parameter
// - expected to be used with 'SELECT .. FOR UPDATE' locking statements
procedure ColumnBlobFromStream(Col: integer; Stream: TStream); overload;
/// return a Column as a TSQLVar value, first Col is 0
// - the specified Temp variable will be used for temporary storage of
// svtUTF8/svtBlob values
procedure ColumnToSQLVar(Col: Integer; var Value: TSQLVar;
var Temp: RawByteString);
{$ifndef LVCL}
/// return a Column as a variant
// - a ftUTF8 TEXT content will be mapped into a generic WideString variant
// for pre-Unicode version of Delphi, and a generic UnicodeString (=string)
// since Delphi 2009: you may not loose any data during charset conversion
// - a ftBlob BLOB content will be mapped into a TBlobData AnsiString variant
function ColumnVariant(Col: integer): Variant; overload;
/// return a Column as a variant, first Col is 0
// - this default implementation will call Column*() method above
// - a ftUTF8 TEXT content will be mapped into a generic WideString variant
// for pre-Unicode version of Delphi, and a generic UnicodeString (=string)
// since Delphi 2009: you may not loose any data during charset conversion
// - a ftBlob BLOB content will be mapped into a TBlobData AnsiString variant
function ColumnToVariant(Col: integer; var Value: Variant): TSQLDBFieldType; overload;
{$endif}
/// return a special CURSOR Column content as a SynDB result set
// - Cursors are not handled internally by mORMot, but some databases (e.g.
// Oracle) usually use such structures to get data from stored procedures
// - such columns are mapped as ftNull internally - so this method is the only
// one giving access to the data rows
// - see also BoundCursor() if you want to access a CURSOR out parameter
function ColumnCursor(Col: integer): ISQLDBRows; overload;
/// return a Column integer value of the current Row, from a supplied column name
function ColumnInt(const ColName: RawUTF8): Int64; overload;
/// return a Column floating point value of the current Row, from a supplied column name
function ColumnDouble(const ColName: RawUTF8): double; overload;
/// return a Column floating point value of the current Row, from a supplied column name
function ColumnDateTime(const ColName: RawUTF8): TDateTime; overload;
/// return a column date and time value of the current Row, from a supplied column name
function ColumnTimestamp(const ColName: RawUTF8): TTimeLog; overload;
/// return a Column currency value of the current Row, from a supplied column name
function ColumnCurrency(const ColName: RawUTF8): currency; overload;
/// return a Column UTF-8 encoded text value of the current Row, from a supplied column name
function ColumnUTF8(const ColName: RawUTF8): RawUTF8; overload;
/// return a Column text value as generic VCL string of the current Row, from a supplied column name
function ColumnString(const ColName: RawUTF8): string; overload;
/// return a Column as a blob value of the current Row, from a supplied column name
function ColumnBlob(const ColName: RawUTF8): RawByteString; overload;
/// return a Column as a blob value of the current Row, from a supplied column name
function ColumnBlobBytes(const ColName: RawUTF8): TBytes; overload;
/// read a blob Column into the Stream parameter
procedure ColumnBlobToStream(const ColName: RawUTF8; Stream: TStream); overload;
/// write a blob Column into the Stream parameter
procedure ColumnBlobFromStream(const ColName: RawUTF8; Stream: TStream); overload;
{$ifndef LVCL}
/// return a Column as a variant, from a supplied column name
function ColumnVariant(const ColName: RawUTF8): Variant; overload;
/// return a Column as a variant, from a supplied column name
// - since a property getter can't be an overloaded method, we define one
// for the Column[] property
function GetColumnVariant(const ColName: RawUTF8): Variant;
/// return a special CURSOR Column content as a SynDB result set
// - Cursors are not handled internally by mORMot, but some databases (e.g.
// Oracle) usually use such structures to get data from strored procedures
// - such columns are mapped as ftNull internally - so this method is the only
// one giving access to the data rows
function ColumnCursor(const ColName: RawUTF8): ISQLDBRows; overload;
/// return a Column as a variant
// - this default property can be used to write simple code like this:
// ! procedure WriteFamily(const aName: RawUTF8);
// ! var I: ISQLDBRows;
// ! begin
// ! I := MyConnProps.Execute('select * from table where name=?',[aName]);
// ! while I.Step do
// ! writeln(I['FirstName'],' ',DateToStr(I['BirthDate']));
// ! I.ReleaseRows;
// ! end;
// - of course, using a variant and a column name will be a bit slower than
// direct access via the Column*() dedicated methods, but resulting code
// is fast in practice
property Column[const ColName: RawUTF8]: Variant read GetColumnVariant; default;
{$ifndef DELPHI5OROLDER}
/// create a TSQLDBRowVariantType able to access any field content via late binding
// - i.e. you can use Data.Name to access the 'Name' column of the current row
// - this Variant will point to the corresponding TSQLDBStatement instance,
// so it's not necessary to retrieve its value for each row; but once the
// associated ISQLDBRows instance is released, you won't be able to access
// its data - use RowDocVariant instead
// - typical use is:
// ! var Row: Variant;
// ! (...)
// ! with MyConnProps.Execute('select * from table where name=?',[aName]) do begin
// ! Row := RowData;
// ! while Step do
// ! writeln(Row.FirstName,Row.BirthDate);
// ! ReleaseRows;
// ! end;
function RowData: Variant;
/// create a TDocVariant custom variant containing all columns values
// - will create a "fast" TDocVariant object instance with all fields
procedure RowDocVariant(out aDocument: variant;
aOptions: TDocVariantOptions=JSON_OPTIONS_FAST);
{$endif DELPHI5OROLDER}
{$endif LVCL}
/// return the associated statement instance
function Instance: TSQLDBStatement;
// return all rows content as a JSON string
// - JSON data is retrieved with UTF-8 encoding
// - if Expanded is true, JSON data is an array of objects, for direct use
// with any Ajax or .NET client:
// & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
// - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
// & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
// - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
// format and contains true BLOB data
// - if ReturnedRowCount points to an integer variable, it will be filled with
// the number of row data returned (excluding field names)
// - similar to corresponding TSQLRequest.Execute method in SynSQLite3 unit
function FetchAllAsJSON(Expanded: boolean; ReturnedRowCount: PPtrInt=nil): RawUTF8;
// append all rows content as a JSON stream
// - JSON data is added to the supplied TStream, with UTF-8 encoding
// - if Expanded is true, JSON data is an array of objects, for direct use
// with any Ajax or .NET client:
// & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
// - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
// & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
// - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
// format and contains true BLOB data
// - similar to corresponding TSQLRequest.Execute method in SynSQLite3 unit
// - returns the number of row data returned (excluding field names)
function FetchAllToJSON(JSON: TStream; Expanded: boolean): PtrInt;
/// append all rows content as binary stream
// - will save the column types and name, then every data row in optimized
// binary format (faster and smaller than JSON)
// - you can specify a LIMIT for the data extent (default 0 meaning all data)
// - generates the format expected by TSQLDBProxyStatement
function FetchAllToBinary(Dest: TStream; MaxRowCount: cardinal=0;
DataRowPosition: PCardinalDynArray=nil): cardinal;
end;
/// generic interface to bind to prepared SQL query
// - inherits from ISQLDBRows, so gives access to the result columns data
// - not all TSQLDBStatement methods are available, but only those to bind
// parameters and retrieve data after execution
// - reference counting mechanism of this interface will feature statement
// cache (if available) for NewThreadSafeStatementPrepared() or PrepareInlined()
ISQLDBStatement = interface(ISQLDBRows)
['{EC27B81C-BD57-47D4-9711-ACFA27B583D7}']
/// bind a NULL value to a parameter
// - the leftmost SQL parameter has an index of 1
// - some providers (e.g. OleDB during MULTI INSERT statements) expect the
// proper column type to be set in BoundType, even for NULL values
procedure BindNull(Param: Integer; IO: TSQLDBParamInOutType=paramIn;
BoundType: TSQLDBFieldType=ftNull);
/// bind an integer value to a parameter
// - the leftmost SQL parameter has an index of 1
procedure Bind(Param: Integer; Value: Int64;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a double value to a parameter
// - the leftmost SQL parameter has an index of 1
procedure Bind(Param: Integer; Value: double;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a TDateTime value to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindDateTime(Param: Integer; Value: TDateTime;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a currency value to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindCurrency(Param: Integer; Value: currency;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a UTF-8 encoded string to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindTextU(Param: Integer; const Value: RawUTF8;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a UTF-8 encoded buffer text (#0 ended) to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindTextP(Param: Integer; Value: PUTF8Char;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a UTF-8 encoded string to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindTextS(Param: Integer; const Value: string;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a UTF-8 encoded string to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindTextW(Param: Integer; const Value: WideString;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a Blob buffer to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindBlob(Param: Integer; Data: pointer; Size: integer;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a Blob buffer to a parameter
// - the leftmost SQL parameter has an index of 1
procedure BindBlob(Param: Integer; const Data: RawByteString;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind a Variant value to a parameter
// - the leftmost SQL parameter has an index of 1
// - will call all virtual Bind*() methods from the Data type
// - if DataIsBlob is TRUE, will call BindBlob(RawByteString(Data)) instead
// of BindTextW(WideString(Variant)) - used e.g. by TQuery.AsBlob/AsBytes
procedure BindVariant(Param: Integer; const Data: Variant; DataIsBlob: boolean;
IO: TSQLDBParamInOutType=paramIn);
/// bind one TSQLVar value
// - the leftmost SQL parameter has an index of 1
procedure Bind(Param: Integer; const Data: TSQLVar;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind one RawUTF8 encoded value
// - the leftmost SQL parameter has an index of 1
// - the value should match the BindArray() format, i.e. be stored as in SQL
// (i.e. number, 'quoted string', 'YYYY-MM-DD hh:mm:ss', null)
procedure Bind(Param: Integer; ParamType: TSQLDBFieldType; const Value: RawUTF8;
ValueAlreadyUnquoted: boolean; IO: TSQLDBParamInOutType=paramIn); overload;
/// bind an array of const values
// - parameters marked as ? should be specified as method parameter in Params[]
// - BLOB parameters can be bound with this method, when set after encoding
// via BinToBase64WithMagic() call
// - TDateTime parameters can be bound with this method, when encoded via
// a DateToSQL() or DateTimeToSQL() call
procedure Bind(const Params: array of const;
IO: TSQLDBParamInOutType=paramIn); overload;
/// bind an array of fields from an existing SQL statement
// - can be used e.g. after ColumnsToSQLInsert() method call for fast data
// conversion between tables
procedure BindFromRows(const Fields: TSQLDBFieldTypeDynArray;
Rows: TSQLDBStatement);
/// bind a special CURSOR parameter to be returned as a SynDB result set
// - Cursors are not handled internally by mORMot, but some databases (e.g.
// Oracle) usually use such structures to get data from strored procedures
// - such parameters are mapped as ftUnknown
// - use BoundCursor() method to retrieve the corresponding ISQLDBRows after
// execution of the statement
procedure BindCursor(Param: integer);
/// return a special CURSOR parameter content as a SynDB result set
// - this method is not about a column, but a parameter defined with
// BindCursor() before method execution
// - Cursors are not handled internally by mORMot, but some databases (e.g.
// Oracle) usually use such structures to get data from strored procedures
// - this method allow direct access to the data rows after execution
function BoundCursor(Param: Integer): ISQLDBRows;
/// bind an array of values to a parameter
// - the leftmost SQL parameter has an index of 1
// - values are stored as in SQL (i.e. number, 'quoted string',
// 'YYYY-MM-DD hh:mm:ss', null)
// - this default implementation will raise an exception if the engine
// does not support array binding
procedure BindArray(Param: Integer; ParamType: TSQLDBFieldType;
const Values: TRawUTF8DynArray; ValuesCount: integer); overload;
/// bind an array of integer values to a parameter
// - the leftmost SQL parameter has an index of 1
// - this default implementation will raise an exception if the engine
// does not support array binding
procedure BindArray(Param: Integer; const Values: array of Int64); overload;
/// bind an array of double values to a parameter
// - the leftmost SQL parameter has an index of 1
// - this default implementation will raise an exception if the engine
// does not support array binding
procedure BindArray(Param: Integer; const Values: array of double); overload;
/// bind an array of TDateTime values to a parameter
// - the leftmost SQL parameter has an index of 1
// - values are stored as in SQL (i.e. 'YYYY-MM-DD hh:mm:ss')
// - this default implementation will raise an exception if the engine
// does not support array binding
procedure BindArrayDateTime(Param: Integer; const Values: array of TDateTime);
/// bind an array of currency values to a parameter
// - the leftmost SQL parameter has an index of 1
// - this default implementation will raise an exception if the engine
// does not support array binding
procedure BindArrayCurrency(Param: Integer; const Values: array of currency);
/// bind an array of RawUTF8 values to a parameter
// - the leftmost SQL parameter has an index of 1
// - values are stored as in SQL (i.e. 'quoted string')
// - this default implementation will raise an exception if the engine
// does not support array binding
procedure BindArray(Param: Integer; const Values: array of RawUTF8); overload;
{$ifndef LVCL}
/// retrieve the parameter content, after SQL execution
// - the leftmost SQL parameter has an index of 1
// - to be used e.g. with stored procedures:
// ! query := 'BEGIN TEST_PKG.DUMMY(?, ?, ?, ?, ?); END;';
// ! stmt := Props.NewThreadSafeStatementPrepared(query, false);
// ! stmt.Bind(1, in1, paramIn);
// ! stmt.BindTextU(2, in2, paramIn);
// ! stmt.BindTextU(3, in3, paramIn);
// ! stmt.BindTextS(4, '', paramOut); // to be retrieved with out1: string
// ! stmt.Bind(5, 0, paramOut); // to be retrieved with out2: integer
// ! stmt.ExecutePrepared;
// ! stmt.ParamToVariant(4, out1, true);
// ! stmt.ParamToVariant(5, out2, true);
// - the parameter should have been bound with IO=paramOut or IO=paramInOut
// if CheckIsOutParameter is TRUE
function ParamToVariant(Param: Integer; var Value: Variant;
CheckIsOutParameter: boolean=true): TSQLDBFieldType;
{$endif}
/// execute a prepared SQL statement
// - parameters marked as ? should have been already bound with Bind*() functions
// - should raise an Exception on any error
// - after execution, you can access any returned data via ISQLDBRows methods
procedure ExecutePrepared;
// execute a prepared SQL statement and return all rows content as a JSON string
// - JSON data is retrieved with UTF-8 encoding
// - if Expanded is true, JSON data is an array of objects, for direct use
// with any Ajax or .NET client:
// & [ {"col1":val11,"col2":"val12"},{"col1":val21,... ]
// - if Expanded is false, JSON data is serialized (used in TSQLTableJSON)
// & { "FieldCount":1,"Values":["col1","col2",val11,"val12",val21,..] }
// - BLOB field value is saved as Base64, in the '"\uFFF0base64encodedbinary"'
// format and contains true BLOB data
procedure ExecutePreparedAndFetchAllAsJSON(Expanded: boolean; out JSON: RawUTF8);
function GetForceBlobAsNull: boolean;
procedure SetForceBlobAsNull(value: boolean);
/// if set, any BLOB field won't be retrieved, and forced to be null
// - this may be used to speed up fetching the results for SQL requests
// with * statements
property ForceBlobAsNull: boolean read GetForceBlobAsNull write SetForceBlobAsNull;
function GetForceDateWithMS: boolean;
procedure SetForceDateWithMS(value: boolean);
/// if set, any ftDate field will contain the milliseconds information
// when serialized into ISO-8601 text
// - this setting is private to each statement, since may vary depending
// on data definition (e.g. ORM TDateTime/TDateTimeMS)
property ForceDateWithMS: boolean read GetForceDateWithMS write SetForceDateWithMS;
/// gets a number of updates made by latest executed statement
function UpdateCount: Integer;
end;
{$ifdef WITH_PROXY}
/// proxy commands implemented by TSQLDBProxyConnectionProperties.Process()
// - method signature expect "const Input" and "var Output" arguments
// - Input is not used for cConnect, cDisconnect, cGetForeignKeys,
// cTryStartTransaction, cCommit, cRollback and cServerTimestamp
// - Input is the TSQLDBProxyConnectionProperties instance for cInitialize
// - Input is the RawUTF8 table name for most cGet* metadata commands
// - Input is the SQL statement and associated bound parameters for cExecute,
// cExecuteToBinary, cExecuteToJSON, and cExecuteToExpandedJSON, encoded as
// TSQLDBProxyConnectionCommandExecute record
// - Output is not used for cConnect, cDisconnect, cCommit, cRollback and cExecute
// - Output is TSQLDBDefinition (i.e. DBMS type) for cInitialize
// - Output is TTimeLog for cServerTimestamp
// - Output is boolean for cTryStartTransaction
// - Output is TSQLDBColumnDefineDynArray for cGetFields
// - Output is TSQLDBIndexDefineDynArray for cGetIndexes
// - Output is TSynNameValue (fForeignKeys) for cGetForeignKeys
// - Output is TRawUTF8DynArray for cGetTableNames
// - Output is RawByteString result data for cExecuteToBinary
// - Output is UpdateCount: integer text for cExecute
// - Output is RawUTF8 result data for cExecuteToJSON and cExecuteToExpandedJSON
// - calls could be declared as such:
// ! Process(cGetToken,?,result: Int64);
// ! Process(cGetDBMS,User#1Hash: RawUTF8,fDBMS: TSQLDBDefinition);
// ! Process(cConnect,?,?);
// ! Process(cDisconnect,?,?);
// ! Process(cTryStartTransaction,?,started: boolean);
// ! Process(cCommit,?,?);
// ! Process(cRollback,?,?);
// ! Process(cServerTimestamp,?,result: TTimeLog);
// ! Process(cGetFields,aTableName: RawUTF8,Fields: TSQLDBColumnDefineDynArray);
// ! Process(cGetIndexes,aTableName: RawUTF8,Indexes: TSQLDBIndexDefineDynArray);
// ! Process(cGetTableNames,?,Tables: TRawUTF8DynArray);
// ! Process(cGetForeignKeys,?,fForeignKeys: TSynNameValue);
// ! Process(cExecute,Request: TSQLDBProxyConnectionCommandExecute,UpdateCount: integer);
// ! Process(cExecuteToBinary,Request: TSQLDBProxyConnectionCommandExecute,Data: RawByteString);
// ! Process(cExecuteToJSON,Request: TSQLDBProxyConnectionCommandExecute,JSON: RawUTF8);
// ! Process(cExecuteToExpandedJSON,Request: TSQLDBProxyConnectionCommandExecute,JSON: RawUTF8);
// - cExceptionRaised is a pseudo-command, used only for sending an exception
// to the client in case of execution problem on the server side
TSQLDBProxyConnectionCommand = (
cGetToken,cGetDBMS,
cConnect, cDisconnect, cTryStartTransaction, cCommit, cRollback,
cServerTimestamp,
cGetFields, cGetIndexes, cGetTableNames, cGetForeignKeys,
cExecute, cExecuteToBinary, cExecuteToJSON, cExecuteToExpandedJSON,
cQuit, cExceptionRaised);
{$endif WITH_PROXY}
{$M+} { published properties to be logged as JSON }
TSQLDBConnection = class;
TSQLDBConnectionProperties = class;
{$M-}
/// where the LIMIT clause should be inserted for a given SQL syntax
// - used by TSQLDBDefinitionLimitClause and SQLLimitClause() method
TSQLDBDefinitionLimitPosition = (posNone, posWhere, posSelect, posAfter, posOuter);
/// defines the LIMIT clause to be inserted for a given SQL syntax
// - used by TSQLDBDefinitionLimitClause and SQLLimitClause() method
TSQLDBDefinitionLimitClause = record
Position: TSQLDBDefinitionLimitPosition;
InsertFmt: PUTF8Char;
end;
/// possible events notified to TOnSQLDBProcess callback method
// - event handler is specified by TSQLDBConnectionProperties.OnProcess or
// TSQLDBConnection.OnProcess properties
// - speConnected / speDisconnected will notify TSQLDBConnection.Connect
// and TSQLDBConnection.Disconnect calls
// - speNonActive / speActive will be used to notify external DB blocking
// access, so can be used e.g. to change the mouse cursor shape (this trigger
// is re-entrant, i.e. it will be executed only once in case of nested calls)
// - speReconnected will be called if TSQLDBConnection did successfully
// recover its database connection (on error, TQuery will call
// speConnectionLost): this event will be called by TSQLDBConnection.Connect
// after a regular speConnected notification
// - speConnectionLost will be called by TQuery in case of broken connection,
// and if Disconnect/Reconnect did not restore it as expected (i.e. speReconnected)
// - speStartTransaction / speCommit / speRollback will notify the
// corresponding TSQLDBConnection.StartTransaction, TSQLDBConnection.Commit
// and TSQLDBConnection.Rollback methods
TOnSQLDBProcessEvent = (
speConnected, speDisconnected,
speNonActive, speActive,
speConnectionLost, speReconnected,
speStartTransaction, speCommit, speRollback);
/// event handler called during all external DB process
// - event handler is specified by TSQLDBConnectionProperties.OnProcess or
// TSQLDBConnection.OnProperties properties
TOnSQLDBProcess = procedure(Sender: TSQLDBConnection; Event: TOnSQLDBProcessEvent) of object;
/// event handler called when the low-level driver send some warning information
// - errors will trigger Exceptions, but sometimes the database driver returns
// some non critical information, which is logged and may be intercepted using
// the TSQLDBConnectionProperties.OnStatementInfo property
// - may be used e.g. to track ORA-28001 or ORA-28002 about account expire
// - is currently implemented by SynDBOracle, SynDBODBC and SynOleDB units
TOnSQLDBInfo = procedure(Sender: TSQLDBStatement; const Msg: RawUTF8) of object;
/// actions implemented by TSQLDBConnectionProperties.SharedTransaction()
TSQLDBSharedTransactionAction = (transBegin,
transCommitWithoutException, transCommitWithException, transRollback);
/// defines a callback signature able to handle multiple INSERT
// - may execute e.g. for 2 fields and 3 data rows on a database engine
// implementing INSERT with multiple VALUES (like MySQL, PostgreSQL, NexusDB,
// MSSQL or SQlite3), as implemented by
// TSQLDBConnectionProperties.MultipleValuesInsert() :
// $ INSERT INTO TableName(FieldNames[0],FieldNames[1]) VALUES
// $ (FieldValues[0][0],FieldValues[1][0]),
// $ (FieldValues[0][1],FieldValues[1][1]),
// $ (FieldValues[0][2],FieldValues[1][2]);
// - for other kind of DB which do not support multi values INSERT, may
// execute a dedicated driver command, like MSSQL "bulk insert" or Firebird
// "execute block"
TOnBatchInsert = procedure(Props: TSQLDBConnectionProperties;
const TableName: RawUTF8; const FieldNames: TRawUTF8DynArray;
const FieldTypes: TSQLDBFieldTypeArray; RowCount: integer;
const FieldValues: TRawUTF8DynArrayDynArray) of object;
/// specify the class of TSQLDBConnectionProperties
// - sometimes used to create connection properties instances, from a set
// of available classes (see e.g. SynDBExplorer or sample 16)
TSQLDBConnectionPropertiesClass = class of TSQLDBConnectionProperties;
/// abstract class used to set Database-related properties
// - handle e.g. the Database server location and connection parameters (like
// UserID and password)
// - should also provide some Database-specific generic SQL statement creation
// (e.g. how to create a Table), to be used e.g. by the mORMot layer
// - this class level will handle a single "main connection" - you may inherit
// from TSQLDBConnectionThreadSafe to maintain one connection per thread
TSQLDBConnectionProperties = class
protected
fServerName: RawUTF8;
fDatabaseName: RawUTF8;
fPassWord: RawUTF8;
fUserID: RawUTF8;
fForcedSchemaName: RawUTF8;
fMainConnection: TSQLDBConnection;
fBatchSendingAbilities: TSQLDBStatementCRUDs;
fBatchMaxSentAtOnce: integer;
fLoggedSQLMaxSize: integer;
fOnBatchInsert: TOnBatchInsert;
fDBMS: TSQLDBDefinition;
fUseCache, fStoreVoidStringAsNull, fLogSQLStatementOnException,
fRollbackOnDisconnect, fReconnectAfterConnectionError,
fFilterTableViewSchemaName: boolean;
fDateTimeFirstChar: AnsiChar;
{$ifndef UNICODE}
fVariantWideString: boolean;
{$endif}
fStatementMaxMemory: Int64;
fForeignKeys: TSynNameValue;
fSQLCreateField: TSQLDBFieldTypeDefinition;
fSQLCreateFieldMax: cardinal;
fSQLGetServerTimestamp: RawUTF8;
fEngineName: RawUTF8;
fOnProcess: TOnSQLDBProcess;
fOnStatementInfo: TOnSQLDBInfo;
fStatementCacheReplicates: integer;
fConnectionTimeOutTicks: Int64;
fSharedTransactions: array of record
SessionID: cardinal;
RefCount: integer;
Connection: TSQLDBConnection;
end;
fExecuteWhenConnected: TRawUTF8DynArray;
procedure SetConnectionTimeOutMinutes(minutes: cardinal);
function GetConnectionTimeOutMinutes: cardinal;
// this default implementation just returns the fDBMS value or dDefault
// (never returns dUnknwown)
function GetDBMS: TSQLDBDefinition; virtual;
function GetDBMSName: RawUTF8; virtual;
function GetForeignKeysData: RawByteString;
procedure SetForeignKeysData(const Value: RawByteString);
function FieldsFromList(const aFields: TSQLDBColumnDefineDynArray; aExcludeTypes: TSQLDBFieldTypes): RawUTF8;
function GetMainConnection: TSQLDBConnection; virtual;
function GetDatabaseNameSafe: RawUTF8; virtual;
/// any overriden TSQLDBConnectionProperties class should call it in the
// initialization section of its implementation unit to be recognized
class procedure RegisterClassNameForDefinition;
/// will be called at the end of constructor
// - this default implementation will do nothing
procedure SetInternalProperties; virtual;
/// Assign schema name to owner from ForceSchemaName or UserID or Database name
procedure SetSchemaNameToOwner(out Owner: RawUTF8); virtual;
/// SQL statement to get all field/column names for a specified Table
// - used by GetFieldDefinitions public method
// - should return a SQL "SELECT" statement with the field names as first
// column, a textual field type as 2nd column, then field length, then
// numeric precision and scale as 3rd, 4th and 5th columns, and the index
// count in 6th column
// - this default implementation just returns nothing
// - if this method is overridden, the ColumnTypeNativeToDB() method should
// also be overridden in order to allow conversion from native column
// type into the corresponding TSQLDBFieldType
function SQLGetField(const aTableName: RawUTF8): RawUTF8; virtual;
/// SQL statement to get advanced information about all indexes for a Table
// - should return a SQL "SELECT" statement with the index names as first
function SQLGetIndex(const aTableName: RawUTF8): RawUTF8; virtual;
/// SQL statement to get all parameter for a specified Stored Procedure
// - used by GetProcedureParameters public method
// - should return a SQL "SELECT" statement with the parameter names as first
// column, a textual field type as 2nd column, then parameter length as 3rd, then
// parameter direction as 4th
// - this default implementation just returns nothing
// - if this method is overridden, the ColumnTypeNativeToDB() method should
// also be overridden in order to allow conversion from native column
// type into the corresponding TSQLDBFieldType
function SQLGetParameter(const aProcName: RawUTF8): RawUTF8; virtual;
/// SQL statement to get all stored procedure names for current connection
// - used by GetProcedureNames public method
// - should return a SQL "SELECT" statement with the procedure names as unique column
// - this default implementation just returns nothing
// - if this method is overridden, the ColumnTypeNativeToDB() method should
// also be overridden in order to allow conversion from native column
// type into the corresponding TSQLDBFieldType
function SQLGetProcedure: RawUTF8; virtual;
/// SQL statement to get all table names
// - used by GetTableNames public method
// - should return a SQL "SELECT" statement with the table names as
// first column (any other columns will be ignored)
// - this default implementation just returns nothing
function SQLGetTableNames: RawUTF8; virtual;
/// SQL statement to get all view names
// - used by GetViewNames public method
// - should return a SQL "SELECT" statement with the view names as
// first column (any other columns will be ignored)
// - this default implementation just returns nothing
function SQLGetViewNames: RawUTF8; virtual;
/// should initialize fForeignKeys content with all foreign keys of this
// database
// - used by GetForeignKey method
procedure GetForeignKeys; virtual; abstract;
/// will use fSQLCreateField[Max] to create the SQL column definition
// - this default virtual implementation will handle properly all supported
// database engines, assuming aField.ColumnType as in TSQLDBFieldTypeDefinition
// - if the field is a primary key, aAddPrimaryKey may be modified to contain
// some text to be appended at the end of the ALTER/CREATE TABLE statement
function SQLFieldCreate(const aField: TSQLDBColumnCreate;
var aAddPrimaryKey: RawUTF8): RawUTF8; virtual;
/// wrapper around GetIndexes() + set Fields[].ColumnIndexed in consequence
// - used by some overridden versions of GetFields() method
procedure GetIndexesAndSetFieldsColumnIndexed(const aTableName: RawUTF8;
var Fields: TSQLDBColumnDefineDynArray);
/// check if the exception or its error message is about DB connection error