Functions/GenXdev.Data.KeyValueStore/Sync-KeyValueStore.ps1

################################################################################
<#
.SYNOPSIS
Synchronizes local and OneDrive key-value store databases.
 
.DESCRIPTION
Performs two-way synchronization between local and OneDrive shadow databases using
a last-modified timestamp strategy. Records are merged based on their last
modification time, with newer versions taking precedence.
 
.PARAMETER SynchronizationKey
Identifies the synchronization scope for the operation. Using "Local" will skip
synchronization as it indicates local-only records.
 
.EXAMPLE
# Synchronize using default local scope
Sync-KeyValueStore
 
.EXAMPLE
# Synchronize specific scope
Sync-KeyValueStore -SynchronizationKey "UserSettings"
#>

function Sync-KeyValueStore {

    [CmdletBinding()]
    param(
        #######################################################################
        [Parameter(
            Mandatory = $false,
            Position = 0,
            HelpMessage = "Key to identify synchronization scope"
        )]
        [string]$SynchronizationKey = "Local"
        #######################################################################
    )

    begin {

        Write-Verbose "Starting key-value store sync with key: $SynchronizationKey"
    }

    process {

        # skip synchronization for local-only records
        if ($SynchronizationKey -eq "Local") {

            Write-Verbose "Skipping sync for local-only key"
            return
        }

        # resolve database file paths for local and shadow copies
        $localDb = Expand-Path "$PSScriptRoot\..\..\..\..\GenXdev.Local\KeyValueStores.sqllite.db"
        $shadowDb = Expand-Path "~\OneDrive\GenXdev.PowerShell.SyncObjects\KeyValueStores.sqllite.db"

        Write-Verbose "Local DB: $localDb"
        Write-Verbose "Shadow DB: $shadowDb"

        # ensure database files exist by initializing if needed
        if (-not ([System.IO.File]::Exists($localDb) -and
                [System.IO.File]::Exists($shadowDb))) {

            Write-Verbose "Initializing missing database files"
            Initialize-KeyValueStores
        }

        # sql query to perform bidirectional sync based on last modified timestamp
        $syncQuery = @"
ATTACH DATABASE @shadowDb AS shadow;
 
-- sync from shadow to local
INSERT OR REPLACE INTO main.KeyValueStore
SELECT * FROM shadow.KeyValueStore s
WHERE s.synchronizationKey = @syncKey
AND (
    NOT EXISTS (
        SELECT 1 FROM main.KeyValueStore m
        WHERE m.synchronizationKey = s.synchronizationKey
        AND m.storeName = s.storeName
        AND m.keyName = s.keyName
        AND m.lastModified >= s.lastModified
    )
);
 
-- sync from local to shadow
INSERT OR REPLACE INTO shadow.KeyValueStore
SELECT * FROM main.KeyValueStore m
WHERE m.synchronizationKey = @syncKey
AND (
    NOT EXISTS (
        SELECT 1 FROM shadow.KeyValueStore s
        WHERE s.synchronizationKey = m.synchronizationKey
        AND s.storeName = m.storeName
        AND s.keyName = m.keyName
        AND s.lastModified > m.lastModified
    )
);
"@


        # prepare parameters for sql query execution
        $params = @{
            'shadowDb' = $shadowDb
            'syncKey'  = $SynchronizationKey
        }

        Write-Verbose "Executing sync query with parameters"
        Invoke-SQLiteQuery -DatabaseFilePath $localDb -Queries $syncQuery -SqlParameters $params
    }

    end {

        Write-Verbose "Sync operation completed"
    }
}
################################################################################