diff --git a/drivers/python/README.md b/drivers/python/README.md index 3e246dda7..07067c19a 100644 --- a/drivers/python/README.md +++ b/drivers/python/README.md @@ -75,6 +75,16 @@ SET search_path = ag_catalog, "$user", public; * Simpler way to access Apache AGE [AGE Sample](samples/apache-age-note.ipynb) in Samples. * Agtype converting samples: [Agtype Sample](samples/apache-age-agtypes.ipynb) in Samples. +### Non-Superuser Usage +* For non-superuser usage see: [Allow Non-Superusers to Use Apache Age](https://age.apache.org/age-manual/master/intro/setup.html). +* Make sure to give your non-superuser db account proper permissions to the graph schemas and corresponding objects +* Make sure to initiate the Apache Age python driver with the ```load_from_plugins``` parameter. This parameter tries to + load the Apache Age extension from the PostgreSQL plugins directory located at ```$libdir/plugins/age```. Example: + ```python. + ag = age.connect(host='localhost', port=5432, user='dbuser', password='strong_password', + dbname=postgres, load_from_plugins=True, graph='graph_name) + ``` + ### License Apache-2.0 License diff --git a/drivers/python/age/__init__.py b/drivers/python/age/__init__.py index 638269090..fd50135af 100644 --- a/drivers/python/age/__init__.py +++ b/drivers/python/age/__init__.py @@ -24,15 +24,17 @@ def version(): return VERSION.VERSION -def connect(dsn=None, graph=None, connection_factory=None, cursor_factory=ClientCursor, **kwargs): +def connect(dsn=None, graph=None, connection_factory=None, cursor_factory=ClientCursor, load_from_plugins=False, + **kwargs): dsn = conninfo.make_conninfo('' if dsn is None else dsn, **kwargs) ag = Age() - ag.connect(dsn=dsn, graph=graph, connection_factory=connection_factory, cursor_factory=cursor_factory, **kwargs) + ag.connect(dsn=dsn, graph=graph, connection_factory=connection_factory, cursor_factory=cursor_factory, + load_from_plugins=load_from_plugins, **kwargs) return ag # Dummy ResultHandler rawPrinter = DummyResultHandler() -__name__="age" \ No newline at end of file +__name__="age" diff --git a/drivers/python/age/age.py b/drivers/python/age/age.py index 2d98e3ff2..817cc6e5a 100644 --- a/drivers/python/age/age.py +++ b/drivers/python/age/age.py @@ -35,20 +35,29 @@ def dump(self, obj: Any) -> bytes | bytearray | memoryview: class AgeLoader(psycopg.adapt.Loader): - def load(self, data: bytes | bytearray | memoryview) -> Any | None: - return parseAgeValue(data.decode('utf-8')) + def load(self, data: bytes | bytearray | memoryview) -> Any | None: + if isinstance(data, memoryview): + data_bytes = data.tobytes() + else: + data_bytes = data + return parseAgeValue(data_bytes.decode('utf-8')) -def setUpAge(conn:psycopg.connection, graphName:str): + +def setUpAge(conn:psycopg.connection, graphName:str, load_from_plugins:bool=False): with conn.cursor() as cursor: - cursor.execute("LOAD 'age';") + if load_from_plugins: + cursor.execute("LOAD '$libdir/plugins/age';") + else: + cursor.execute("LOAD 'age';") + cursor.execute("SET search_path = ag_catalog, '$user', public;") ag_info = TypeInfo.fetch(conn, 'agtype') if not ag_info: raise AgeNotSet() - + conn.adapters.register_loader(ag_info.oid, AgeLoader) conn.adapters.register_loader(ag_info.array_oid, AgeLoader) @@ -184,9 +193,10 @@ def __init__(self): self.graphName = None # Connect to PostgreSQL Server and establish session and type extension environment. - def connect(self, graph:str=None, dsn:str=None, connection_factory=None, cursor_factory=ClientCursor, **kwargs): + def connect(self, graph:str=None, dsn:str=None, connection_factory=None, cursor_factory=ClientCursor, + load_from_plugins:bool=False, **kwargs): conn = psycopg.connect(dsn, cursor_factory=cursor_factory, **kwargs) - setUpAge(conn, graph) + setUpAge(conn, graph, load_from_plugins) self.connection = conn self.graphName = graph return self