diff --git a/changelog/5197.bugfix.rst b/changelog/5197.bugfix.rst new file mode 100644 index 000000000000..73ba13397798 --- /dev/null +++ b/changelog/5197.bugfix.rst @@ -0,0 +1,4 @@ +Fixed incompatibility of Oracle with the :ref:`sql-tracker-store`, by using a ``Sequence`` +for the primary key columns. This does not change anything for SQL databases other than Oracle. +If you are using Oracle, please create a sequence with the instructions in the docs +:ref:`oracle-configuration`. diff --git a/changelog/5197.doc.rst b/changelog/5197.doc.rst new file mode 100644 index 000000000000..70e27737f43e --- /dev/null +++ b/changelog/5197.doc.rst @@ -0,0 +1 @@ +Added section on setting up the SQLTrackerStore with Oracle diff --git a/docs/api/tracker-stores.rst b/docs/api/tracker-stores.rst index cd3f400eb40b..51a9c1f1238c 100644 --- a/docs/api/tracker-stores.rst +++ b/docs/api/tracker-stores.rst @@ -27,6 +27,8 @@ InMemoryTrackerStore (default) :Configuration: To use the `InMemoryTrackerStore` no configuration is needed. +.. _sql-tracker-store: + SQLTrackerStore ~~~~~~~~~~~~~~~ @@ -70,6 +72,59 @@ SQLTrackerStore - ``login_db`` (default: ``None``): Alternative database name to which initially connect, and create the database specified by `db` (PostgreSQL only) - ``query`` (default: ``None``): Dictionary of options to be passed to the dialect and/or the DBAPI upon connect + +:Officially Compatible Databases: + - PostgreSQL + - Oracle > 11.0 + - SQLite + +.. _oracle-configuration: + +:Oracle Configuration: + To use the SQLTrackerStore with Oracle, there are a few additional steps. + First, create a database ``tracker`` in your Oracle database and create a user with access to it. + Create a sequence in the database with the following command, where username is the user you created + (read more about creating sequences `here `__): + + .. code-block:: sql + + CREATE SEQUENCE username.events_seq; + + Next you have to extend the Rasa Open Source image to include the necessary drivers and clients. + First download the Oracle Instant Client from `here `__, + rename it to ``oracle.rpm`` and store it in the directory from where you'll be building the docker image. + Copy the following into a file called ``Dockerfile``: + + .. code-block:: bash + + FROM rasa/rasa:|version|-full + # Switch to root user to install packages + USER root + RUN apt-get update -qq \ + && apt-get install -y --no-install-recommends \ + alien \ + libaio1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + # Copy in oracle instaclient + # https://www.oracle.com/database/technologies/instant-client/linux-x86-64-downloads.html + COPY oracle.rpm oracle.rpm + # Install the Python wrapper library for the Oracle drivers + RUN pip install cx-Oracle + # Install Oracle client libraries + RUN alien -i oracle.rpm + USER 1001 + + Then build the docker image: + + .. code-block:: bash + + docker build . -t rasa-oracle:|version|-oracle-full + + Now you can configure the tracker store in the ``endpoints.yml`` as described above, + and start the container. The ``dialect`` parameter with this setup will be ``oracle+cx_oracle``. + Read more about :ref:`deploying-your-rasa-assistant`. + RedisTrackerStore ~~~~~~~~~~~~~~~~~~ diff --git a/rasa/core/tracker_store.py b/rasa/core/tracker_store.py index 8ea2c4479e48..920064e3f8a4 100644 --- a/rasa/core/tracker_store.py +++ b/rasa/core/tracker_store.py @@ -21,6 +21,7 @@ from rasa.core.trackers import ActionExecuted, DialogueStateTracker, EventVerbosity from rasa.utils.common import class_from_module_path, raise_warning, arguments_of from rasa.utils.endpoints import EndpointConfig +from sqlalchemy import Sequence if typing.TYPE_CHECKING: from sqlalchemy.engine.url import URL @@ -521,6 +522,24 @@ def keys(self) -> Iterable[Text]: return [c["sender_id"] for c in self.conversations.find()] +def _create_sequence(table_name: Text) -> Sequence: + """Creates a sequence object for a specific table name. + + If using Oracle you will need to create a sequence in your database, as described here: + https://rasa.com/docs/rasa/api/tracker-stores/#sqltrackerstore + Args: + table_name: The name of the table, which gets a Sequence assigned + + Returns: A `Sequence` object + """ + + from sqlalchemy.ext.declarative import declarative_base + + sequence_name = f"{table_name}_seq" + Base = declarative_base() + return Sequence(sequence_name, metadata=Base.metadata, optional=True) + + class SQLTrackerStore(TrackerStore): """Store which can save and retrieve trackers from an SQL database.""" @@ -535,7 +554,9 @@ class SQLEvent(Base): __tablename__ = "events" - id = Column(Integer, primary_key=True) + # `create_sequence` is needed to create a sequence for databases that + # don't autoincrement Integer primary keys (e.g. Oracle) + id = Column(Integer, _create_sequence(__tablename__), primary_key=True) sender_id = Column(String(255), nullable=False, index=True) type_name = Column(String(255), nullable=False) timestamp = Column(Float)