Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions jitpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
jdk:
- openjdk11
install:
- mvn clean install -DskipTests
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.tidesdb</groupId>
<artifactId>tidesdb-java</artifactId>
<version>0.4.0</version>
<version>0.5.0</version>
<packaging>jar</packaging>

<name>TidesDB Java</name>
Expand Down
32 changes: 32 additions & 0 deletions src/main/c/com_tidesdb_TidesDB.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,38 @@ JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeRenameColumnFamily(JNIEnv
}
}

JNIEXPORT void JNICALL Java_com_tidesdb_TidesDB_nativeCloneColumnFamily(JNIEnv *env, jclass cls,
jlong handle,
jstring sourceName,
jstring destName)
{
tidesdb_t *db = (tidesdb_t *)(uintptr_t)handle;
const char *srcCfName = (*env)->GetStringUTFChars(env, sourceName, NULL);
if (srcCfName == NULL)
{
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get source column family name");
return;
}

const char *dstCfName = (*env)->GetStringUTFChars(env, destName, NULL);
if (dstCfName == NULL)
{
(*env)->ReleaseStringUTFChars(env, sourceName, srcCfName);
throwTidesDBException(env, TDB_ERR_MEMORY, "Failed to get destination column family name");
return;
}

int result = tidesdb_clone_column_family(db, srcCfName, dstCfName);

(*env)->ReleaseStringUTFChars(env, sourceName, srcCfName);
(*env)->ReleaseStringUTFChars(env, destName, dstCfName);

if (result != TDB_SUCCESS)
{
throwTidesDBException(env, result, getErrorMessage(result));
}
}

JNIEXPORT jobject JNICALL Java_com_tidesdb_ColumnFamily_nativeGetStats(JNIEnv *env, jclass cls,
jlong handle)
{
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/tidesdb/TidesDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,26 @@ public void renameColumnFamily(String oldName, String newName) throws TidesDBExc
nativeRenameColumnFamily(nativeHandle, oldName, newName);
}

/**
* Creates a complete copy of an existing column family with a new name.
* The clone contains all the data from the source at the time of cloning.
* The clone is completely independent - modifications to one do not affect the other.
*
* @param sourceName the source column family name
* @param destName the destination column family name
* @throws TidesDBException if the clone fails
*/
public void cloneColumnFamily(String sourceName, String destName) throws TidesDBException {
checkNotClosed();
if (sourceName == null || sourceName.isEmpty()) {
throw new IllegalArgumentException("Source column family name cannot be null or empty");
}
if (destName == null || destName.isEmpty()) {
throw new IllegalArgumentException("Destination column family name cannot be null or empty");
}
nativeCloneColumnFamily(nativeHandle, sourceName, destName);
}

private void checkNotClosed() {
if (closed) {
throw new IllegalStateException("TidesDB instance is closed");
Expand Down Expand Up @@ -288,4 +308,6 @@ private static native void nativeCreateColumnFamily(long handle, String name,
private static native void nativeBackup(long handle, String dir) throws TidesDBException;

private static native void nativeRenameColumnFamily(long handle, String oldName, String newName) throws TidesDBException;

private static native void nativeCloneColumnFamily(long handle, String sourceName, String destName) throws TidesDBException;
}
58 changes: 58 additions & 0 deletions src/test/java/com/tidesdb/TidesDBTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,64 @@ void testBtreeIterator() throws TidesDBException {

@Test
@Order(15)
void testCloneColumnFamily() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb15").toString())
.numFlushThreads(2)
.numCompactionThreads(2)
.logLevel(LogLevel.INFO)
.blockCacheSize(64 * 1024 * 1024)
.maxOpenSSTables(256)
.build();

try (TidesDB db = TidesDB.open(config)) {
ColumnFamilyConfig cfConfig = ColumnFamilyConfig.defaultConfig();
db.createColumnFamily("source_cf", cfConfig);

ColumnFamily sourceCf = db.getColumnFamily("source_cf");

// Insert data into source
try (Transaction txn = db.beginTransaction()) {
for (int i = 0; i < 10; i++) {
txn.put(sourceCf, ("key" + i).getBytes(), ("value" + i).getBytes());
}
txn.commit();
}

// Clone the column family
db.cloneColumnFamily("source_cf", "cloned_cf");

// Verify clone exists
ColumnFamily clonedCf = db.getColumnFamily("cloned_cf");
assertNotNull(clonedCf);
assertEquals("cloned_cf", clonedCf.getName());

// Verify both column families are listed
String[] families = db.listColumnFamilies();
assertTrue(families.length >= 2);

// Verify data exists in clone
try (Transaction txn = db.beginTransaction()) {
for (int i = 0; i < 10; i++) {
byte[] result = txn.get(clonedCf, ("key" + i).getBytes());
assertNotNull(result);
assertArrayEquals(("value" + i).getBytes(), result);
}
}

// Verify independence: insert into clone, should not appear in source
try (Transaction txn = db.beginTransaction()) {
txn.put(clonedCf, "clone_only_key".getBytes(), "clone_only_value".getBytes());
txn.commit();
}

try (Transaction txn = db.beginTransaction()) {
assertThrows(TidesDBException.class, () -> txn.get(sourceCf, "clone_only_key".getBytes()));
}
}
}

@Test
@Order(16)
void testTransactionPutGetDeleteBadKey() throws TidesDBException {
Config config = Config.builder(tempDir.resolve("testdb3").toString())
.numFlushThreads(2)
Expand Down
Loading