hirejared-kw-db/docs/project-docs/03-scripts/table-admin/create-tables.md
2024-11-19 07:52:08 -05:00

7.8 KiB

Table Creation Scripts Documentation

Overview

These scripts handle the creation of all database tables for the keyword management system. Each table has its own creation script to maintain modularity and allow for independent testing.

Script Locations

/bin/table-admin/create-tables/
├── create-keywords-table.sh
├── create-campaigns-table.sh
├── create-landing-pages-table.sh
├── create-ad-groups-table.sh
├── create-keyword-assignments-table.sh
└── create-import-history-table.sh

Keywords Table Script

Location

/bin/table-admin/create-tables/create-keywords-table.sh

SQL Command

CREATE TABLE Keywords (
    keyword_id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    keyword VARCHAR(255) NOT NULL,
    
    -- GKWP Data
    gkwp_search_volume INTEGER,
    gkwp_competition_index INTEGER,
    gkwp_cpc_min DECIMAL(10,2),
    gkwp_cpc_max DECIMAL(10,2),
    gkwp_last_updated TIMESTAMP,
    
    -- SEMRush Data
    semrush_search_volume INTEGER,
    semrush_difficulty INTEGER,
    semrush_cpc DECIMAL(10,2),
    semrush_intent VARCHAR(50),
    semrush_last_updated TIMESTAMP,
    
    -- Metadata
    first_imported TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Case-insensitive unique constraint
CREATE UNIQUE INDEX unique_lowercase_keyword 
ON Keywords (LOWER(keyword));

Implementation

#!/bin/bash

# Load configuration
source ../../../.database

# Set error handling
set -e

# Setup logging
LOG_DIR="../../../logs"
LOG_FILE="${LOG_DIR}/$(date +%Y-%m-%d)-create-keywords-table.log"

mkdir -p "$LOG_DIR"

# Function to log messages
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Check if table exists
TABLE_EXISTS=$(psql -h "${DB_HOST}" \
                   -p "${DB_PORT}" \
                   -U "${DB_USER}" \
                   -d "${DB_NAME}" \
                   -tAc "SELECT to_regclass('public.keywords');")

if [ "$TABLE_EXISTS" == "keywords" ]; then
    log_message "Error: Keywords table already exists"
    exit 1
fi

# Create table
log_message "Creating Keywords table..."

psql -h "${DB_HOST}" \
     -p "${DB_PORT}" \
     -U "${DB_USER}" \
     -d "${DB_NAME}" \
     -f "sql/create-keywords-table.sql"

# Verify creation
if [ $? -eq 0 ]; then
    log_message "Keywords table created successfully"
    
    # Log table structure
    log_message "Table structure:"
    psql -h "${DB_HOST}" \
         -p "${DB_PORT}" \
         -U "${DB_USER}" \
         -d "${DB_NAME}" \
         -c "\d+ keywords" >> "$LOG_FILE"
else
    log_message "Error: Failed to create Keywords table"
    exit 1
fi

Campaigns Table Script

Location

/bin/table-admin/create-tables/create-campaigns-table.sh

SQL Command

CREATE TABLE Campaigns (
    campaign_id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    campaign_name VARCHAR(255) NOT NULL,
    status VARCHAR(50) NOT NULL DEFAULT 'active',
    start_date DATE,
    end_date DATE,
    budget_daily DECIMAL(10,2),
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_campaign_name UNIQUE(campaign_name)
);

Landing Pages Table Script

Location

/bin/table-admin/create-tables/create-landing-pages-table.sh

SQL Command

CREATE TABLE Landing_Pages (
    landing_page_id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    url VARCHAR(2048) NOT NULL,
    page_name VARCHAR(255) NOT NULL,
    status VARCHAR(50) NOT NULL DEFAULT 'active',
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT unique_url UNIQUE(url),
    CONSTRAINT unique_page_name UNIQUE(page_name)
);

Ad Groups Table Script

Location

/bin/table-admin/create-tables/create-ad-groups-table.sh

SQL Command

CREATE TABLE Ad_Groups (
    ad_group_id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    campaign_id INTEGER NOT NULL,
    landing_page_id INTEGER NOT NULL,
    ad_group_name VARCHAR(255) NOT NULL,
    status VARCHAR(50) NOT NULL DEFAULT 'active',
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_campaign 
        FOREIGN KEY (campaign_id) 
        REFERENCES Campaigns(campaign_id),
    CONSTRAINT fk_landing_page 
        FOREIGN KEY (landing_page_id) 
        REFERENCES Landing_Pages(landing_page_id),
    CONSTRAINT unique_ad_group_name_per_campaign 
        UNIQUE(campaign_id, ad_group_name)
);

Keyword Assignments Table Script

Location

/bin/table-admin/create-tables/create-keyword-assignments-table.sh

SQL Command

CREATE TABLE Keyword_Ad_Group_Assignments (
    keyword_id INTEGER NOT NULL,
    ad_group_id INTEGER NOT NULL,
    status VARCHAR(50) NOT NULL DEFAULT 'active',
    assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    notes TEXT,
    PRIMARY KEY (keyword_id, ad_group_id),
    FOREIGN KEY (keyword_id) REFERENCES Keywords(keyword_id),
    FOREIGN KEY (ad_group_id) REFERENCES Ad_Groups(ad_group_id)
);

Import History Table Script

Location

/bin/table-admin/create-tables/create-import-history-table.sh

SQL Command

CREATE TABLE Import_History (
    import_id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    import_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    source_type VARCHAR(50) NOT NULL,
    filename VARCHAR(255),
    original_seed_keyword VARCHAR(255),
    assigned_ad_group_id INTEGER,
    row_count INTEGER,
    success_count INTEGER,
    error_count INTEGER,
    import_status VARCHAR(50) NOT NULL,
    error_details TEXT,
    notes TEXT,
    FOREIGN KEY (assigned_ad_group_id) REFERENCES Ad_Groups(ad_group_id)
);

Testing

Located in /tests/table-admin/create-tables/

Each table has its own test file:

/tests/table-admin/create-tables/
├── create-keywords-table.test.sh
├── create-campaigns-table.test.sh
├── create-landing-pages-table.test.sh
├── create-ad-groups-table.test.sh
├── create-keyword-assignments-table.test.sh
└── create-import-history-table.test.sh

Example test for Keywords table:

#!/bin/bash

source ../../utils/test-framework.sh

test_keywords_table_creation() {
    # Ensure table doesn't exist
    psql -d "${DB_NAME}" -c "DROP TABLE IF EXISTS Keywords CASCADE;"
    
    # Create table
    ../bin/table-admin/create-tables/create-keywords-table.sh
    
    # Verify table exists
    exists_check=$(psql -tAc "SELECT to_regclass('public.keywords');" "${DB_NAME}")
    assert_equals "$exists_check" "keywords" "Keywords table should exist"
    
    # Verify columns
    columns=$(psql -tAc "\d keywords" "${DB_NAME}")
    assert_contains "$columns" "keyword_id" "Should have keyword_id column"
    assert_contains "$columns" "keyword" "Should have keyword column"
    
    # Test unique constraint
    psql -d "${DB_NAME}" -c "INSERT INTO Keywords (keyword) VALUES ('Test');"
    error_check=$(psql -d "${DB_NAME}" -c "INSERT INTO Keywords (keyword) VALUES ('TEST');" 2>&1)
    assert_contains "$error_check" "duplicate key value" "Should enforce case-insensitive uniqueness"
}

run_test_suite

Notes

  • Tables must be created in the correct order due to foreign key constraints
  • Each script includes proper error handling and logging
  • Case-insensitive keyword storage is enforced
  • All timestamp fields use timezone-free TIMESTAMP type
  • Status fields use predefined values
  • Each table has appropriate indexes for performance