Skip to content

feat(plugin-neo4j): add a Neo4j driver over Bolt with Cypher - #3925

Merged
datlechin merged 6 commits into
TableProApp:mainfrom
J2TeamNNL:feat/plugin-neo4j
Nov 1, 2026
Merged

datlechin merged 6 commits into
TableProApp:mainfrom
J2TeamNNL:feat/plugin-neo4j

Conversation

@J2TeamNNL

@J2TeamNNL J2TeamNNL commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • New registry plugin Neo4jDriverPlugin. It talks Bolt itself, on the SwiftNIO and NIOSSL packages the Kafka plugin already links, so there is no new dependency. The handshake offers Bolt 5.4 down to 5.0, then 4.4 down to 4.2.
  • TableProNeo4jCore holds the parts that need no socket: PackStream (nesting capped at 64 levels), Bolt messages and chunking, value text, the Cypher the grid sends, and the rows it gets back.
  • Plugins/SharedNIO/PluginNIOChannel.swift: the cancellable NIO connect, the TLS context and the connect error text, shared by Kafka and Neo4j instead of copied.
  • Sidebar: each database from SHOW DATABASES, with Labels and Relationship Types inside, as the plugin's schemas. A user refused access to system still gets the database they connected to.
  • Grid: a label opens one row per node with <id>, <labels> and one column per property; a relationship type shows <id>, <start>, <end> and its properties. Sort and every filter operator become Cypher. A <labels> filter matches when any label does, BETWEEN reads both bounds, REGEX reads the value as text. Read-only in this PR.
  • A grid page always runs in Bolt read mode, so a raw filter cannot write.
  • Editor: Cypher runs as typed, with $1, $2 for the host's parameters. The row cap pulls cap + 1 records and discards the rest on the server.
  • Export pulls 1,000 records at a time instead of holding the whole result.
  • Stop sends RESET only to a user statement on the wire. A statement still waiting for the connection is dropped before it is sent, and metadata work holding the connection finishes.
  • A statement the classifier proves a read goes out in Bolt read mode (.neo4j is in enginesWithProvenReads), so the server refuses a write the classifier missed.
  • Safe Mode reads Cypher as the server does: \uXXXX escapes are decoded before tiering, and a DELETE with no WHERE, LIMIT or property map gets the dangerous-query confirmation. CREATE, MERGE, SET, REMOVE, DELETE, FOREACH and a procedure not known to read are writes, DROP is destructive, LOAD CSV reaches outside the database.
  • Grammar: a withoutDashComments fact for Cypher, so (a)-- (b) stays code and the ; after it still splits.
  • TLS uses the SSL settings in the form; Preferred connects without TLS when the TLS attempt fails. URLs with neo4j, neo4j+s, neo4j+ssc, bolt, bolt+s and bolt+ssc work: +s sets Verify Identity, +ssc sets Required.
  • SSH tunnel works like other network plugins. neo4j:// connects to the host it names and does not follow a cluster routing table.

Docs: TableProApp/docs#142, to merge after this one.

Test plan

  • swift test: TableProNeo4jCoreTests (39, including BETWEEN bounds, every <labels> operator, REGEX as text, the nesting limit), TableProSQLGrammarTests (272), TableProPluginKitTests (625).
  • Unit plan: every suite in TableProTests/Core/Utilities/SQL (814, including Cypher unicode escapes, the unfiltered DELETE, the -- split), PluginDriverAdapterStatementContextTests, ConnectionURLParserTLSPortTests, PluginMetadataRegistryTypeCountTests, SQLLexicalFeatureMappingTests.
  • scripts/probes/check-neo4j.sh against neo4j:5 (5.26.32), 40 checks: read mode refuses a write, a raw filter cannot write, BETWEEN and <labels> filters, a 2,500-row export in 3 batches, a queued statement cancelled while a count holds the connection, SSL Preferred against a server without TLS, and the server decoding \u0061bc outside a string.
  • The same probe against neo4j:4.4 (4.4.48): 40 checks, Bolt 4 path with numeric ids and no LOGON.
  • Neo4jDriverPlugin and KafkaDriverPlugin builds, verify-plugin-loads.sh, R1 and R2, check-plugin-provides.py, verify.sh l10n, and verify.sh lint on every changed Swift file.
  • AllPlugins locally stops on the missing Dameng bridge library, with no Swift errors; CI builds it.
  • Not rerun after the rewrite: the TLS-proxy checks with a self-signed certificate from the first round.

Fixes #1986

Labels and relationship types in the sidebar, nodes and relationships as grid rows, and Cypher in the editor. A Bolt 4.2 to 5.4 client written on the SwiftNIO packages already in the project.
J2TeamNNL and others added 4 commits October 31, 2026 15:34
…ed write counts

- Read-only and Safe Mode treat INSERT, ENABLE, DEALLOCATE and REALLOCATE as writes.
- A duration whose nanoseconds overflow Int64 formats instead of trapping.
- A path lists its steps in order, with each relationship pointing the way it was walked.
- A row-capped statement keeps the summary from its final DISCARD, so its write counts are kept.
- Rows affected counts created or deleted nodes and relationships; other counters show as a status message.
- The sidebar heading reads Types for both labels and relationship types.
- A name holding \u is refused: Neo4j decodes the escape inside backticks, so it could close its quotes.
- Text filters read any value as text, so a number or list no longer fails the page; labels match when one does.
- Clearing case sensitivity applies to =, != and IN, and still compares numbers as numbers.
- Only canonical numbers become number literals, so 012 and 20-digit ids stay text.
- Filtered and exact row counts are answered, so a filtered grid has a total.
- Indexes list on Neo4j 4.4 and stay with their own label or relationship type.
- START, STOP, ALTER and the other admin words count as writes only before their object, so a variable named start is a read.
- A cancel that lands as a capped statement ends no longer sends a DISCARD the server would reject.
# Conflicts:
#	apple/Packages/TableProCore/Package.swift
#	apple/Packages/TableProCore/Sources/TableProSQLGrammar/SQLLexicalProfile.swift
#	apple/TablePro/Core/Utilities/SQL/QueryClassifier.swift
@datlechin
datlechin merged commit fd8244b into TableProApp:main Nov 1, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Neo4j driver

2 participants