API
@-Formulas
JavaScript
LotusScript
Reg Exp
Web Design
Notes Client
XPages
 
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.