Database Schema
Primentra creates 36 SQL Server tables, in nine groups. Every write goes through a stored procedure — the API never sends raw SQL.
stg schema, one per staged entity.Core data — the EAV model
| Table | Purpose | Key columns |
|---|---|---|
Models | Top-level data domains | Id, Name, IsActive, SortOrder |
Entities | Entity types within a model | Id, ModelId, Name, TableName, RequiresApproval, SharedAcrossModels, AutoGenerateCode |
Attributes | Column definitions per entity | Id, EntityId, DisplayName, Name, DataType, DomainEntityId, IsRequired, DecimalPlaces, DateFormat |
EntityRows | One record per master data row | Id, EntityId, Code, Name, CreatedBy, ModifiedBy |
EntityValues | One value per attribute per row | EntityRowId, AttributeId, TextValue, IntValue, DecimalValue, DateTimeValue, DomainValue |
Attributes.DataType is limited by a CHECK constraint to Text, Int, Decimal, DateTime, Domain and Boolean.
Users and permissions
| Table | Purpose | Key columns |
|---|---|---|
Users | User accounts | Id, Email, DisplayName, PasswordHash, MustChangePassword, FailedLoginAttempts, IsActive, IsDeleted, UserType |
Roles | Permission roles | Id, Name, IsAdmin, CanApprove |
RoleMembers | User to role membership | RoleId, UserId |
Permissions | Per-role access | RoleId, Scope, TargetId, Level, CanCreate, CanRead, CanUpdate, CanDelete, IsModerator, IsExplicit |
ApiKeys | API key credentials for the REST API | Id, UserId, Name, KeyHash, KeyPrefix, ExpiresAt, LastUsedAt, CallCount |
Permissions.Scope is model, entity or attribute. Users.UserType is standard or api.
Modeling extras
| Table | Purpose | Key columns |
|---|---|---|
EntityDerivedColumns | Derived column definitions (path traversal) | Id, EntityId, DisplayName, AttributePath, SortOrder |
DateTimeFormats | The installation-wide date format library | Id, Name, Mask, SqlPattern, HasTime, IsSystem |
Business rules
| Table | Purpose | Key columns |
|---|---|---|
BusinessRules | Rule definitions per entity | Id, EntityId, Name, Template, Definition, RuleKind, Severity, Message, IsActive |
RowValidationFailures | One row per failing rule per data row | Id, EntityRowId, BusinessRuleId, Severity, Message, AttributeName |
RuleKind is validation or default. An absent RowValidationFailures row means the record passes.
Approval workflow
| Table | Purpose | Key columns |
|---|---|---|
ApprovalRequests | Approval requests | Id, EntityId, SubmittedByUserId, Status, SubmitterNote, Version, PreviousRequestId |
ApprovalRows | Row snapshots inside a request | Id, ApprovalRequestId, EntityRowId, Operation, RowStatus, RowCode, SnapshotData |
ApprovalReviews | Reviewer decisions | Id, ApprovalRequestId, ReviewedByUserId, Decision, Comment, FlaggedRowIds |
EntityApprovers | Per-entity approver assignments | EntityId, UserId, ReceivesEmail |
Integration, staging and scheduling
| Table | Purpose | Key columns |
|---|---|---|
IntegrationViews | SQL view definitions for external systems | Id, Name, EntityId, ViewType, ExcludedColumns, ColumnSnapshot |
StagingImport | Temporary rows for the model import wizard | SessionId, EntityName, ModelName, RowData, Status |
StagingConfigurations | Staging setup, one row per staged entity | Id, EntityId, IsEnabled, TableName, DefaultImportAction, DefaultMergeMode, SentinelText |
StagingFieldRules | Per-attribute merge and error rules | Id, EntityId, AttributeId, MergeMode, OnValidationError |
StagingBatches | One row per batch run | Id, EntityId, BatchTag, Status, TotalRows, SuccessRows, ErrorRows, HeartbeatAt |
StagingErrorDetails | Per-row error detail for a batch | Id, BatchId, StagingRowId, AttributeName, ErrorCode, ErrorMessage |
ImportDomainFKStaging | Unresolved domain references during model import | Id, AttributeId, DomainEntityName, DomainEntityModelName |
SchedulerConfigurations | Per-entity scheduling settings | Id, EntityId, ProcessingMode, ScheduleType, IntervalMinutes, RunTimes, IsRunning, NextScheduledRun |
SchedulerLog | Every dispatcher action per entity | Id, EntityId, EventTime, EventType, TriggerSource, RowsQueued, BatchId |
Logs
| Table | Purpose | Key columns |
|---|---|---|
AuditLog | Business event history (append-only) | EntityId, EntityName, ModelName, Action, RecordCount, UserId, Comment, CreatedAt |
ErrorLog | Server-side technical errors (append-only) | Timestamp, Level, Method, Path, StatusCode, Message, Stack |
Settings
| Table | Purpose | Key columns |
|---|---|---|
AppSettings | Application-wide configuration | SettingKey, SettingValue |
UserSettings | Per-user key-value preferences | UserId, SettingKey, SettingValue |
UserFavorites | Per-user entity favorites | UserId, EntityId |
UserHiddenEntities | Per-user hidden sidebar entities | UserId, EntityId |
System
| Table | Purpose | Key columns |
|---|---|---|
SystemConfig | Internal timestamps, such as the last cleanup run | ConfigKey, ConfigValue (DATETIME2), Metadata |
SchemaVersion | The deployed schema version | Version, UpdatedAt |
ErrorCodes | Reference table for every error code | ErrorCode, CodeType, Name, Category, Description, UsedIn |
ErrorCodes.CodeType is Throw for stored procedure THROW numbers, or StagingBitmask for the bitmask values on a staging row.
Most tables carry CreatedAt and ModifiedAt with a GETUTCDATE() default. Append-only tables and simple link tables — AuditLog, ErrorLog, UserFavorites, UserHiddenEntities, EntityApprovers, ApprovalRows, EntityDerivedColumns — carry only a creation timestamp.
Foreign key cascade behavior
Stored procedures delete in the correct dependency order. The cascades below are defined at the database level as a safety net.
| Relationship | On delete |
|---|---|
Entity → Attributes | CASCADE |
Entity → IntegrationViews | CASCADE |
Entity → EntityApprovers | CASCADE |
Entity → EntityDerivedColumns | CASCADE |
Entity → BusinessRules | CASCADE |
Entity → StagingConfigurations and StagingFieldRules | CASCADE |
Entity → UserFavorites and UserHiddenEntities | CASCADE |
Attribute → EntityValues | CASCADE |
Attribute → ImportDomainFKStaging | CASCADE |
Row → EntityValues and RowValidationFailures | CASCADE |
Role → Permissions and RoleMembers | CASCADE |
User → RoleMembers, UserSettings, UserFavorites, UserHiddenEntities, EntityApprovers | CASCADE |
Approval request → ApprovalRows and ApprovalReviews | CASCADE |
Staging batch → StagingErrorDetails | CASCADE |
Attribute → StagingFieldRules | NO ACTION — usp_Attribute_Delete clears the rules first |
Business rule → RowValidationFailures | NO ACTION — usp_BusinessRule_Delete clears the failures first |
Entity → ApprovalRequests, StagingBatches, SchedulerConfigurations | NO ACTION — the delete is blocked until history is cleared |
EntityRows, Entities and Models have no cascade delete. SQL Server rejects a configuration where two cascade paths converge on the same table, so the stored procedures handle those in order instead.
Do not delete directly from the database
You can delete rows in SQL Server Management Studio, but do not do it in production:
- No audit entry is written. The audit log only records actions that pass through the application. A direct
DELETE FROM Entitiesleaves no trace of who deleted what. - Permissions become orphaned.
Permissions.TargetIdhas no foreign key, so deleting an entity leaves permission rows pointing at nothing. They accumulate silently. - Staging and approval history blocks the delete.
StagingBatchesandApprovalRequestsreferenceEntitieswith NO ACTION, so a manual delete fails with a constraint error rather than doing anything useful.
If you must remove data at database level, call usp_Entity_Delete or usp_Model_Delete. They clean up in the correct order, handle permissions, and write an audit entry, all in one transaction.
Related
- Three-Tier Design & Security — why every write goes through a stored procedure
- DBA Reference — the T-SQL for clearing a stuck approval lock
- Performance — the indexes these tables carry, and what they cost
- Attributes & Data Types — the six data types the schema allows
- Audit Log — reading the audit trail from the UI