Copy Database
In a recent project, we were copying databases from one server to another. There were many databases to do, so we decided to write an agent to do the copy. But we wanted copies, not replicas. We found out that the NotesDatabase.CreateCopy method does not copy documents (it only copies the design). We could have used that method, then got a handle to all the documents in the source database and copied them over to the destination database. But instead we used another approach. We decided to create a new replica of the database, then change its replica ID to something unique. This would effectively create a copy of the database. Earlier in part 1 of this tip, we talked about the Change Replica ID function that is needed. Here in part 2 of this tip, we talk about the main structure of the copy database code.
Before I get into the code, let me point out some things:
First, note that if this script is running on a server, it will need to run unrestricted. That's because the Change Replica ID function uses Notes API calls.
Second, note that if you (or the agent signer) will need to have access to all the documents in the source database to perform a true copy. This code does not bypass any reader field security. The only way to get around that is if you are an administrator on your server and are running under full administrator rights.
Finally, note that we will also need the Random File Name function. If you have multiple script libraries (like we do) then the Create Copy script library should include the Random File Name and Change Replica ID script libraries.
Function CreateCopy(sourceDb As NotesDatabase, destServer As String, destPath As String) As NotesDatabase
On Error Goto BubbleError
' Create a copy of the passed-in database by creating a new replica, then changing
' the replica ID of the new replica to something unique.
Dim tempDb As New NotesDatabase("", "")
Dim session As NotesSession
Dim destDb As NotesDatabase
Dim dataDir As String
Dim tempFileName As String
' Make sure the destination database does not already exist
Set session = sourceDb.Parent
On Error Resume Next
Set destDb = session.GetDatabase(destServer, destPath, False)
If Not destDb.IsOpen Then Call destDb.Open("", "")
If Not destDb.IsOpen Then Set destDb = Nothing
On Error Goto 0
If Err <> 0 Then
Err = 0
Set destDb = Nothing
End If
We start out by making sure the destination database does not already exist. If it does already exist, this function will not replace the database. A possible enhancement to this function would be an additional boolean parameter to indicate if an overwrite is allowed or not. If the database does not exist, then the variable destDb is set to Nothing.
' If the destination database does not exist, then create the replica
If destDb Is Nothing Then
' Get the full path to a random file name in the user's data directory
dataDir = session.GetEnvironmentString("Directory", True)
tempFileName = RandomFileName(dataDir, "nsf")
' If the RandomFileName function returned an empty string, just specify some file name
If tempFileName = "" Then tempFileName = "temp.nsf"
' Pull out the directory info so only the file name remains
If Instr(tempFileName, dataDir) <> 0 Then tempFileName = Strright(tempFileName, dataDir)
If Left(tempFileName, 1) = "\" Or Left(tempFileName, 1) = "/" Then tempFileName = Mid(tempFileName, 2)
' Create an empty database from our random file name
Call tempDb.Create("", tempFileName, True)
The next part of the code creates a brand new local database. This will be used in the call to the ChangeReplicaID function. This assures that the replica ID, after we're done, will be a unique value. The brand new local database is only used temporarily - it will be deleted at the bottom of this function. Note that we use the RandomFileName function to make sure the local file we're creating doesn't already exist. This is more reliable than hard-coding a file name. Also note that the RandomFileName function returns the full path to the file name (including the directory). For the Create method, we don't want the full path - we just want the relative path.
' Create a replica of the source database to the destination server
If Not sourceDb.IsOpen Then Call sourceDb.Open("", "")
Set destDb = sourceDb.CreateReplica(destServer, destPath)
If Not destDb.IsOpen Then Call destDb.Open("", "")
If Not ChangeReplicaID(tempDb, destDb) Then ' If the destination db's replica ID was not changed
Set destDb = Nothing
End If
Call tempDb.Remove
End If
Set CreateCopy = destDb
Exit Function
BubbleError:
Error Err, Error$ & Chr$(10) & " in procedure " & Getthreadinfo(1) & ", line " & Cstr(Erl)
End Function
The replica of the source database is then created. This copies over all design elements and documents (unless there's reader security and the agent signer doesn't have access, as mentioned above). After the replica has been created, the replica ID of the new database is changed to match the replica ID of the temporary database. The temporary database is then removed, which leaves the new database as having its own unique replica ID. The function then returns a handle to the new database.