[SalesForce] SOQL results returning RecordTypeId which is not included in query

I am running this script from developer console.

List<Account> lstAccount = [SELECT Id, Name, BillingStreet From Account LIMIT 1];
System.debug(lstAccount);

Debug Log:

23:47:04:012 USER_DEBUG [5]|DEBUG|(Account:{Id=0019000001nNLGEAA4, Name=Acct_nm001, BillingStreet=xyz Road, RecordTypeId=01290000000iaNiAAI})

I never queried for RecordtypeId in SOQL, but it is included in the list.

Although, if I run the query from the query editor, it is not showing RecordtypeId column.

Is this by design? Then why not Salesforce is not providing us audit fields in the first scenario.

Best Answer

In Apex Code, the SOQL engine automatically includes RecordTypeId and Id in any query result, even if you don't ask for it. There are a few other hidden parameters that appear in a query when presented to the SOQL engine.

For example, if you don't include a LIMIT statement, or set it to higher than 1+Limits.getLimitQueryRows()-Limits.getQueryRows(), the system automatically adds or changes the limit to LIMIT :1+Limits.getLimitQueryRows()-Limits.getQueryRows(). This is how the governor limits are enforced. The exception to this is for Batchable classes that use a Database.getQueryLocator statement, where the limit is set 50,000,001 rows. Also, when you don't specify ALL ROWS, Apex Code silently includes a filter IsDeleted = FALSE. In other words, whatever you put into your code isn't necessarily what will be executed by the SOQL engine.

Note that this means the following two queries are (approximately) the same in Apex Code:

Account[] accounts1 = [SELECT Id, RecordTypeId, Name, (SELECT Id, RecordType, Name FROM Contacts WHERE IsDeleted = FALSE) FROM Account WHERE IsDeleted = FALSE LIMIT :1+Limits.getLimitQueryRows()-Limits.getQueryRows()];
Account[] accounts2 = [SELECT Name, (SELECT Name FROM Contacts) FROM Account];

I don't know if there's a documented reason why RecordTypeId was specifically included, while audit fields were not, but I seem to recall that this behavior did not always exist in Apex Code, which suggests to me that some new SObject method was added that required this field to be present to function correctly. My best guess is that this field was needed for the Visualforce runtime engine to correctly render dynamic picklists on records.