@@ -158,6 +158,9 @@ def __init__(self, ffi_result: _FfiQueryResult):
158158 self ._column_names = ffi_result .column_names ()
159159 self ._rows = ffi_result .get_all_rows ()
160160 self ._position = 0
161+ self ._compiling_time = ffi_result .get_compiling_time ()
162+ self ._execution_time = ffi_result .get_execution_time ()
163+ self ._column_data_types = ffi_result .get_column_data_types ()
161164
162165 @property
163166 def column_names (self ) -> list [str ]:
@@ -169,6 +172,21 @@ def num_rows(self) -> int:
169172 """Number of rows."""
170173 return len (self ._rows )
171174
175+ @property
176+ def compiling_time (self ) -> float :
177+ """Time spent translating Cypher to SQL (milliseconds)."""
178+ return self ._compiling_time
179+
180+ @property
181+ def execution_time (self ) -> float :
182+ """Time spent executing the SQL query (milliseconds)."""
183+ return self ._execution_time
184+
185+ @property
186+ def column_data_types (self ) -> list [str ]:
187+ """Inferred column data types (e.g., ``["String", "Int64", "Date"]``)."""
188+ return list (self ._column_data_types )
189+
172190 def has_next (self ) -> bool :
173191 """Return True if there are more rows (Kuzu-compatible cursor)."""
174192 return self ._position < len (self ._rows )
@@ -472,10 +490,79 @@ def store_subgraph(self, graph: GraphResult) -> StoreStats:
472490 edges_stored = ffi_stats .edges_stored ,
473491 )
474492
493+ # -- Write operations --
494+
495+ def create_node (self , label : str , properties : dict ) -> str :
496+ """Create a node with the given label and properties.
497+
498+ Returns the node ID (caller-provided or auto-generated UUID).
499+
500+ >>> node_id = conn.create_node("User", {"user_id": "u1", "name": "Alice", "age": 30})
501+ """
502+ return self ._ffi .create_node (label , _python_to_ffi_props (properties ))
503+
504+ def create_edge (
505+ self , edge_type : str , from_id : str , to_id : str , properties : dict | None = None
506+ ) -> None :
507+ """Create an edge between two nodes.
508+
509+ >>> conn.create_edge("FOLLOWS", "u1", "u2", {"follow_date": "2024-01-15"})
510+ """
511+ self ._ffi .create_edge (edge_type , from_id , to_id , _python_to_ffi_props (properties or {}))
512+
513+ def create_nodes (self , label : str , batch : list [dict ]) -> list [str ]:
514+ """Create multiple nodes in a single batch INSERT.
515+
516+ >>> ids = conn.create_nodes("User", [{"user_id": "u1", ...}, {"user_id": "u2", ...}])
517+ """
518+ return self ._ffi .create_nodes (label , [_python_to_ffi_props (p ) for p in batch ])
519+
520+ def import_file (self , label : str , file_path : str ) -> None :
521+ """Import nodes from a file (CSV, Parquet, JSON — auto-detected from extension).
522+
523+ >>> conn.import_file("User", "users.csv")
524+ """
525+ self ._ffi .import_file (label , file_path )
526+
527+ def execute_sql (self , sql : str ) -> None :
528+ """Execute a raw SQL statement (DDL, DML, or administrative command).
529+
530+ >>> conn.execute_sql("OPTIMIZE TABLE default.users FINAL")
531+ """
532+ self ._ffi .execute_sql (sql )
533+
475534 def __repr__ (self ) -> str :
476535 return "<Connection>"
477536
478537
538+ def _python_to_ffi_value (v ):
539+ """Convert a Python value to an FFI Value enum variant."""
540+ if v is None :
541+ return _FfiValue .NULL ()
542+ if isinstance (v , bool ):
543+ return _FfiValue .BOOL (v = v )
544+ if isinstance (v , int ):
545+ return _FfiValue .INT64 (v = v )
546+ if isinstance (v , float ):
547+ return _FfiValue .FLOAT64 (v = v )
548+ if isinstance (v , str ):
549+ return _FfiValue .STRING (v = v )
550+ if isinstance (v , list ):
551+ return _FfiValue .LIST (items = [_python_to_ffi_value (i ) for i in v ])
552+ if isinstance (v , dict ):
553+ from clickgraph ._ffi import MapEntry as _FfiMapEntry
554+ return _FfiValue .MAP (entries = [
555+ _FfiMapEntry (key = str (k ), value = _python_to_ffi_value (val ))
556+ for k , val in v .items ()
557+ ])
558+ return _FfiValue .STRING (v = str (v ))
559+
560+
561+ def _python_to_ffi_props (props : dict ) -> dict :
562+ """Convert a Python dict to an FFI-compatible property map."""
563+ return {str (k ): _python_to_ffi_value (v ) for k , v in props .items ()}
564+
565+
479566# ---------------------------------------------------------------------------
480567# Database
481568# ---------------------------------------------------------------------------
0 commit comments