Support PostgreSQL: replace SQLite-only SQL functions and alias-in-HAVING - #17
Open
steveneppler wants to merge 3 commits into
Open
Support PostgreSQL: replace SQLite-only SQL functions and alias-in-HAVING#17steveneppler wants to merge 3 commits into
steveneppler wants to merge 3 commits into
Conversation
Keeps the multi-database SQL fix intact while dropping changes unrelated to it, so the diff shows only the behavioral change: - Restore the trailing newline on the seven touched files (.editorconfig sets insert_final_newline, and their absence made three previously Pint-clean files dirty) - Revert unrelated reformatting: CSV header array layout and concat spacing in ExportController, arrow-fn and brace style in Dashboard, method-chain indentation in Debug - Revert the incidental package-lock.json change - DatabaseHelper: PSR-12 indentation, docblock, and a default arm that throws InvalidArgumentException instead of UnhandledMatchError on an unrecognized format Verified against PostgreSQL 16 and SQLite: all rewritten queries return identical results on both drivers, and /analytics renders 200 on Postgres (it 500s on main). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRjmmn5mw7HSdtXiLHsoHs
…ression teslog:process-states failed on PostgreSQL when a charging session's first state had no GPS fix: the nearby-state lookup ordered by ABS(JULIANDAY(timestamp) - JULIANDAY(?)), and julianday() exists only in SQLite. Adds DatabaseHelper::absTimeDiffSeconds(), which returns the absolute distance in seconds between a timestamp column and a bound parameter, expressed per driver (EXTRACT(EPOCH FROM ...) on pgsql, JULIANDAY on sqlite, TIMESTAMPDIFF elsewhere). Verified on PostgreSQL 16 and SQLite by seeding a charging session whose states lack GPS: the command previously threw "function julianday(timestamp without time zone) does not exist" on Postgres, and now creates the charge and resolves the same coordinates from the nearby state on both drivers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRjmmn5mw7HSdtXiLHsoHs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15.
Builds on @lumberjacklv's work in #16 (his commit is preserved here), rebased onto current
mainafter #14 landed, with formatting cleanups and one additional PostgreSQL fix.The problem
/analyticsreturns HTTP 500 on PostgreSQL. Issue #15 identified theHAVINGclause inTempEfficiencyChart, but that is one of four independent breakages — the samestrftimeproblem also takes down the Dashboard and Debug pages, andteslog:process-statesfails on a separate SQLite-only function.Every failure is in raw SQL (
DB::raw,selectRaw,whereRaw,orderByRaw), which Laravel passes through to the driver verbatim, plus one reliance on a non-standard dialect extension. Standard SQL in the app — window functions in the map queries,DATE(), the aggregates, all migrations — already worked on PostgreSQL untouched.Changes
DatabaseHelper(new) — driver-specific SQL for constructs with no cross-database equivalent:formatDateTime($column, $format)—hour/year-month/datetime, emittingdate_trunc/to_charon pgsql,strftimeon sqlite,DATE_FORMATelsewhere. Replaces all 11 hardcodedstrftimecall sites.absTimeDiffSeconds($column)— absolute distance in seconds between a timestamp column and a bound parameter, viaEXTRACT(EPOCH FROM ...)on pgsql,JULIANDAYon sqlite,TIMESTAMPDIFFelsewhere.TempEfficiencyChart—having('cnt', '>=', 3)→havingRaw('COUNT(*) >= 3'). Referencing a select alias inHAVINGis a SQLite/MySQL extension that PostgreSQL does not accept. (Note: the fix suggested in #15,having('COUNT(*)', '>=', 3), would not work — Laravel quotes the first argument as an identifier, producinghaving "COUNT(*)" >= 3.)ProcessVehicleStates— the nearby-state lookup usedABS(JULIANDAY(timestamp) - JULIANDAY(?))to order states by proximity in time.julianday()exists only in SQLite. This path triggers when a charging session's first state has no GPS fix, which is common since charging states often lack coordinates.Verification
Tested against a live PostgreSQL 16 instance with migrations run and a seeded dataset (63 vehicle states, 10 drives, 410 drive points, 10 charges, idles, battery health, telemetry rows).
Before, on PostgreSQL:
42883: function strftime(unknown, timestamp without time zone) does not exist42703: column "cnt" does not exist(issue #15)strftimedoes not existstrftimedoes not existteslog:process-states42883: function julianday(timestamp without time zone) does not existAfter — full sweep of 19 authenticated routes and 8 commands, run on both PostgreSQL and SQLite:
/analyticspreviously 500 on pgsql)teslog:*commands exit 0 on both driversteslog:process-stateswas exercised with a seeded GPS-less charging session to force theJULIANDAYpath; it now creates the charge and resolves identical coordinates from the nearby state on both driversCostCharts,ExportController) are clean again, and the newDatabaseHelperis cleanNotes for review
.editorconfigsetsinsert_final_newline), CSV header array reformatting, concat spacing, arrow-fn and brace style, and an incidentalpackage-lock.jsonedit. Net diff is 79/14 rather than 122/48, so the behavioral change is the whole diff.DatabaseHelperinterpolates$columninto raw SQL. All call sites pass hard-coded names; the docblocks say so explicitly.formatDateTimehas adefaultarm throwingInvalidArgumentExceptionrather than surfacing anUnhandledMatchErroron a typo'd format.BackupDatabase,RestoreDatabase, and thesettings/backuproute are intentionally SQLite-only and already guard onconfig('database.default'), failing cleanly rather than erroring. Left as-is.LIKEis case-sensitive on PostgreSQL but case-insensitive for ASCII on SQLite, so the Debug page's field filter matches differently across drivers. This is a behavior difference rather than an error, and picking the intended semantics is a product decision — flagging rather than changing it.🤖 Generated with Claude Code
https://claude.ai/code/session_01NRjmmn5mw7HSdtXiLHsoHs
Generated by Claude Code