Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | added aUseBatchMode optional parameter to TSQLRecordMany.ManyDelete() method |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
c0c4838f266af1732b00c6552ab808b2 |
User & Date: | ab 2012-08-20 07:48:24 |
2012-08-20
| ||
13:00 |
| |
07:48 | added aUseBatchMode optional parameter to TSQLRecordMany.ManyDelete() method check-in: c0c4838f26 user: ab tags: trunk | |
2012-08-18
| ||
06:31 | updated documentation check-in: 6fda439201 user: ab tags: trunk | |
Changes to SQLite3/SQLite3Commons.pas.
644 645 646 647 648 649 650 651 652 653 654 655 656 657 .... 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 ..... 22800 22801 22802 22803 22804 22805 22806 22807 22808 22809 22810 22811 22812 22813 22814 22815 22816 22817 22818 22819 22820 22821 22822 22823 22824 22825 22826 22827 22828 22829 |
parameters wrapper for both method callbacks and interface-based services: now aParams.Context.ID is to be used insted of aParams.ID - added TJSONObjectDecoder record/object helper for JSON object decoding (used e.g. by GetJSONObjectAsSQL() function, and for SQlite3DB process) - removed TSQLRecordExternal class type, to allow any TSQLRecord (e.g. TSQLRecordMany) to be used with VirtualTableExternalRegister() - there was indeed no implementation requirement to force a specific class type - now JSON parser will handle #1..' ' chars as whitespace (not only ' ') - now huge service JSON response is truncated (to default 20 KB) in logs String usage in the Synopse SQLite3 database framework: - RawUTF8 is used for every internal data usage, since both SQLite3 and JSON do expect UTF-8 encoding ................................................................................ // - source ID is taken from the fSourceID field (set by TSQLRecord.Create) // - note that if the Source record has just been added, fSourceID is not // set, so this method will fail: please call the other overloaded method function ManyAdd(aClient: TSQLRest; aDestID: Integer; NoDuplicates: boolean=false): boolean; overload; /// will delete the record associated with a particular Source/Dest pair // - will return TRUE if the pair was found and successfully deleted function ManyDelete(aClient: TSQLRest; aSourceID, aDestID: Integer): boolean; overload; /// will delete the record associated with the current source and a specified Dest // - source ID is taken from the fSourceID field (set by TSQLRecord.Create) // - note that if the Source record has just been added, fSourceID is not // set, so this method will fail: please call the other overloaded method function ManyDelete(aClient: TSQLRest; aDestID: Integer): boolean; overload; /// will retrieve the record associated with a particular Source/Dest pair // - will return TRUE if the pair was found // - in this case, all "through" columns are available in the TSQLRecordMany // field instance function ManySelect(aClient: TSQLRest; aSourceID, aDestID: Integer): boolean; overload; /// will retrieve the record associated with the current source and a specified Dest // - source ID is taken from the fSourceID field (set by TSQLRecord.Create) ................................................................................ begin if fSourceID=nil then result := false else // avoid GPF result := DestGet(aClient,fSourceID^,DestIDs); // fSourceID has been set by TSQLRecord.Create end; function TSQLRecordMany.ManyDelete(aClient: TSQLRest; aSourceID, aDestID: Integer): boolean; var aID: integer; begin result := false; if (self=nil) or (aClient=nil) or (aSourceID=0) or (aDestID=0) then exit; aID := InternalIDFromSourceDest(aClient,aSourceID,aDestID); if aID<>0 then result := aClient.Delete(RecordClass,aID); end; function TSQLRecordMany.ManyDelete(aClient: TSQLRest; aDestID: Integer): boolean; begin if fSourceID=nil then result := false else // avoid GPF result := ManyDelete(aClient,fSourceID^,aDestID); end; function TSQLRecordMany.ManySelect(aClient: TSQLRest; aSourceID, aDestID: Integer): boolean; begin if (self=nil) or (aClient=nil) or (aSourceID=0) or (aDestID=0) then result := false else // invalid parameters result := aClient.Retrieve(FormatUTF8('Source=:(%): AND Dest=:(%):', |
> > > > | > > > > | > | > > > | | > | |
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 .... 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 ..... 22809 22810 22811 22812 22813 22814 22815 22816 22817 22818 22819 22820 22821 22822 22823 22824 22825 22826 22827 22828 22829 22830 22831 22832 22833 22834 22835 22836 22837 22838 22839 22840 22841 22842 |
parameters wrapper for both method callbacks and interface-based services: now aParams.Context.ID is to be used insted of aParams.ID - added TJSONObjectDecoder record/object helper for JSON object decoding (used e.g. by GetJSONObjectAsSQL() function, and for SQlite3DB process) - removed TSQLRecordExternal class type, to allow any TSQLRecord (e.g. TSQLRecordMany) to be used with VirtualTableExternalRegister() - there was indeed no implementation requirement to force a specific class type - added aUseBatchMode optional parameter to TSQLRecordMany.ManyDelete() method - now JSON parser will handle #1..' ' chars as whitespace (not only ' ') - now huge service JSON response is truncated (to default 20 KB) in logs String usage in the Synopse SQLite3 database framework: - RawUTF8 is used for every internal data usage, since both SQLite3 and JSON do expect UTF-8 encoding ................................................................................ // - source ID is taken from the fSourceID field (set by TSQLRecord.Create) // - note that if the Source record has just been added, fSourceID is not // set, so this method will fail: please call the other overloaded method function ManyAdd(aClient: TSQLRest; aDestID: Integer; NoDuplicates: boolean=false): boolean; overload; /// will delete the record associated with a particular Source/Dest pair // - will return TRUE if the pair was found and successfully deleted // - if aUseBatchMode is TRUE, it will use aClient.BatchDelete() instead // of the slower aClient.Delete() method - but you shall call it within // a BatchStart / BatchSend block function ManyDelete(aClient: TSQLRest; aSourceID, aDestID: Integer; aUseBatchMode: boolean=false): boolean; overload; /// will delete the record associated with the current source and a specified Dest // - source ID is taken from the fSourceID field (set by TSQLRecord.Create) // - note that if the Source record has just been added, fSourceID is not // set, so this method will fail: please call the other overloaded method // - if aUseBatchMode is TRUE, it will use aClient.BatchDelete() instead // of the slower aClient.Delete() method - but you shall call it within // a BatchStart / BatchSend block function ManyDelete(aClient: TSQLRest; aDestID: Integer; aUseBatchMode: boolean=false): boolean; overload; /// will retrieve the record associated with a particular Source/Dest pair // - will return TRUE if the pair was found // - in this case, all "through" columns are available in the TSQLRecordMany // field instance function ManySelect(aClient: TSQLRest; aSourceID, aDestID: Integer): boolean; overload; /// will retrieve the record associated with the current source and a specified Dest // - source ID is taken from the fSourceID field (set by TSQLRecord.Create) ................................................................................ begin if fSourceID=nil then result := false else // avoid GPF result := DestGet(aClient,fSourceID^,DestIDs); // fSourceID has been set by TSQLRecord.Create end; function TSQLRecordMany.ManyDelete(aClient: TSQLRest; aSourceID, aDestID: Integer; aUseBatchMode: boolean=false): boolean; var aID: integer; begin result := false; if (self=nil) or (aClient=nil) or (aSourceID=0) or (aDestID=0) then exit; aID := InternalIDFromSourceDest(aClient,aSourceID,aDestID); if aID<>0 then if aUseBatchMode and aClient.InheritsFrom(TSQLRestClientURI) then result := TSQLRestClientURI(aClient).BatchDelete(RecordClass,aID)>=0 else result := aClient.Delete(RecordClass,aID); end; function TSQLRecordMany.ManyDelete(aClient: TSQLRest; aDestID: Integer; aUseBatchMode: boolean=false): boolean; begin if fSourceID=nil then result := false else // avoid GPF result := ManyDelete(aClient,fSourceID^,aDestID,aUseBatchMode); end; function TSQLRecordMany.ManySelect(aClient: TSQLRest; aSourceID, aDestID: Integer): boolean; begin if (self=nil) or (aClient=nil) or (aSourceID=0) or (aDestID=0) then result := false else // invalid parameters result := aClient.Retrieve(FormatUTF8('Source=:(%): AND Dest=:(%):', |
Changes to SQLite3/SQLite3DB.pas.
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 ... 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 ... 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 ... 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
fSelectOneDirectSQL := fSelectOneDirectSQL+' where ID=?'; fSelectAllDirectSQL := FormatUTF8('select %,ID from %',[StoredClassProps.SQLInsertSet,fTableName]); end; function TSQLRestServerStaticExternal.AdaptSQLForEngineList(var SQL: RawUTF8): boolean; var Prop: ShortString; P: PUTF8Char; BPos,RowIDFromPos,TablePos,RowIDWherePos: integer; tmp: RawUTF8; procedure GetFieldProp; var i,L: integer; B: PUTF8Char; begin if P^=#0 then begin Prop[0] := #0; ................................................................................ end; begin result := inherited AdaptSQLForEngineList(SQL); if result or (SQL='') then exit; // found generic 'SELECT * FROM table' query // change 'RowID' into 'ID' column name, and SQLTableName into fTableName // process 'SELECT Field1,Field2 FROM table WHERE RowID=... AND/OR/NOT Field2=' RowIDFromPos := 0; RowIDWherePos := 0; P := pointer(SQL); GetFieldProp; if Prop<>'SELECT' then exit; repeat GetFieldProp; if Prop='' then exit; if Prop='ROWID' then if RowIDFromPos>0 then exit else RowIDFromPos := BPos; ................................................................................ if Prop='ORDER' then begin // simple ORDER BY clause is accepted GetFieldProp; if Prop<>'BY' then exit; GetFieldProp; if not (GotoNextNotSpace(P)^ in [#0,';']) then exit; // allow only one column name if Prop='ROWID' then system.delete(SQL,BPos,3); // 'RowID' -> 'ID' end else if Prop='WHERE' then repeat GetFieldProp; if Prop='NOT' then GetFieldProp; // allow field1=456 AND NOT field2='Toto' if Prop='' then exit; ................................................................................ repeat inc(P) until P^ in [#0..' ',';',')']; // go to end of value P := GotoNextNotSpace(P); if PWord(P)^=ord(')')+ord(':')shl 8 then inc(P,2); // ignore :(...): parameter P := GotoNextNotSpace(P); if P^ in [#0,';'] then break; // properly ended the WHERE clause GetFieldProp; if (Prop<>'AND') and (Prop<>'OR') then exit; until false else if Prop<>'' then exit; if RowIDWherePos>0 then system.delete(SQL,RowIDWherePos,3); // 'RowID' -> 'ID' system.delete(SQL,TablePos,length(StoredClassProps.SQLTableName)); insert(fTableName,SQL,TablePos); // change table name if RowIDFromPos>0 then system.delete(SQL,RowIDFromPos,3); // 'RowID' -> 'ID' result := true; |
| > > > > | > > > > > > > > > > > > > > > > > > > > > > > |
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 ... 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 ... 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 ... 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 |
fSelectOneDirectSQL := fSelectOneDirectSQL+' where ID=?'; fSelectAllDirectSQL := FormatUTF8('select %,ID from %',[StoredClassProps.SQLInsertSet,fTableName]); end; function TSQLRestServerStaticExternal.AdaptSQLForEngineList(var SQL: RawUTF8): boolean; var Prop: ShortString; P: PUTF8Char; BPos,AfterSelectPos,RowIDFromPos,TablePos,RowIDWherePos,RowIDOrderByPos: integer; LimitPos, LimitRowCount,err: integer; tmp: RawUTF8; procedure GetFieldProp; var i,L: integer; B: PUTF8Char; begin if P^=#0 then begin Prop[0] := #0; ................................................................................ end; begin result := inherited AdaptSQLForEngineList(SQL); if result or (SQL='') then exit; // found generic 'SELECT * FROM table' query // change 'RowID' into 'ID' column name, and SQLTableName into fTableName // process 'SELECT Field1,Field2 FROM table WHERE RowID=... AND/OR/NOT Field2=' LimitPos := 0; RowIDFromPos := 0; RowIDWherePos := 0; RowIDOrderByPos := 0; P := pointer(SQL); GetFieldProp; if Prop<>'SELECT' then exit; AfterSelectPos := BPos; repeat GetFieldProp; if Prop='' then exit; if Prop='ROWID' then if RowIDFromPos>0 then exit else RowIDFromPos := BPos; ................................................................................ if Prop='ORDER' then begin // simple ORDER BY clause is accepted GetFieldProp; if Prop<>'BY' then exit; GetFieldProp; if not (GotoNextNotSpace(P)^ in [#0,';']) then exit; // allow only one column name if Prop='ROWID' then RowIDOrderByPos := BPos; // 'RowID' -> 'ID'; GetFieldProp; if Prop='LIMIT' then LimitPos := BPos; end else if Prop='WHERE' then repeat GetFieldProp; if Prop='NOT' then GetFieldProp; // allow field1=456 AND NOT field2='Toto' if Prop='' then exit; ................................................................................ repeat inc(P) until P^ in [#0..' ',';',')']; // go to end of value P := GotoNextNotSpace(P); if PWord(P)^=ord(')')+ord(':')shl 8 then inc(P,2); // ignore :(...): parameter P := GotoNextNotSpace(P); if P^ in [#0,';'] then break; // properly ended the WHERE clause GetFieldProp; if Prop='LIMIT' then begin LimitPos := BPos; break; end else if (Prop<>'AND') and (Prop<>'OR') then exit; until false else if Prop='LIMIT' then LimitPos := BPos else if Prop<>'' then exit; // handle LIMIT # statement at the end of the SQL if LimitPos>0 then begin GetFieldProp; val(Prop,LimitRowCount,err); if err<>0 then exit; exit; // TODO: add LIMIT clause using SynDB.DB_SQLLIMITCLAUSE[]+LimitRowCount // (so we need to have DBMS: TSQLDBDefinition at TSQLDBConnectionProperties level) system.delete(SQL,LimitPos,P-pointer(SQL)+LimitPos); // erase 'LIMIT #' end; // convert all 'RowID' to 'ID' if RowIDOrderByPos>0 then system.delete(SQL,RowIDOrderByPos,3); if RowIDWherePos>0 then system.delete(SQL,RowIDWherePos,3); // 'RowID' -> 'ID' system.delete(SQL,TablePos,length(StoredClassProps.SQLTableName)); insert(fTableName,SQL,TablePos); // change table name if RowIDFromPos>0 then system.delete(SQL,RowIDFromPos,3); // 'RowID' -> 'ID' result := true; |
Changes to SynDB.pas.
1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 |
'', '', ''); /// the known function prototypes used to retrieve a mORMot field type // from the native type as returned by a TSQLDBSQLGetField function DB_COLUMNNATIVETODB: array[TSQLDBDefinition] of TSQLDBColumnTypeNativeToDB = ( nil, ColumnTypeNativeToDBOracle, nil, nil, nil, nil); {$ifdef EMULATES_TQUERY} { -------------- TQuery TField TParam emulation classes and types } type /// generic Exception type raised by the TQuery class |
> > > > > > > > > > > > > > |
1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 |
'', '', ''); /// the known function prototypes used to retrieve a mORMot field type // from the native type as returned by a TSQLDBSQLGetField function DB_COLUMNNATIVETODB: array[TSQLDBDefinition] of TSQLDBColumnTypeNativeToDB = ( nil, ColumnTypeNativeToDBOracle, nil, nil, nil, nil); /// the known SQL syntax to limit the number of returned rows in a SELECT // - Positon indicates if should be included within the WHERE clause, // at the beginning of the SQL statement, or at the end of the SQL statement // - Insert will replace '%' with the maximum number of lines to be retrieved DB_SQLLIMITCLAUSE: array[TSQLDBDefinition] of record Position: (posNone, posWhere, posSelect, posAfter); Insert: RawUTF8; end = ( (Position: posNone; Insert:''), (Position: posWhere; Insert:'rownum<=%'), (Position: posSelect; Insert:'top %'), (Position: posSelect; Insert:'top %'), (Position: posAfter; Insert:'limit %'), (Position: posAfter; Insert:'limit %')); {$ifdef EMULATES_TQUERY} { -------------- TQuery TField TParam emulation classes and types } type /// generic Exception type raised by the TQuery class |