So, let's look at assembly, or CLR, objects. There are four principal types of CLR object - Aggregate Functions, Stored Procedures, Table Functions and Scalar Functions. Of course, there are also Assembly Types - but these were covered previously in the post on types, and are not objects, as such.
Assembly modules, as they are called, are all covered by one metadata view - [sys].[assembly_modules]
. Let's look at what we get:
object_id | This is the object_id of the object to which the assembly module relates. |
---|---|
assembly_id | This is the ID of the assembly in [sys].[assemblies] that contains the class and, if applicable, method that defines the assembly module. |
assembly_class | This is the name of the class that contains the method that defines the module, or the name of the class that implements the module in the case of aggregate functions. |
assembly_method | This is the name of the method within the class that implements the module (NULL for aggregate functions). Note that the method must be marked with an attribute which specifies that the method is a SQL method - these attributes are called SqlFunctionAttribute , SqlProcedureAttribute and SqlTriggerAttribute for functions, procedures and triggers respectively. |
null_on_null_input | This is 1 if the module was declared to produce NULL on any NULL input, by specifying WITH RETURNS NULL ON NULL INPUT . Note that this only applies to functions. |
execute_as_principal_id | This is the ID of the database principal specified in the WITH EXECUTE AS clause. If there is no WITH EXECUTE AS clause, then this will be NULL. Explicitly specifying WITH EXECUTE AS CALLER will also result in this column being NULL. Specifying EXECUTE AS OWNER will result in the value being -2. For EXECUTE AS [database_principal_name] or EXECUTE AS SELF , this will be the ID of the relevant database principal in [sys].[database_principals] . |
So, we get a bit of information there. Certainly enough to re-create the modules, given the source assemblies. However, as virtually all of the 'juicy stuff' is defined within the assembly itself, there's not much to look at.
Let's look at what we're told about the assemblies themselves then, by the metadata view [sys].[assemblies]
:
name | This is the name of the assembly within the database, which is not necessarily the same as the assembly's natural CLR name. |
---|---|
principal_id | This is the ID of the database principal in [sys].[database_principals] that owns the assembly. |
assembly_id | This is the ID of the assembly within the database. |
clr_name | This is the fully qualified, version-number-and-all version of the assembly name. This name uniquely identifies not only the assembly, but also the version of the assembly. |
permission_set | This is the permission set that the assembly has - 1 for SAFE , 2 for EXTERNAL_ACCESS and 3 for UNSAFE . Permission sets are reasonably complex - and would warrant a post on their own. Basically speaking, however, the SAFE permission set restricts what the assembly can do, as well as limiting it's access to resources within the SQL Server instance. The EXTERNAL_ACCESS permission set still restricts what the assembly can do, but allows it access to resources outside of SQL Server (for example, the event log). Access to external resources is usually performed via the SQL Server service account. The UNSAFE permission set does not restrict what the assembly can do at all, and you have to be very careful when writing UNSAFE . Under this permission set you can do things like start threads, etc. |
permission_set_desc | This is the description that relates to the value in the permission_set column. |
is_visible | This is 1 if the assembly is 'visible' - i.e. it can be used to create assembly modules. An assembly that is not visible is only used by other assemblies in the database (containing common core functionality, for example). |
create_date | This is the date on which the assembly was created in the database. |
modify_date | This is the date on which the assembly was last modified using ALTER ASSEMBLY . |
is_user_defined | This is 1 if the assembly is a user assembly. System assemblies are those that ship with the system (for example Microsoft.SqlServer.Types which defines the geometry , geography and hierarchyid data types). |
That tells us quite a lot about the assemblies present in the database, but doesn't actually give us any idea of what the actual assembly DLL would contain, binary-wise. Enter [sys].[assembly_files]
:
assembly_id | This is the ID of the assembly in [sys].[assemblies] to which the file belongs. |
---|---|
name | This is the name of the assembly file. For the main assembly DLL file, this will be the name of the assembly. |
file_id | This is the ID of the file within the assembly. ID 1 is always the assembly DLL, i.e. the content of the file as it would be on-disk. Other files can often be present when deploying from Visual Studio, which will include the source files as well as the assembly config files. |
content | This is the varbinary(MAX) content of the file - again, exactly as it would appear on disk. |
That's better, that gives us the ability to actually re-create the DLL on-disk. SQL Everywhere, our SQL Server IDE, routinely uses the information contained in [sys].[assembly_files]
to re-create the assembly on disk, and inspect it using reflection in order to provide intellisense for assembly classes, methods and type methods.
Next time, we'll be looking at some of the metadata available that gives us information about database principals, server principals and roles.