function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
jhartjhart 

Bug: Dynamic DML not allowed on SObject lists

You can use Dynamic DML to insert untyped SObjects one-at-a-time, but not as an array:

SObjectType token = Schema.getGlobalDescribe().get('contact');
SObject[] objs = new SObject[0];
for (integer i = 0; i<2; i++) {
SObject tmp = token.newSObject();
tmp.put('LastName', 'test' + i);
objs.add(tmp);
}

boolean showBug = true;

if (showBug) { // fails with "DML not allowed on abstract class SObject"
insert objs;
}
else { // works
for (SObject i : objs) insert i;
}

 

 

Best Answer chosen by Admin (Salesforce Developers) 
jhartjhart

I'm told this has been fixed for the next release (Winter '11).

 

In the meantime, you can use a workaround to do bulk DML on SObjects.  You need to get an SObject collection that, under the hood, is actually typed.  You can get these "actually typed" SObject collections using queries:

 

 

// note the bogus Id is necessary to prevent "non-selective index/data skew" errors
// when your desired type (eg, Task) has >100K rows
SObject[] tt = Database.query('select Id from Task where Id = \'000000000000000\' limit 1');
// or
// tt = [select Id from Task where Id = '000000000000000' limit 1];
//

tt.clear();  // not strictly necessary if using a bogus ID above

Schema.SObjectType token = Schema.getGlobalDescribe().get('task');
for (integer i=0; i<2; i++) {
  SObject t = token.newSObject();
  t.put('OwnerId',UserInfo.getUserId());
  t.put(...);
  tt.add(t);
  }

// or
// for (integer i=0; i<2; i++) tt.add(new Task(...));
 
insert tt; // WORKS

 

 

All Answers

jhartjhart

Note - salesforce developer support has acknowledged & reproduced the bug; no timeline yet for a fix.

jhartjhart

Salesforce support has logged this as a feature request, more than a bug.

 

In case anyone wonders why this is important, here goes:

 

This is a serious problem for managed packages that need to support multiple sharing models for their custom objects.
 
In our case, most of our customers want our primary object to use "Public Read/Write" as its sharing model.  However, other customers want it to use the "Private" model.
 
To support the "Private" model, my code needs to be able to insert sharing rows for my custom objects.
 
However, if my Apex code directly references those __Share objects, then my objects are *stuck* at the "Private" model.  Once salesforce detects a static reference to the __Share object, I can no longer change the sharing model back to "Public Read/Write".
 
Therefore, to support both sharing models, we cannot have static references to the __Share objects.  Instead, we need to use Dynamic Apex for all our interactions with the __Share objects.
 
Having to do these one-row-at-a-time means that we will blow out execution limits pretty quickly.  This is bad for us, and bad for salesforce (because efficiency is lost doing row-at-a-time inserts).

cogcog

Ha, I was thinking i was doing something wrong. Thought of searching it and here it is. One more bug that no one knows. Have to think about workarounds.

disturbed118disturbed118

This needs to be implemented List of type Sobjects should be allowed to do dml operations.

jhartjhart

I'm told this has been fixed for the next release (Winter '11).

 

In the meantime, you can use a workaround to do bulk DML on SObjects.  You need to get an SObject collection that, under the hood, is actually typed.  You can get these "actually typed" SObject collections using queries:

 

 

// note the bogus Id is necessary to prevent "non-selective index/data skew" errors
// when your desired type (eg, Task) has >100K rows
SObject[] tt = Database.query('select Id from Task where Id = \'000000000000000\' limit 1');
// or
// tt = [select Id from Task where Id = '000000000000000' limit 1];
//

tt.clear();  // not strictly necessary if using a bogus ID above

Schema.SObjectType token = Schema.getGlobalDescribe().get('task');
for (integer i=0; i<2; i++) {
  SObject t = token.newSObject();
  t.put('OwnerId',UserInfo.getUserId());
  t.put(...);
  tt.add(t);
  }

// or
// for (integer i=0; i<2; i++) tt.add(new Task(...));
 
insert tt; // WORKS

 

 

This was selected as the best answer
sumansuman

This issue is still not fixed (even in winter 11). Thnx for the work around Jhart. I'll try that.

jhartjhart

Suman -

 

 

I need to amend the above post, because if you have an object type with a large # of rows (>100K), then you'll get a "data skew / non-selective Index" error with the above query.

 

You need to use something like this to be safe:

 

 

SObject[] tt = Database.query('select Id from Task  where Id = \'000000000000000\' limit 1');

 

 

 

(edit - woah, firefox 4 beta does not post well to this forum!)