Skip to content

Commit e2e272b

Browse files
sqlparserJamesclaude
authored
Merge pom_dlineage.xml into pom.xml, and make CI run the jar it builds (#48)
Closes #46 and #47. Both were reported against the standalone data-lineage build, and both come from the same place: it was a second POM that no workflow ever *ran*. #46 the documented `java -cp` put external_lib/* on the classpath, a directory only the Windows .bat route creates. Anyone following the Maven instructions in order got NoClassDefFoundError on the first command in the section. The jar Maven had actually downloaded sat in ~/.m2 under a filename carrying a version number that changes every release, so it could not be guessed either. #47 pom_dlineage.xml lacked the JAXB dependencies pom.xml had carried for ages. It compiled fine and died at runtime on every JDK after 8. Since pom.xml *excluded* DataFlowAnalyzer, that second POM was the only thing that ever compiled the class, so no other build path could catch it. That is the third and fourth time this file broke unnoticed, after the pinned 3.1.1.0 parser jar and the <parent> published nowhere. Fixing the two instances would leave the category, so the file is gone. One POM ------- DataFlowAnalyzer's exclusion was circular: excluded because a separate POM compiled it, and that POM existed to produce a standalone jar. maven-shade-plugin produces the standalone jar from the one build, so nothing is excluded from pom.xml any more: target/gsp_demo_java-1.0-SNAPSHOT.jar the demos target/gsp_demo_java-1.0-SNAPSHOT-dlineage.jar executable, self-contained java -jar target/gsp_demo_java-1.0-SNAPSHOT-dlineage.jar /f x.sql /o out.json /json No classpath, no version number. #39 goes with it -- the second POM's incremental cleanup deleting the root build's target/classes, since both shared ${project.basedir} -- because one POM cannot collide with itself. target-dlineage/ is gone. Dependencies by coordinate, not by file --------------------------------------- lib/ held three <scope>system</scope> jars. Two were on Maven Central all along and byte-for-byte identical to the committed copies: sha256 7a43d2d5...f4e429f9 simple-xml-2.7.1.jar sha256 641a4d65...5fe692631d fastjson-1.2.83.jar so they are ordinary dependencies now: same bytes, resolved instead of committed. expr4j genuinely has no public coordinate and moved to lib-repo/, a file-based Maven repository in the checkout, as org.boris:expr:0.0.0-vendored with a hand-written .pom. lib/ is deleted. system scope is not merely deprecated here, it was load-bearing for the bug: maven-shade-plugin, maven-assembly-plugin and dependency:copy-dependencies all skip system-scope artifacts, so no packaged jar or generated classpath could ever contain them. That is most of why #46 needed a hand-assembled classpath. The switch also restored simple-xml's transitive stax/xpp3, which system scope had been silently dropping, and -Dexec.classpathScope=compile is no longer needed by any demo. jaxb-runtime 2.3.3 -> 2.3.9 --------------------------- Adding the JAXB dependencies as #47 suggests is not sufficient. 2.3.3's Injector calls sun.misc.Unsafe.defineClass, removed in JDK 11, so XML output still fails on JDK 9+ -- writing a 0-byte file and exiting 0. The API stays at 2.3.3 because the parser calls javax.xml.bind.JAXBContext and that is the last javax-namespace release; the runtime must not be "aligned" to match it. CI runs it now -------------- .github/scripts/smoke-dlineage-jar.sh executes the jar in both workflows on JDK 8 and 21. It asserts on the output, not the exit status: in #47 the process created the file, wrote nothing into it, and exited 0, so both an existence check and an exit-code check would have called that a pass. It checks XML as well as JSON because those are separate code paths and JAXB only breaks one -- verified by reverting to 2.3.3, where the guard goes red. Also ---- - setenv/fetch-parser.bat runs dependency:copy-dependencies instead of copying the parser alone, since lib/ no longer supplies the rest. external_lib\* is the whole CLASSPATH now. This also gives the .bat route JAXB, which it never had -- run_DataFlowAnalyzer.bat hit #47 too. - buildJar.sh, buildJar.bat and dlineage/MANIFEST.MF deleted: a fourth way to build the same jar, all naming files that no longer exist, none run by CI. - set-parser-version.sh drops to four files. Verified: mvn clean package green, 144 tests pass with nothing skipped, build from a completely empty local repository resolves expr from lib-repo and creates no gudusoft/ group directory, run-all-demos.sh 77/77, run-demo-cases.sh 22/22, and a Linux simulation of the .bat compile+run flow against external_lib alone. Claude-Session: https://claude.ai/code/session_012qPRpoD8exYRrUmbfXXWXj Co-authored-by: James <James@gudusoft.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 10ebf50 commit e2e272b

23 files changed

Lines changed: 693 additions & 474 deletions

‎.github/scripts/run-all-demos.sh‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ trap 'rm -rf "$WORK"' EXIT
4343
read -r -a EXTRA_MVN <<< "${MVN_ARGS:-}"
4444

4545
echo "== building classpath ${MVN_ARGS:+($MVN_ARGS)} =="
46-
# system-scope jars are on the compile classpath but not the runtime one, which
47-
# is why every demo here is documented as needing -Dexec.classpathScope=compile.
46+
# compile scope covers everything the demos need. It used to be load-bearing for
47+
# a different reason: three dependencies were <scope>system</scope> jars under
48+
# lib/, and system scope is on the compile classpath but not the runtime one,
49+
# which is why the demos were all documented as needing
50+
# -Dexec.classpathScope=compile. Those became ordinary Maven coordinates when
51+
# pom_dlineage.xml was merged into pom.xml, so that caveat no longer applies.
4852
if ! mvn -B -q ${EXTRA_MVN[@]+"${EXTRA_MVN[@]}"} dependency:build-classpath \
4953
-Dmdep.outputFile="$WORK/cp.txt" \
5054
-Dmdep.includeScope=compile > "$WORK/cp.log" 2>&1; then

‎.github/scripts/set-parser-version.sh‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
# Read, set, or verify the GSP parser version everywhere this repository writes
44
# it down.
55
#
6-
# The version lives in five files. The root build and the dlineage build each
7-
# declare a ${gsp.core.version} property, and the three connector/ modules
8-
# hardcode the version in their gsqlparser dependency, because they are separate
9-
# builds with no parent to inherit a property from. Nothing made them agree, so
10-
# they could drift apart silently -- and a bump meant five hand edits, which is
11-
# most of why bumping felt expensive.
6+
# The version lives in four files. The root build declares it as a
7+
# ${gsp.core.version} property, and the three connector/ modules hardcode the
8+
# version in their gsqlparser dependency, because they are separate builds with
9+
# no parent to inherit a property from. Nothing made them agree, so they could
10+
# drift apart silently -- and a bump meant four hand edits, which is most of why
11+
# bumping felt expensive.
12+
#
13+
# It was five until pom_dlineage.xml was merged into pom.xml. That second POM
14+
# declared its own copy of the property for no reason other than that it was a
15+
# second POM.
1216
#
1317
# Usage:
1418
# set-parser-version.sh print the current version(s)
15-
# set-parser-version.sh --check exit 1 if the five disagree
16-
# set-parser-version.sh 4.1.8 rewrite all five to 4.1.8
19+
# set-parser-version.sh --check exit 1 if the four disagree
20+
# set-parser-version.sh 4.1.8 rewrite all four to 4.1.8
1721
#
1822
# Exit codes:
1923
# 0 success, or --check found them consistent
@@ -54,8 +58,6 @@ new = os.environ["NEW"]
5458
TARGETS = [
5559
("pom.xml", "root build property",
5660
r"(?s)(<gsp\.core\.version>)([^<]+)(</gsp\.core\.version>)"),
57-
("pom_dlineage.xml", "dlineage build property",
58-
r"(?s)(<gsp\.core\.version>)([^<]+)(</gsp\.core\.version>)"),
5961
]
6062
for mod in ("oracleConnector", "snowflakeConnector", "sqlServerConnector"):
6163
TARGETS.append((
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Run the standalone data-lineage jar and check what it produced.
4+
#
5+
# This exists because building the dlineage demo proved nothing, twice. Until
6+
# 2026-07-28 it was a second POM, pom_dlineage.xml, and the only thing CI did
7+
# with it was `package`:
8+
#
9+
# * issue #47 -- it was missing the JAXB dependency the root POM already
10+
# carried. Compiles perfectly. Dies at runtime on every JDK after 8.
11+
# * issue #46 -- the documented run command put external_lib/ on the
12+
# classpath, a directory only the Windows .bat route ever creates, so
13+
# nobody following the Maven instructions could run the thing at all.
14+
#
15+
# Both shipped through a green build. So this runs it.
16+
#
17+
# The assertions are about the OUTPUT, not the exit status, and that is the
18+
# whole point. In issue #47 the process created lineage.json, died before
19+
# writing a byte into it, and still exited 0. An existence check, or an exit
20+
# code check, would both have called that a pass. Non-empty and parseable is
21+
# the weakest check that would actually have caught it.
22+
#
23+
# Usage:
24+
# smoke-dlineage-jar.sh [jar]
25+
#
26+
# Exit codes:
27+
# 0 the jar ran and produced valid, non-empty lineage in both formats
28+
# 1 anything else
29+
30+
set -euo pipefail
31+
32+
cd "$(dirname "${BASH_SOURCE[0]}")/../.." || exit 1
33+
34+
JAR="${1:-target/gsp_demo_java-1.0-SNAPSHOT-dlineage.jar}"
35+
SQL="samples/dlineage/demo.sql"
36+
37+
fail() { echo "::error::$*" >&2; exit 1; }
38+
39+
[ -f "$JAR" ] || fail "$JAR was not produced by 'mvn package'"
40+
[ -f "$SQL" ] || fail "$SQL is missing; the smoke test has no input"
41+
42+
OUT=$(mktemp -d)
43+
trap 'rm -rf "$OUT"' EXIT
44+
45+
echo "running $JAR on $SQL"
46+
echo "java: $(java -version 2>&1 | head -1)"
47+
48+
# --- JSON ------------------------------------------------------------------
49+
# No classpath, no version number, no external_lib. If this invocation ever
50+
# needs more than `java -jar`, the packaging has regressed and the README has
51+
# gone stale with it.
52+
java -jar "$JAR" /f "$SQL" /o "$OUT/lineage.json" /json \
53+
/simpleShowRelationTypes fdd,fdr /filterRelationTypes fdd
54+
55+
[ -s "$OUT/lineage.json" ] ||
56+
fail "lineage.json is empty or missing: the tool ran but produced nothing (cf. issue #47)"
57+
58+
python3 - "$OUT/lineage.json" <<'PY'
59+
import json, sys
60+
path = sys.argv[1]
61+
try:
62+
d = json.load(open(path))
63+
except Exception as e:
64+
sys.exit("lineage.json is not valid JSON: %s" % e)
65+
66+
# The shape differs with /graph, so accept either place the relationships live.
67+
rel = d.get("relationships") or d.get("data", {}).get("sqlflow", {}).get("relationships", [])
68+
if not rel:
69+
sys.exit("lineage.json parsed but carries no relationships; "
70+
"the analyzer returned an empty result")
71+
print("ok: JSON output, %d relationships" % len(rel))
72+
PY
73+
74+
# --- XML -------------------------------------------------------------------
75+
# A separate code path, through JAXB, and the one that actually broke. With
76+
# jaxb-runtime 2.3.3 on JDK 11+ the JSON above still succeeds while this writes
77+
# a 0-byte file and exits 0, so checking JSON alone is not enough. See the
78+
# jaxb-runtime comment in pom.xml for why the API and the runtime are
79+
# deliberately on different versions.
80+
java -jar "$JAR" /f "$SQL" /o "$OUT/lineage.xml"
81+
82+
[ -s "$OUT/lineage.xml" ] ||
83+
fail "lineage.xml is empty or missing: the JAXB output path failed (cf. issue #47)"
84+
85+
python3 - "$OUT/lineage.xml" <<'PY'
86+
import sys, xml.dom.minidom
87+
path = sys.argv[1]
88+
try:
89+
doc = xml.dom.minidom.parse(path)
90+
except Exception as e:
91+
sys.exit("lineage.xml is not well-formed: %s" % e)
92+
93+
rel = doc.getElementsByTagName("relationship")
94+
if not rel:
95+
sys.exit("lineage.xml is well-formed but carries no <relationship> elements")
96+
print("ok: XML output, %d relationships" % len(rel))
97+
PY
98+
99+
echo "ok: the standalone dlineage jar runs and produces lineage in both formats"

‎.github/workflows/build.yml‎

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ jobs:
3939
java-version: ${{ matrix.java }}
4040
cache: maven
4141

42-
# The parser version is written in five files: the root and dlineage
43-
# properties, and the three connector modules, which are separate builds
44-
# with no parent to inherit from. Nothing used to make them agree, so a
45-
# bump that missed one left a connector silently building against an older
46-
# parser. Use .github/scripts/set-parser-version.sh <version> to move them.
42+
# The parser version is written in four files: the root property, and the
43+
# three connector modules, which are separate builds with no parent to
44+
# inherit from. Nothing used to make them agree, so a bump that missed one
45+
# left a connector silently building against an older parser. Use
46+
# .github/scripts/set-parser-version.sh <version> to move them.
4747
- name: Parser version is consistent across all POMs
4848
run: .github/scripts/set-parser-version.sh --check
4949

@@ -75,31 +75,24 @@ jobs:
7575
echo "$out"
7676
grep -q "syntax errors: 0" <<<"$out"
7777
78-
# pom_dlineage.xml is a second, separate build in this same directory, and
79-
# nothing used to run it. It declared a <parent> of gudusoft:gsp_java,
80-
# the private library reactor, so it only built for someone who had that
81-
# POM installed locally; a plain clone got "Non-resolvable parent POM"
82-
# before Maven read any of its own contents. Building it here is what
83-
# stops that class of thing hiding again.
78+
# The standalone lineage tool. It used to be a second POM,
79+
# pom_dlineage.xml, and the step here only *built* it -- which is exactly
80+
# how it shipped broken twice. Building proves nothing about a program:
81+
# issue #47 was a missing JAXB dependency that compiles perfectly and dies
82+
# at runtime on any JDK past 8, and issue #46 was a documented run command
83+
# pointing at a directory the Maven route never creates. Both survived a
84+
# green build. So this runs it and checks the output; see the script for
85+
# why it asserts on the output rather than the exit status.
8486
#
85-
# It also has to leave the root build alone: it declares a single source
86-
# file, so sharing target/ with the root build makes the compiler plugin's
87-
# incremental cleanup delete every class the root build just produced
88-
# (issue #39). That is why it writes to target-dlineage/, and why the
89-
# count check below is worth the two lines.
90-
- name: Build the separate dlineage POM, and check it leaves target/ alone
91-
shell: bash
92-
run: |
93-
set -euo pipefail
94-
before=$(find target/classes -name '*.class' | wc -l)
95-
mvn -B -f pom_dlineage.xml package -DskipTests
96-
after=$(find target/classes -name '*.class' | wc -l)
97-
echo "root classes before=$before after=$after"
98-
if [ "$before" != "$after" ]; then
99-
echo "::error::pom_dlineage.xml changed the root build's target/classes ($before -> $after); see issue #39"
100-
exit 1
101-
fi
102-
ls -l target-dlineage/*.jar
87+
# It runs on both matrix JDKs for the same reason: #47 does not reproduce
88+
# on 8 at all, so a single-JDK check would have missed it.
89+
#
90+
# The issue-#39 guard that used to live here -- counting target/classes
91+
# before and after, because the second POM's incremental cleanup would
92+
# delete the root build's output -- is gone with the second POM. One POM
93+
# cannot collide with itself.
94+
- name: Run the standalone dlineage jar
95+
run: .github/scripts/smoke-dlineage-jar.sh
10396

10497
# The .bat scripts are the original Windows, no-Maven workflow: edit
10598
# setenv\setenv.bat, cd into a demo folder, run compile_<demo>.bat then

‎.github/workflows/nightly.yml‎

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,12 @@ jobs:
8484
run: .github/scripts/check-test-results.sh
8585

8686

87-
# A second, separate build in the same directory that nothing used to run.
88-
# It carried a <parent> pointing at the private gudusoft:gsp_java reactor,
89-
# so it only built where that POM happened to be installed. Also asserts it
90-
# leaves the root build's target/classes alone (issue #39).
91-
- name: Build the separate dlineage POM
92-
shell: bash
93-
run: |
94-
set -euo pipefail
95-
before=$(find target/classes -name '*.class' | wc -l)
96-
mvn -B -f pom_dlineage.xml package -DskipTests
97-
after=$(find target/classes -name '*.class' | wc -l)
98-
if [ "$before" != "$after" ]; then
99-
echo "::error::pom_dlineage.xml changed the root build's target/classes ($before -> $after); see issue #39"
100-
exit 1
101-
fi
102-
ls -l target-dlineage/*.jar
87+
# The standalone lineage tool, run rather than merely built. It was its own
88+
# POM until 2026-07-28, and only ever compiled here, which is how it
89+
# shipped a missing JAXB dependency (issue #47) and an unrunnable
90+
# documented command (issue #46) through green builds.
91+
- name: Run the standalone dlineage jar
92+
run: .github/scripts/smoke-dlineage-jar.sh
10393

10494
- name: Every demo starts
10595
run: .github/scripts/run-all-demos.sh --timeout 90
@@ -172,22 +162,13 @@ jobs:
172162
run: .github/scripts/check-test-results.sh
173163

174164

175-
# A second, separate build in the same directory that nothing used to run.
176-
# It carried a <parent> pointing at the private gudusoft:gsp_java reactor,
177-
# so it only built where that POM happened to be installed. Also asserts it
178-
# leaves the root build's target/classes alone (issue #39).
179-
- name: Build the separate dlineage POM against the newest release
180-
shell: bash
181-
run: |
182-
set -euo pipefail
183-
before=$(find target/classes -name '*.class' | wc -l)
184-
mvn -B -f pom_dlineage.xml package -DskipTests -Dgsp.core.version=${{ steps.ver.outputs.version }}
185-
after=$(find target/classes -name '*.class' | wc -l)
186-
if [ "$before" != "$after" ]; then
187-
echo "::error::pom_dlineage.xml changed the root build's target/classes ($before -> $after); see issue #39"
188-
exit 1
189-
fi
190-
ls -l target-dlineage/*.jar
165+
# Same smoke run as the pinned job, against the jar "Compile everything"
166+
# just built with -Dgsp.core.version=<newest release>. This is the check
167+
# that would notice a new parser breaking data lineage at runtime while
168+
# still compiling, which is precisely the failure mode a build-only step
169+
# cannot see.
170+
- name: Run the standalone dlineage jar against the newest release
171+
run: .github/scripts/smoke-dlineage-jar.sh
191172

192173
- name: Every demo starts
193174
env:
@@ -257,7 +238,7 @@ jobs:
257238
--title "Bump the parser from $pinned to $NEW" \
258239
--body "\`$NEW\` is the newest release on sqlparser.com. Tonight's nightly ran it through the same checks the pinned \`$pinned\` gets, and it passed all of them: the full test suite with no failures, every demo starting, and every case in \`.github/scripts/demo-cases.tsv\` producing its expected output.
259240
260-
This touches the five places the version is written -- \`pom.xml\`, \`pom_dlineage.xml\` and the three \`connector/*/pom.xml\` -- via \`.github/scripts/set-parser-version.sh\`, so they cannot drift apart.
241+
This touches the four places the version is written -- \`pom.xml\` and the three \`connector/*/pom.xml\` -- via \`.github/scripts/set-parser-version.sh\`, so they cannot drift apart.
261242
262243
Merge to move the demos to \`$NEW\`. Close to stay on \`$pinned\`; the nightly will go on testing \`$NEW\` and will not reopen this until a newer release appears.
263244

‎.gitignore‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ rebel.xml
5757

5858
# Maven target
5959
**/target
60-
target-dlineage
6160

6261
*.iml
6362

@@ -68,12 +67,9 @@ CLAUDE.md
6867

6968

7069

71-
# generated by src/main/java/.../dlineage/buildJar.sh
72-
src/main/java/gudusoft/gsqlparser/demos/dlineage/class/
73-
src/main/java/gudusoft/gsqlparser/demos/dlineage/data_flow_analyzer.jar
74-
75-
# populated by the .bat workflow, not vendored:
76-
# mvn dependency:copy -Dartifact=com.gudusoft:gsqlparser:<ver> -DoutputDirectory=external_lib
70+
# populated by the .bat workflow, not vendored. setenv/fetch-parser.bat runs:
71+
# mvn dependency:copy-dependencies -DoutputDirectory=external_lib -DincludeScope=runtime
72+
# Delete the directory to force a refetch after changing a dependency.
7773
external_lib/
7874

7975
# javac output dir used by compile_<demo>.bat

0 commit comments

Comments
 (0)