GQL Support Implementation - #1468
Conversation
Both keywords are mapped to OPENCYPHER_L in sqlwords.gperf and distinguished via yylval->subtok in the lexer. The inline rewriter calls DB.DBA.OPENGQL_EXEC() for GQL/OPENGQL (mirroring the existing OPENCYPHER_EXEC pattern). DB.DBA.GQL renamed to DB.DBA.GQL_EXEC to avoid keyword conflicts.
…encypher-gql-preview
OPENGQL is a reserved SQL keyword (sqlwords.gperf) so the parser rejects it as a procedure name. Quote the identifier in the CREATE PROCEDURE declaration and update the VAD check to call OPENGQL_EXEC directly, matching the established OPENCYPHER_EXEC pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OPENGQL_EXEC was collecting the SPARQL result into a PL array and returning it with `return data`, which delivers nothing to isql or ODBC/JDBC callers. Replace with exec_result_names/exec_result streaming, matching the established OPENCYPHER_EXEC pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ANY_GRAPH AST node guard in DB.DBA.OPENGQL_EXEC, matching the
fix already present in DB.DBA.GQL_TO_SPARQL. When the parser sets
aref(proc_body, 1) to vector('ANY_GRAPH'), set _default_graph to
null instead of passing it to GQL_GRAPH_REF_VALUE, which would
fail with SR066: Unsupported case in CONVERT (ARRAY_OF_POINTER
-> VARCHAR).
Add parser support for keyword function tokens in GQL_PARSE_PRIMARY (SQRT, EXP, LOG, LOG10, LN, POWER, MOD, SIN, COS, TAN, COT, ASIN, ACOS, ATAN, DEGREES, RADIANS, CEILING, LEFT, RIGHT, BTRIM, LTRIM, RTRIM, CHAR_LENGTH, CHARACTER_LENGTH, OCTET_LENGTH, BYTE_LENGTH, CARDINALITY, SIZE, COALESCE, NULLIF, IFNULL, GREATEST, LEAST, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCAL_TIME, LOCAL_TIMESTAMP). Add translator bif: mappings in GQL_GEN_EXPR. Add GQL_SUPPORT_ANALYSIS.md.
Update GQL_SUPPORT_ANALYSIS.md to clarify SELECT statement has no TCK feature file coverage and is a secondary query form.
Parser changes (gql_expr.sql, gql_parser.sql, gql_lexer.sql):- Add || concatenation operator (token 54) in lexer and parser- Add ALL_DIFFERENT, SAME, PROPERTY_EXISTS, ELEMENT_ID predicates in GQL_PARSE_PRIMARY- Add IS DIRECTED, IS TYPED, IS SOURCE OF, IS DESTINATION OF in GQL_TRY_IS_EXPR- Add FINISH clause (token 328) in GQL_PARSE_COMPOSITE_QUERY- Add OTHERWISE (token 430) as set operator alongside UNION/EXCEPT/INTERSECTTranslator changes (gql_translate.sql):- STROP (||) maps to SPARQL CONCAT()- ALL_DIFFERENT generates pairwise inequality filters- SAME generates pairwise equality filters- PROPERTY_EXISTS generates EXISTS check on property triple- IS_DIRECTED returns true (all RDF edges are directed)- IS_TYPED maps to DATATYPE() comparison with XSD type IRI- IS_SOURCE_OF/IS_DEST_OF maps to variable equality with edge endpoints- ISLABEL maps to EXISTS { var rdf:type label }- ELEMENT_ID maps to STR()- FINISH suppresses RETURN clause- NULLIF maps to IF(a=b, NULL, a)
…ot handling, add OTHERWISE translator - IS TYPED now accepts keyword tokens (INTEGER, STRING, BOOLEAN, etc.) as type names, not just IDENT - PROPERTY_EXISTS now consumes optional DOT before property name (e.g., PROPERTY_EXISTS(n, .name)) - OTHERWISE set operator translator: returns left if non-empty, else right via UNION with FILTER NOT EXISTS - Updated GQL_SUPPORT_ANALYSIS.md with Phase 1/2 completion status
Add 8 translate-suite tests covering: - USE PROPERTY GRAPH graph IRI derivation (TR16d) - Per-graph ontology NS in PREFIX emission (TR16e) - Per-graph label IRI (TR16f) - INSERT INTO PROPERTY GRAPH (TR16g) - Per-graph node IRI minting (TR16h) - CREATE PROPERTY GRAPH catalog metadata (TR16i) - DROP PROPERTY GRAPH IRI derivation (TR16j) - Edge property IRIs with per-graph NS (TR16k) Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…OPERTY Fix malformed --; procedure separators in gql_runtime.sql and gql_translate.sql (should be bare ;). Add DROP_PROPERTY_GRAPH to the catalog_asts dispatch in gql_translate.sql so DROP PROPERTY GRAPH is routed to GQL_GEN_CATALOG instead of falling through to the no-RETURN error. Add USE_PROPERTY_AT_SCHEMA handling in gql_main.sql GQL_TO_SPARQL so the graph IRI is derived from the bare property-graph name before calling GQL_TO_SPARQL_IMPL. Fix TR16k test to use supported edge INSERT syntax with variables and explicit IRIs instead of unsupported double-paren anonymous node syntax. All 8 property-graph tests (TR16d-TR16k) pass. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…operties
Add lexer tokens << (420) and >> (421) for RDF 1.2 triple-term and
reified-triple syntax.
Add parser support in GQL_PARSE_EDGE_PATTERN for:
[r:TYPE <<( props )>>] → explicit triple-term (standard RDF 1.2)
[r:TYPE << props >>] → reified-triple shorthand
[r:TYPE <<(...) ~ :iri >>] → named reifier
[r:TYPE <<... ~ >>] → fresh blank node reifier
Add GQL_PARSE_PROPERTIES_BARE for parsing key:value pairs without
enclosing braces (needed for <<(...)>> syntax).
Extend edge AST with triple_term_mode (index 9) and reifier_name
(index 10).
Update codegen in gql_sparql_gen.sql and gql_translate.sql to:
- Emit standard rdf:reifies + <<(s p o)>> for triple-term mode
- Emit << s p o ~ reifier >> for reified-triple shorthand
- Use named reifier when provided, fresh blank node otherwise
- Fix existing rdf12 default to use standard rdf:reifies pattern
instead of non-standard triple-term-as-subject
Add test cases TR168-TR174 covering all new syntaxes plus backward
compatibility with existing {props}, {| |}, and classic reification.
All 7 new tests pass. Backward compatibility verified.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
In property-graph mode, auto-generated node and edge IRIs now use sequential indices (node_0, node_1, edge_0, edge_1, ...) instead of UUIDs. This produces short, deterministic, human-readable identifiers that match property-graph conventions (Neo4j, TigerGraph, etc.). The counter is stored as a Virtuoso named sequence keyed by the property-graph data namespace, so each graph has its own independent counter. Changes: - Add GQL_PG_NEXT_ID() for atomic get-and-increment per-graph counter - GQL_NEW_NODE_URI_CTX: use sequential ID when is_property_graph=1, UUID otherwise - GQL_NEW_EDGE_URI_CTX: use sequential ID when is_property_graph=1, UUID otherwise - Fix classic reification INSERT path to use generated edge URI instead of SPARQL variable when no explicit reifier is provided - Add test cases TR16l (sequential edge ID) and TR16m (classic UUID) - Update TR16h description for sequential IDs Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Revert the per-property-graph sequential ID counter. UUIDs are lock-free, stateless, and collision-proof. The sequential approach added two mutex-protected sequence_set calls per ID with no benefit at query time -- Virtuoso iri_to_id mapping already provides compact internal integer IDs for storage and indexing. Keep the INSERT path fix that uses the generated edge URI instead of the SPARQL variable for classic reification. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Complete the property-graph DDL: CREATE VIRTUAL PROPERTY GRAPH maps
relational tables to a virtual RDF named graph via Virtuoso quad maps
(queryable from GQL and SPARQL); CREATE PHYSICAL PROPERTY GRAPH keeps
the existing writable named-graph behaviour. All 14 PG DDL tests pass.
Quad-map generation (gql_pg_ddl.sql):
- Generate Virtuoso IRI classes ("create iri class ... %U (in _c varchar)")
per node table plus one quad-map graph carrying node subject maps
(rdf:type + property triples) and relationship triples
(source-class -> label -> destination-class). The earlier inline
"<...{col}...>" template form is not valid Virtuoso quad-map syntax.
- Resolve table and column names to their canonical catalog case
(case-insensitive servers fold unquoted names to upper); qualify bare
names via complete_table_name + SYS_KEYS.
- Fix an infinite loop in the IRI-template rewriter (it re-scanned the
text it had just inserted) and extract plain PK column names from the
REPL_PK_COLS descriptor vectors.
- Declare the pgraph: prefix inline in every generated statement and run
each through GQL_PG_NL (Virtuoso does not interpret "\n" in string
literals; a literal backslash breaks the SPARQL tokenizer).
- Idempotent DROP: remove the single quad map and per-node IRI classes.
Parser: recognise the property-graph DDL words (VIRTUAL, PHYSICAL,
TABLES, PROPERTIES, KEY, REFERENCES, REIFIER, TEMPLATE, NAMED,
ANONYMOUS, IRI) as soft keywords matched positionally via GQL_KW instead
of reserving them in the lexer -- reserving IRI/KEY/etc. shadowed
ordinary identifiers and functions such as iri() in normal queries.
Accept keyword-like words as table/column/label names in the DDL.
DROP PROPERTY GRAPH is now idempotent (silent when not defined).
VAD: add rdb2rdf pre-install dependency check, load gql_bootstrap before
gql_pg_ddl (it defines the GQL_PROPERTY_GRAPH_DEF catalog + GQL_PG_DEF_*
helpers), add gql_pg_ddl to the load order, and sync the vad/data mirror
copies.
Tests: add binsrc/gql/test_gql_pg_ddl.sql (PG01-PG14).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The INSERT INTO PROPERTY GRAPH <virtual> inline form was already rejected, but USE PROPERTY GRAPH <virtual> ... INSERT/SET/REMOVE/DELETE slipped through and produced SPARQL Update against the read-only quad maps. Propagate the bare property-graph name from the schema-level USE PROPERTY GRAPH binding into the query context and, after the clause loop, reject any write operation whose active property graph is virtual (GQ213). MATCH/RETURN on a virtual PG and writes to physical PGs are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both VIRTUAL and PHYSICAL now parse the same NODE TABLES / RELATIONSHIP TABLES syntax. VIRTUAL requires NODE TABLES; PHYSICAL makes it optional (with tables: build + materialize; without: empty writable graph). The bare form CREATE PROPERTY GRAPH (no VIRTUAL/PHYSICAL keyword) now routes through GQL_PARSE_CREATE_PROPERTY_GRAPH_V2 with mode=546 (physical) instead of the legacy GQL_PARSE_CREATE_GRAPH path, so it also accepts NODE TABLES. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
GQL_PG_CREATE_PHYSICAL now accepts the same (pg_name, node_tables, rel_tables) signature as GQL_PG_CREATE_VIRTUAL. When node tables are supplied, it builds quad maps via the shared GQL_PG_BUILD_QM helper, materializes the triples into the named graph (RDF_VIEW_SYNC_TO_PHYSICAL with SPARQL INSERT INTO GRAPH fallback), then drops the quad maps so the graph is purely physical and writable. When no node tables are supplied, it preserves the old behavior: creates an empty writable named graph. GQL_PG_MATERIALIZE also gains the SPARQL INSERT INTO GRAPH fallback for virtual graphs, so materialization works even when RDF_VIEW_SYNC_TO_PHYSICAL is unavailable or fails. GQL_PG_DROP now drops residual quad maps/IRI classes for physical graphs that were created from tables (DEFINITION non-null). The translator (gql_sparql_gen.sql) passes node/rel tables through to GQL_PG_CREATE_PHYSICAL. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add PG15-PG20 covering the new from-tables physical graph behavior: - PG15: CREATE PHYSICAL with NODE TABLES creates materialized graph - PG16: Data from underlying tables appears in physical graph - PG17: INSERT into physical graph succeeds (writable) - PG18: DROP removes triples and catalog for physical-from-tables - PG19: Bare CREATE PROPERTY GRAPH with NODE TABLES defaults to physical - PG20: CREATE PHYSICAL (no tables) creates empty writable graph Fix PG12 to use the new 3-arg GQL_PG_CREATE_PHYSICAL signature. Update cleanup to drop new test graphs. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Three fixes found by building and running the test suite: 1. Parser: PHYSICAL branch peeked at NODE token but did not consume it before expecting TABLES, causing "Expected TABLES" error. Now consumes NODE with _pos := _pos + 1. 2. Materialization: RDF_VIEW_SYNC_TO_PHYSICAL hangs on property-graph quad maps because they lack RDF View metadata. Removed that code path; SPARQL INSERT INTO GRAPH is now the sole materialization mechanism for both CREATE PHYSICAL and GQL_PG_MATERIALIZE. 3. Deadlock fix: the quad map and the INSERT target used the same graph IRI, causing a self-referential deadlock. CREATE PHYSICAL now builds quad maps under a temporary "<graph>-virtual-tmp" IRI, then copies triples into the real target graph IRI. All 20 PG DDL tests pass (PG01-PG20). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add gql_pg_ddl.sql to SQL_FILES_SPARQL in libsrc/Wi/Makefile.am so it is embedded in sql_code_sparql.c and loaded automatically when the server starts with a fresh database. Previously it was only available via the opengql VAD or manual LOAD, despite all other GQL modules being compiled in. Also add gql_pg_ddl.sql to binsrc/gql/gql_load.sql (the loader script for older builds) after gql_bootstrap.sql, which creates the catalog tables it depends on. Verified with a fresh database: all GQL PG DDL procedures (GQL_PG_CREATE_VIRTUAL, GQL_PG_CREATE_PHYSICAL, GQL_PG_BUILD_QM, GQL_PG_DROP_QM, etc.) are present at startup with no manual loading. All 20 PG DDL tests pass. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Property-graph DDL executes as a side effect of GQL translation (it calls PL procedures, not SPARQL text). GQL_GEN_CATALOG previously appended a leftover ";\n-- ... executed" string for CREATE_PROPERTY_GRAPH_V2 and DROP_PROPERTY_GRAPH, which the executors then tried to run — raising a spurious SQ074 over the SQL/GQL-prefix path even though the graph was already created/dropped. - GQL_GEN_CATALOG (gql_sparql_gen.sql): CREATE/DROP now contribute no SPARQL text; GQL_TO_SPARQL returns empty for these DDL statements. - OPENGQL_EXEC (gql_endpoint.sql): flag PG DDL and skip the empty-branch re-run of GQL_RUN, which would otherwise double-execute (GQ202 on CREATE). - Parser (gql_parser.sql): accept DROP [VIRTUAL|PHYSICAL] PROPERTY GRAPH; the keyword asserts the target's mode and GQL_GEN_CATALOG raises GQ214 on a mismatch, leaving the graph intact. - Tests (test_gql_pg_ddl.sql): PG01/PG05 now assert on the catalog rather than the returned text; add PG21-PG25 for DROP VIRTUAL/PHYSICAL and the GQ214 mode-mismatch. Suite passes 25/25. VAD mirror copies under binsrc/gql/vad/vad/data/ kept in sync. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`GQL USE PROPERTY GRAPH <n> MATCH ... RETURN ...` over a SQL client
(isql/DBeaver) failed with SR066 (CONVERT ARRAY_OF_POINTER -> VARCHAR).
OPENGQL_EXEC derives its default graph from the PROC at-schema clause; for
USE PROPERTY GRAPH that node is ('USE_PROPERTY_AT_SCHEMA', <GRAPH_REF>),
and GQL_GRAPH_REF_VALUE's fallback cast(... as varchar) blew up on the
wrapper vector. (USE GRAPH <iri> worked because its at-schema node is a
plain GRAPH_REF.)
Add a USE_PROPERTY_AT_SCHEMA branch to OPENGQL_EXEC that unwraps the inner
GRAPH_REF and resolves GQL_PG_GRAPH_IRI(name) — mirroring the translator's
handling. The GQL prefix now returns rows for single-column, multi-column,
and bare-node (RETURN n) queries.
VAD mirror copy kept in sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the SPARQL/openCypher/openGQL <select> block from the SPARQL endpoint HTML form (sparql_ui.sql). The endpoint still honors the `language=` request parameter and the `GQL`/`opencypher` keyword prefix; only the on-page selector is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Delete .windsurf/workflows/create-documentation-frontend.md, an IDE workflow doc that should never have been in the source tree, and add .gitignore rules for editor / AI-assistant directories (.windsurf, .claude, .cursor, .vscode, .idea, .aider*, .devin, .codeium, .continue) so they cannot be committed again. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The USE-PROPERTY-GRAPH work moved the GQL namespaces from the old opengql/ontology# / opengql/data# / urn:opengql:default scheme to host-based per-graph IRIs (…/gql/ontology/, …/gql/data/, …/pgraph#default), but test_gql_translate.sql and test_gql_runtime.sql still asserted the old strings and so failed: - RT3/RT4/RT5: assert the current host-based namespaces (structure, not a literal host). - TR112/TR113: edge-label predicate is …/ontology/ACTED_IN (slash), not …#ACTED_IN. - TR4b2/TR4b4/TR80c/TR80d: the weight property IRI is …/gql/ontology/weight. - TR16i/TR16j: CREATE/DROP PROPERTY GRAPH execute as catalog side effects and now translate to empty SPARQL, so assert the catalog outcome (GQL_PG_DEF_GET) instead of matching generated text. The binsrc/tests/suite/ copies had drifted from the maintained binsrc/gql/ sources; both are re-synced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GQL_REGISTER_NS registered a global 'gql:' namespace prefix pointing at the openGQL ontology, overriding the GraphQL plugin's own global 'gql:' (http://www.openlinksw.com/schemas/graphql#). That silently broke the GraphQL endpoint's schema-mapping lookups (GQTC2 "Can not find property mapping"), since binsrc/graphql/ itself is unchanged on this branch. openGQL never needs the global decl — every SPARQL statement it generates emits its own `PREFIX gql: <ontology-ns>`. Register the openGQL namespaces under non-colliding prefixes (gqlo:, gqld:) and leave 'gql:' to the GraphQL plugin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…icates
Two issues surfaced by the tgql_rdf_equiv ACID test's "multiple sequential
transfers" case:
1. Arithmetic SET (SET a.balance = a.balance - N) translated to a combined
DELETE {…?old} INSERT {…?new} WHERE { … BIND((?prop-N) AS ?new) … }.
That MODIFY works at the SPARQL top level, but when executed via exec()
inside a stored procedure (the GQL_RUN / DML path), the BIND-computed
INSERT variable is dropped — only the DELETE takes effect, so each SET
*erased* the balance instead of updating it. Wrap the WHERE body in a
`SELECT *` subquery so the computed value is materialised as a projected
binding and survives. INSERT-only SET (no DELETE) is unchanged.
2. The ACID test summed balances via SPARQL using the pre-scheme-change
predicates <urn:gql:acid#balance>/#initialBalance/#Account, but GQL stores
properties under its ontology namespace (…/gql/ontology/…). The sums
therefore matched nothing (0), so the conservation check failed. Match
the GQL-namespace predicates host-independently with strends().
With both, the full GQL ACID suite passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Files preserved in external storage for potential future re-addition. See plans/remove-opencypher-and-store-files-here-for-future/PLAN.md. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Drop 11 cypher_*.sql entries from SQL_FILES_SPARQL in Makefile.am (keep all gql_*.sql entries). Remove opencypher_plugin Load lines from virtuoso.ini, default-virtuoso.ini, and installer virtuoso.ini. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
sqlwords.gperf: remove OPENCYPHER keyword (keep GQL/OPENGQL -> OPENCYPHER_L). scn3.l: remove scn3_opencypher_inline function and cypher dispatch branches in OPENCYPHER_SKIP rules; simplify subtok to always 1 (GQL only). Keep OPENCYPHER_SKIP state and OPENCYPHER_L token for GQL. sparql_io.sql: remove SPARQL_ENDPOINT_OPENCYPHER_BODY and SPARQL_ENDPOINT_RETURN_OPENCYPHER_SPARQL procedures, openCypher routing block, and opencypher_request variable; repoint warning suppression to opengql_request. sparql_ui.sql: remove ui_can_opencypher, openCypher panel, and 'opencypher' from language list; keep opengql panel and ui_can_opengql. sql3.y is unchanged: OPENCYPHER_L grammar rules are shared by GQL. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Integrates 61 upstream commits including: - RDF 1.2 / RDF-star initial support - OpenSSL 3.0 / TLS API modernization - zlib 1.3.2 upgrade - SQL optimizer fixes (OR/NULL, INTERSECT, NOT(equality), etc.) - Deprecated admin interface and installer file removal - delta_sharing plugin Conflicts resolved in 6 files: - bin/installer/virtuoso.ini (accepted upstream deletion) - binsrc/sqldoc/isql_log_normalize.c (combined both fixes) - binsrc/tests/suite/tgraphql.sh (combined test changes) - binsrc/tests/suite/tgraphql.sql (combined test cases) - binsrc/tests/suite/virtuoso-1111.ini (combined config changes) - libsrc/Wi/rdfinf.c (merged RDF-star + our RDF 1.2 changes) Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
7 files added by the GQL implementation or upstream merge were missing the standard OpenLink Virtuoso Open-Source GPL v2 license header: - libsrc/Wi/gql_pl.sql - binsrc/gql/vad/vad/data/gql_pl.sql - binsrc/gql/test_gql_pg_ddl.sql - binsrc/hosting/gqlc/gqlc.sql - binsrc/hosting/gqlc/gqlc_plugin.c - binsrc/ws/delta_sharing/delta_sharing_plugin.c - binsrc/ws/delta_sharing/sql/server.sql Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
The gql_to_sparql() may return incorrect SPARQL, example: The |
When a user writes RETURN p.name AS ?n (SPARQL-style variable alias), gql_to_sparql() produced invalid SPARQL with a doubled question mark (??n). The root cause was two-fold: 1. Parser (gql_parser.sql): The RETURN item parser took the raw token value after AS without checking the token type. When the alias was a PARAM token (?n or $n), the value included the ?/$ prefix, which was then stored in the AST. The translator unconditionally prepended ? to the alias, producing ??n. Fix: Normalize the alias token after AS by checking the token type. IDENT tokens are used as-is, PARAM tokens have their ?/$ prefix stripped, ACCENT_IDENT tokens have backticks stripped, and keyword tokens (200+) are accepted as alias names (e.g. AS node). Other token types raise a syntax error. 2. Translator (gql_translate.sql): The PARAM handler in GQL_GEN_EXPR only stripped the $ prefix (char 36) from parameter names, not the ? prefix (char 63). When ?n appeared in ORDER BY (parsed as a PARAM node), the handler prepended another ?, producing ??n. Fix: Strip both ? (char 63) and $ (char 36) prefixes in the PARAM handler. Also adds 4 regression tests (TR175-TR178) covering AS ?n, AS $name, AS ?n ORDER BY ?n, and AS n (bare identifier regression check). Addresses: openlink#1468 (comment 5367492189)
|
Thanks for catching this. The issue is fixed in commit 006a58a on this branch. Root cause was two-fold:
Fix:
This means all of the following now produce RETURN p.name AS ?n
RETURN p.name AS $n
RETURN p.name AS `n`
RETURN p.name AS nAnd Four regression tests (TR175–TR178) were added to |
Summary
Implements GQL query support in Virtuoso by translating GQL to SPARQL, built on top of the merged
develop/7upstream (RDF 1.2/RDF-star, OpenSSL 3 modernization, zlib 1.3.2, delta_sharing plugin, SQL optimizer fixes).develop/7(105 files changed, ~50k insertions)danielhmills/develop/7with all 6 predicted conflicts resolvedOPENCYPHER_LSQL token mapping is retained as the shared lexer entry point)Core GQL implementation (
libsrc/Wi/gql_*.sql)14 new server-bootstrap SQL modules implementing a full GQL→SPARQL translator:
gql_lexer.sqlgql_parser.sqlgql_expr.sqlgql_translate.sqlgql_sparql_gen.sqlgql_plan.sqlgql_var_pass.sqlgql_runtime.sqlGQL_RUN/OPENGQL_EXECgql_pg_ddl.sqlgql_pl.sqlgql_main.sqlUSE PROPERTY GRAPHscopinggql_endpoint.sql/sparqland openGQL HTTP endpoint integrationgql_stats.sqlgql_bootstrap.sqlKey features
ASaliases<<(...)>>,<<...>>,~reifier for edge propertiesgql/data, vocabulary undergql/ontologybinsrc/hosting/gqlc/): ICU-basedGQL_NORMALIZE,GQL_IS_NORMALIZED,GQL_PERCENTILE_CONT,GQL_PERCENTILE_DISCTests
binsrc/tests/suite/(test_gql_.sql, tgql_.sh) + unit tests inbinsrc/gql/binsrc/gql/tck/VAD packaging
binsrc/gql/vad/— openGQL VAD package with install/uninstall scripts and mirroredvad/data/copiesBuild system
libsrc/Wi/Makefile.am— GQL modules compiled into server bootstrapbinsrc/hosting/gqlc/Makefile.am— gqlc C pluginbinsrc/ws/delta_sharing/Makefile.am— delta_sharing plugin (from upstream)Test plan
make -j4builds cleanly (macOS Apple Silicon, OpenSSL 3.6.2)make check— full test suite: 9,922 passed, 0 failed, 0 abortedtgraphql: 82 passed, 0 failedtgql_unit: 21 passed (GQL observability + unit tests)tgql_rdf_equiv: 14 passed, 0 failedDB.DBA.GQL_TO_SPARQL_PARAMStranslates GQL MATCH to SPARQL correctlyGraphQL/SPARQL Bridge version 0.9.3).claude,.windsurf, etc.) or LLM prompt files in tree or historyGenerated with Devin