DEV Community

Marcel
Marcel

Posted on

# Database Architecture: Implementing the Temporary File Pattern for Deletions in C

When building a custom file-based database from scratch in raw C, you quickly hit a low-level hardware constraint: operating systems and storage controller drivers cannot natively delete a single line of text from the middle of a physical file on a disk drive.

To create a complete CRUD (Create, Read, Update, Delete) system for my contact book application, I had to implement a classic systems programming technique: the Temporary File Architectural Pattern.

This guide explains how database engines manipulate raw disk files to perform safe, non-destructive row deletions without corrupting adjacent data blocks.

The Architectural Limitation of Disk Storage

Unlike volatile RAM, where bytes can be modified dynamically at any specific memory address offset, data written to disk sectors is contiguous. If you open a text file stream in standard modify mode, you can overwrite existing bytes, but you cannot "squeeze" a line out and expect the remaining data to automatically shift upward to close the gap.

To execute a deletion, we have to fake it using a dual-stream filtering pipeline.

The Strategy: Read, Filter, and Rebuild

Instead of attempting to alter the original database file (contacts.txt) directly, the architecture relies on creating an isolated clone (temp.txt) that only inherits the data we want to keep.

The execution steps are straightforward:

  1. Open the primary database file in Read ("r") mode.
  2. Open a temporary storage file in Write ("w") mode.
  3. Stream the primary file line-by-line into a stack-allocated character buffer.
  4. Use a pattern matcher to inspect the string. If it matches the deletion query, the CPU drops the buffer. If it doesn't match, the stream writes the buffer directly into the temporary file.
  5. Close both stream pipelines, wipe out the old file, and rename the temporary clone to become the new primary database.

Technical Implementation

Here is the robust abstraction layer handling the file stream operations safely:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int deleteContactRecord(const char *target_name) {
    // Open dual pipelines to the storage controller
    FILE *source_db = fopen("contacts.txt", "r");
    FILE *temp_db = fopen("temp.txt", "w");

    if (source_db == NULL || temp_db == NULL) {
        printf("System Error: Unable to initialize file database streams.\n");
        return 1;
    }

    char line_buffer[200];
    int deletion_flag = 0;

    // Stream lines into RAM row-by-row
    while (fgets(line_buffer, sizeof(line_buffer), source_db) != NULL) {
        // If the deletion query string is NOT found in this row, preserve it
        if (strstr(line_buffer, target_name) == NULL) {
            fprintf(temp_db, "%s", line_buffer);
        } else {
            deletion_flag = 1; // Match found; skip writing to filter it out
        }
    }

    // Force system flush and close connections
    fclose(source_db);
    fclose(temp_db);

    // Commit file table updates permanently
    if (deletion_flag == 1) {
        remove("contacts.txt");         // Erase the old database state
        rename("temp.txt", "contacts.txt"); // Promote temp file to primary
        printf("Record mapping to '%s' successfully purged.\n", target_name);
    } else {
        remove("temp.txt"); // Dissolve the empty utility file
        printf("Record not found. State preserved.\n");
    }

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Why This Matters for Systems Engineering

This exact "read-filter-write" transaction pattern is the basics of database persistence.

With the CRUD engine of this app complete, the next architectural milestone is shifting from sequential file parsing to O(1) memory lookups by designing a custom Hash Table data structure from scratch.

c #programming #computerengineering #backend #database

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.