From 7609b12b25a62a1d8278afb319bdedee48f22505 Mon Sep 17 00:00:00 2001 From: Arnaud Bouchez Date: Mon, 27 Feb 2023 22:07:57 +0100 Subject: [PATCH] disable ftBlob array binding on mormot.db.sql.postgresql - as reported by https://github.com/synopse/mORMot2/issues/147 --- src/db/mormot.db.core.pas | 5 ++++- src/db/mormot.db.sql.pas | 7 ++++++- src/db/mormot.db.sql.postgres.pas | 1 + src/mormot.commit.inc | 2 +- src/orm/mormot.orm.core.pas | 4 ++-- src/orm/mormot.orm.server.pas | 16 +++++++++++----- src/orm/mormot.orm.sql.pas | 8 ++++++-- 7 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/db/mormot.db.core.pas b/src/db/mormot.db.core.pas index fe6fb9a8e..bc6f44beb 100644 --- a/src/db/mormot.db.core.pas +++ b/src/db/mormot.db.core.pas @@ -1220,6 +1220,8 @@ procedure SetID(const U: RawByteString; var result: TID); overload; // and server TOrmModel tables or fields do not match // - boOnlyObjects will force to generate only a JSON array of raw JSON // objects with no BATCH prefix nor verbs + // - boMayHaveBlob could be set if some BLOB are likely to appear in the datga + // so that some engines could disabled e.g. array binding TRestBatchOption = ( boInsertOrIgnore, boInsertOrReplace, @@ -1228,7 +1230,8 @@ procedure SetID(const U: RawByteString; var result: TID); overload; boPutNoCacheFlush, boRollbackOnError, boNoModelEncoding, - boOnlyObjects); + boOnlyObjects, + boMayHaveBlob); /// a set of options for TRest.BatchStart() process // - TJsonObjectDecoder will use it to compute the corresponding SQL diff --git a/src/db/mormot.db.sql.pas b/src/db/mormot.db.sql.pas index 0c4f522ed..9f46a3a5f 100644 --- a/src/db/mormot.db.sql.pas +++ b/src/db/mormot.db.sql.pas @@ -1243,7 +1243,8 @@ TSqlDBConnectionProperties = class fBatchSendingAbilities: TSqlDBStatementCRUDs; fUseCache, fStoreVoidStringAsNull, fLogSqlStatementOnException, fRollbackOnDisconnect, fReconnectAfterConnectionError, - fEnsureColumnNameUnique, fFilterTableViewSchemaName: boolean; + fEnsureColumnNameUnique, fFilterTableViewSchemaName, + fNoBlobBindArray: boolean; {$ifndef UNICODE} fVariantWideString: boolean; {$endif UNICODE} @@ -1875,6 +1876,10 @@ TSqlDBConnectionProperties = class // - as used by SqlDateToIso8601Quoted() and BindArray() property DateTimeFirstChar: AnsiChar read fDateTimeFirstChar write fDateTimeFirstChar; + /// if the engine do not support BindArray(ftBlob) + // - only set for TSqlDBPostgresConnectionProperties by now + property NoBlobBindArray: boolean + read fNoBlobBindArray write fNoBlobBindArray; {$ifndef UNICODE} /// set to true to force all variant conversion to WideString instead of // the default faster AnsiString, for pre-Unicode version of Delphi diff --git a/src/db/mormot.db.sql.postgres.pas b/src/db/mormot.db.sql.postgres.pas index 07052a8f9..366e48cf6 100644 --- a/src/db/mormot.db.sql.postgres.pas +++ b/src/db/mormot.db.sql.postgres.pas @@ -557,6 +557,7 @@ constructor TSqlDBPostgresConnectionProperties.Create( // JsonDecodedPrepareToSql will detect cPostgreBulkArray and set // DecodedFieldTypesToUnnest -> fast bulk insert/delete/update fBatchSendingAbilities := [cCreate, cDelete, cUpdate, cPostgreBulkArray]; + fNoBlobBindArray := true; // no BindArray() on ftBlob // disable MultiInsert SQL and rely on cPostgreBulkArray process for cCreate fOnBatchInsert := nil; // see TRestStorageExternal.InternalBatchStop end; diff --git a/src/mormot.commit.inc b/src/mormot.commit.inc index 792ebabb9..f73adea5c 100644 --- a/src/mormot.commit.inc +++ b/src/mormot.commit.inc @@ -1 +1 @@ -'2.0.4974' +'2.0.4975' diff --git a/src/orm/mormot.orm.core.pas b/src/orm/mormot.orm.core.pas index e03fd9560..6009b620c 100644 --- a/src/orm/mormot.orm.core.pas +++ b/src/orm/mormot.orm.core.pas @@ -11168,10 +11168,10 @@ procedure TRestBatch.Reset(aTable: TOrmClass; fBatch.AddComma; end; Options := Options - BATCH_OPTIONS_CLIENTONLY; - if byte(Options) <> 0 then + if word(Options) <> 0 then begin fBatch.AddShort('"options",'); - fBatch.Add(byte(Options)); + fBatch.Add(word(Options)); fBatch.AddComma; end; end; diff --git a/src/orm/mormot.orm.server.pas b/src/orm/mormot.orm.server.pas index 8d6dc425d..58acb22a3 100644 --- a/src/orm/mormot.orm.server.pas +++ b/src/orm/mormot.orm.server.pas @@ -960,6 +960,7 @@ function TRestOrmServer.RecordVersionSynchronizeSlaveToBatch(Table: TOrmClass; rec: TOrm; deletedminid: TID; deleted: TOrmTableDeleted; + opt: TRestBatchOptions; {%H-}log: ISynLog; begin log := fRest.LogClass.Enter( @@ -997,7 +998,11 @@ function TRestOrmServer.RecordVersionSynchronizeSlaveToBatch(Table: TOrmClass; deletedminid + ORMVERSION_DELETEID_RANGE]); if listdeleted = nil then exit; // DB error - result := TRestBatch.Create(self, nil, 10000); + opt := [boExtendedJson]; // default options + if (listupdated.RowCount <> 0) and + (props.BlobFields <> nil) then + include(opt, boMayHaveBlob); + result := TRestBatch.Create(self, nil, 10000, opt); result.OnWrite := OnWrite; if (listupdated.RowCount = 0) and (listdeleted.RowCount = 0) then @@ -2030,8 +2035,9 @@ function TRestOrmServerBatchSend.IsNotAllowed: boolean; begin result := (fUriContext <> nil) and (fUriContext.Command = execOrmWrite) and - not fUriContext.CanExecuteOrmWrite(BATCH_METHOD[fEncoding], - fRunTable, fRunTableIndex, fValueID, fUriContext.Call.RestAccessRights^); + not fUriContext.CanExecuteOrmWrite( + BATCH_METHOD[fEncoding], fRunTable, fRunTableIndex, + fValueID, fUriContext.Call.RestAccessRights^); end; procedure TRestOrmServerBatchSend.ParseCommand; @@ -2411,10 +2417,10 @@ procedure TRestOrmServerBatchSend.ParseHeader; if IdemPChar(fParse.Json, '"OPTIONS",') then begin inc(fParse.Json, 10); - byte(fBatchOptions) := GetNextItemCardinal(fParse.Json, ','); + word(fBatchOptions) := GetNextItemCardinal(fParse.Json, ','); end else - byte(fBatchOptions) := 0; + word(fBatchOptions) := 0; end; procedure TRestOrmServerBatchSend.ParseAndExecute; diff --git a/src/orm/mormot.orm.sql.pas b/src/orm/mormot.orm.sql.pas index 348529aad..00c1a2c34 100644 --- a/src/orm/mormot.orm.sql.pas +++ b/src/orm/mormot.orm.sql.pas @@ -1019,6 +1019,9 @@ function TRestStorageExternal.InternalBatchStart(Encoding: TRestBatchEncoding; (Method in [mPOST..mDELETE]) and (BATCH[Method] in fProperties.BatchSendingAbilities) then begin + if (boMayHaveBlob in BatchOptions) and + fProperties.NoBlobBindArray then + exit; // slower but safer access with no array binding StorageLock(true {$ifdef DEBUGSTORAGELOCK}, 'ExtBatchStart' {$endif}); // lock protected by try..finally in TRestServer.RunBatch caller try @@ -1100,8 +1103,9 @@ procedure TRestStorageExternal.InternalBatchStop; if {%H-}Fields = nil then begin Decode.AssignFieldNamesTo(Fields); - SQL := JsonDecodedPrepareToSql(Decode, ExternalFields, Types, - Occasion, fBatchOptions, {array=}true); + SQL := JsonDecodedPrepareToSql( + Decode, ExternalFields, Types, Occasion, fBatchOptions, + {array=}true); SetLength(Values, Decode.FieldCount); ValuesMax := fBatchCount - BatchBegin; if ValuesMax > max then