Skip to content

Commit 9388e0a

Browse files
committed
add feature to ask user if not exist in insert data in scylla adaptor
1 parent 8d5ee6b commit 9388e0a

4 files changed

Lines changed: 89 additions & 4 deletions

File tree

archipy/adapters/scylladb/adapters.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,18 +382,23 @@ def drop_table(self, table: str) -> None:
382382
raise
383383

384384
@override
385-
def insert(self, table: str, data: dict[str, Any], ttl: int | None = None) -> None:
385+
def insert(self, table: str, data: dict[str, Any], ttl: int | None = None, if_not_exists: bool = False) -> None:
386386
"""Insert data into a table.
387387
388388
Args:
389389
table (str): The name of the table.
390390
data (dict[str, Any]): Key-value pairs representing column names and values.
391391
ttl (int | None): Time to live in seconds. If None, data persists indefinitely.
392+
if_not_exists (bool): If True, use lightweight transaction (INSERT ... IF NOT EXISTS).
393+
This prevents errors on duplicate primary keys but is slow
392394
"""
393395
columns = ", ".join(data.keys())
394396
placeholders = ", ".join(["%s" for _ in data.keys()])
395397
query = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
396398

399+
if if_not_exists:
400+
query += " IF NOT EXISTS"
401+
397402
if ttl is not None:
398403
query += f" USING TTL {ttl}"
399404

@@ -961,18 +966,29 @@ async def drop_table(self, table: str) -> None:
961966
raise
962967

963968
@override
964-
async def insert(self, table: str, data: dict[str, Any], ttl: int | None = None) -> None:
969+
async def insert(
970+
self,
971+
table: str,
972+
data: dict[str, Any],
973+
ttl: int | None = None,
974+
if_not_exists: bool = False,
975+
) -> None:
965976
"""Insert data into a table asynchronously.
966977
967978
Args:
968979
table (str): The name of the table.
969980
data (dict[str, Any]): Key-value pairs representing column names and values.
970981
ttl (int | None): Time to live in seconds. If None, data persists indefinitely.
982+
if_not_exists (bool): If True, use lightweight transaction (INSERT ... IF NOT EXISTS).
983+
This prevents errors on duplicate primary keys but is slow
971984
"""
972985
columns = ", ".join(data.keys())
973986
placeholders = ", ".join(["%s" for _ in data.keys()])
974987
query = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
975988

989+
if if_not_exists:
990+
query += " IF NOT EXISTS"
991+
976992
if ttl is not None:
977993
query += f" USING TTL {ttl}"
978994

archipy/adapters/scylladb/ports.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,15 @@ def drop_table(self, table: str) -> None:
129129
raise NotImplementedError
130130

131131
@abstractmethod
132-
def insert(self, table: str, data: dict[str, Any], ttl: int | None = None) -> None:
132+
def insert(self, table: str, data: dict[str, Any], ttl: int | None = None, if_not_exists: bool = False) -> None:
133133
"""Insert data into a table.
134134
135135
Args:
136136
table (str): The name of the table.
137137
data (dict[str, Any]): Key-value pairs representing column names and values.
138138
ttl (int | None): Time to live in seconds. If None, data persists indefinitely.
139+
if_not_exists (bool): If True, use lightweight transaction (INSERT ... IF NOT EXISTS).
140+
This prevents errors on duplicate primary keys but is slow
139141
140142
Raises:
141143
NotImplementedError: If not implemented by the subclass.
@@ -405,13 +407,21 @@ async def drop_table(self, table: str) -> None:
405407
raise NotImplementedError
406408

407409
@abstractmethod
408-
async def insert(self, table: str, data: dict[str, Any], ttl: int | None = None) -> None:
410+
async def insert(
411+
self,
412+
table: str,
413+
data: dict[str, Any],
414+
ttl: int | None = None,
415+
if_not_exists: bool = False,
416+
) -> None:
409417
"""Insert data into a table asynchronously.
410418
411419
Args:
412420
table (str): The name of the table.
413421
data (dict[str, Any]): Key-value pairs representing column names and values.
414422
ttl (int | None): Time to live in seconds. If None, data persists indefinitely.
423+
if_not_exists (bool): If True, use lightweight transaction (INSERT ... IF NOT EXISTS).
424+
This prevents errors on duplicate primary keys but is slow
415425
416426
Raises:
417427
NotImplementedError: If not implemented by the subclass.

