Developing with the Prism API

Use a driver for application code. The Prism API defines the messages and transport conventions needed to implement a client. Use the Prism API release that matches your server and consult the protocol reference for message fields.

Obtain the bindings

The Prism API 2.2 release includes the Protobuf schemas and a Go archive. The definitions are also published as org.polypheny:prism:2.2 on Maven Central, polypheny-prism-api==2.2 on PyPI, and Polypheny.Prism version 2.2.0 on NuGet. The Maven JAR contains schemas for code generation. For other languages, generate bindings from the schemas with protoc.

To generate Python bindings yourself, unpack the release’s protobuf-files-2.2.zip and run the following in the directory containing org. The compiler and runtime versions in this example support the optional fields in the definitions.

python -m pip install grpcio-tools==1.64.1 protobuf==5.27.2
python -m grpc_tools.protoc -I . --python_out=. org/polypheny/prism/*.proto

Transport and session

  1. Open a TCP connection to the Prism port, or connect to the configured Unix domain socket. Both peers send the transport version prefixed by a single byte giving the length, including the final newline. The transport version strings are plain-v1@polypheny.com for TCP and unix-v1@polypheny.com for Unix sockets. Verify the peer sends the same string.
  2. After the transport handshake, prefix every serialized Protobuf message with its byte length as an eight-byte little-endian integer. Read the full prefix and payload, handling partial socket reads.
  3. Send a Request with a positive ID and a connection_request as the first message. Supply the Prism API version, credentials, and any connection properties. Check ConnectionResponse.is_compatible before sending queries. TCP authentication requires a username and password. With a Unix socket, the server can use the peer’s operating system credentials when the client omits the username.
  4. Match each Response to its request by ID. Read responses until Response.last is true. Handle an error_response as an error for that request. For query results, Frame.is_last separately tells you when to stop sending FetchRequest messages.
  5. Close statement handles when finished. When auto-commit is disabled, use the commit and rollback requests to manage transactions. End the session with DisconnectRequest and close the socket.

Run a query over TCP

This example requires Python 3.9 or later and a running Polypheny server with Prism 2.2. It uses 127.0.0.1:20590, the local pa user, and an empty password by default. Set PRISM_HOST, PRISM_PORT, PRISM_USER, and PRISM_PASSWORD as needed. Install the bindings in a Python virtual environment.

python -m pip install polypheny-prism-api==2.2

Save as prism_query.py and run it with Python. The example fetches two rows one at a time and prints Ada and Lin.

import os
import socket
import struct
from itertools import count

from org.polypheny.prism import protointerface_pb2 as protocol


def read_exact(sock, size):
    data = bytearray()
    while len(data) < size:
        chunk = sock.recv(size - len(data))
        if not chunk:
            raise EOFError("Prism closed the connection")
        data.extend(chunk)
    return bytes(data)


def exchange(sock, request):
    payload = request.SerializeToString()
    sock.sendall(struct.pack("<Q", len(payload)) + payload)
    while True:
        (size,) = struct.unpack("<Q", read_exact(sock, 8))
        response = protocol.Response.FromString(read_exact(sock, size))
        if response.id != request.id:
            raise RuntimeError(f"Unexpected response ID {response.id}")
        if response.HasField("error_response"):
            raise RuntimeError(response.error_response.message)
        if response.last:
            return response


address = (os.getenv("PRISM_HOST", "127.0.0.1"), int(os.getenv("PRISM_PORT", "20590")))
request_ids = count(1)

with socket.create_connection(address, timeout=10) as sock:
    version = b"plain-v1@polypheny.com\n"
    handshake = bytes([len(version)]) + version
    sock.sendall(handshake)
    (size,) = read_exact(sock, 1)
    if read_exact(sock, size) != version:
        raise RuntimeError("Unexpected Prism transport version")

    request = protocol.Request(id=next(request_ids))
    request.connection_request.major_api_version = 2
    request.connection_request.minor_api_version = 2
    request.connection_request.username = os.getenv("PRISM_USER", "pa")
    request.connection_request.password = os.getenv("PRISM_PASSWORD", "")
    response = exchange(sock, request).connection_response
    if not response.is_compatible:
        server_api = f"{response.major_api_version}.{response.minor_api_version}"
        raise RuntimeError(f"Incompatible Prism API version {server_api}")

    request = protocol.Request(id=next(request_ids))
    query = request.execute_unparameterized_statement_request
    query.language_name = "sql"
    query.statement = "SELECT name FROM (VALUES ('Ada'), ('Lin')) AS example(name) ORDER BY name"
    query.fetch_size = 1
    statement = exchange(sock, request).statement_response
    frame = statement.result.frame

    while True:
        for row in frame.relational_frame.rows:
            print(row.values[0].string.string)
        if frame.is_last:
            break
        request = protocol.Request(id=next(request_ids))
        request.fetch_request.statement_id = statement.statement_id
        request.fetch_request.fetch_size = 1
        frame = exchange(sock, request).frame

    request = protocol.Request(id=next(request_ids))
    request.close_statement_request.statement_id = statement.statement_id
    exchange(sock, request)

    request = protocol.Request(id=next(request_ids))
    request.disconnect_request.SetInParent()
    exchange(sock, request)

The helper consumes intermediate responses and returns the final response. For direct execution, the intermediate response carries the statement ID before results arrive. To inspect a document namespace, send an EntitiesRequest on the same authenticated session and read the document variants of its EntitiesResponse. The protocol reference describes the available fields.

© Polypheny GmbH. All Rights Reserved.