features/scylladb_adapter.feature

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,25 @@ Feature: ScyllaDB Adapter
217217
Given a ScyllaDB adapter is configured
218218
When I get pool statistics
219219
Then the pool statistics should be returned
220+
221+
@needs-scylladb
222+
Scenario: Insert with If Not Exists
223+
Given a ScyllaDB adapter is configured
224+
And a keyspace "test_ks" with replication factor 1 exists
225+
And a table "inventory" with schema "CREATE TABLE IF NOT EXISTS inventory (id int PRIMARY KEY, item text, quantity int)"
226+
And data exists in table "inventory":
227+
| id | item | quantity |
228+
| 1 | Widget | 100 |
229+
When I insert data into table "inventory" with id 1, item "some_item", quantity 500
230+
Then the table "inventory" should contain 1 row
231+
232+
@needs-scylladb @async
233+
Scenario: Async Insert with If Not Exists
234+
Given an async ScyllaDB adapter is configured
235+
And an async keyspace "test_ks" with replication factor 1 exists
236+
And an async table "async_inventory" with schema "CREATE TABLE IF NOT EXISTS async_inventory (id int PRIMARY KEY, item text, quantity int)"
237+
And data exists in table "async_inventory":
238+
| id | item | quantity |
239+
| 1 | Widget | 100 |
240+
When I async insert data into table "async_inventory" with id 1, item "some_item", quantity 500
241+
Then the async table "async_inventory" should contain 1 row

features/steps/scylladb_adapter_steps.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,3 +991,40 @@ def step_verify_pool_stats(context: Context) -> None:
991991
assert isinstance(stats, dict), "Pool statistics should be a dictionary"
992992
assert "monitoring_enabled" in stats, "Pool statistics should contain 'monitoring_enabled' key"
993993
logger.info("Pool statistics verified")
994+
995+
996+
# If not Exist in insert steps
997+
998+
999+
@when('I insert data into table "{table}" with id {id:d}, item "{item}", quantity {quantity:d}')
1000+
def step_insert_with_if_not_exists(
1001+
context: Context, table: str, id: int, item: str, quantity: int,
1002+
) -> None:
1003+
"""Insert data in a table with if not exists.
1004+
1005+
Args:
1006+
context (Context): Behave context.
1007+
table (str): Table name.
1008+
id (int): User ID.
1009+
item (str): Item.
1010+
quantity (int): Quantity
1011+
"""
1012+
adapter = get_scylladb_adapter(context)
1013+
adapter.insert(table=table, data={"id": id, "item": item, "quantity": quantity}, if_not_exists=True)
1014+
logger.info("Inserted user data into '%s'", table)
1015+
1016+
1017+
@when('I async insert data into table "{table}" with id {id:d}, item "{item}", quantity {quantity:d}')
1018+
async def step_insert_with_if_not_exists(context: Context, table: str, id: int, item: str, quantity: int) -> None:
1019+
"""Insert data in a table with if not exists asynchronously.
1020+
1021+
Args:
1022+
context (Context): Behave context.
1023+
table (str): Table name.
1024+
id (int): User ID.
1025+
item (str): Item.
1026+
quantity (int): Quantity
1027+
"""
1028+
adapter = get_scylladb_adapter(context)
1029+
await adapter.insert(table=table, data={"id": id, "item": item, "quantity": quantity}, if_not_exists=True)
1030+
logger.info("Inserted user data into '%s'", table)

0 commit comments

Comments
 (0)