Comments (5)
I believe this is ultimately a side effect of your use of .date
as the field type used for the birthday
field in the Fluent migration - the Fluent Postgres driver maps .date
to Postgres's DATE
type. However, the Date
encoding logic in PostgresNIO unconditionally sends Date
values to the server encoded as binary-format TIMESTAMP WITH TIME ZONE
(aka timestamptz
) values. Although the database type says "with time zone", the format is actually a count of seconds since 2000-01-01T00:00:00Z
, always in UTC. Postgres then performs the appropriate time zone adjustment when implicitly coercing the timestamptz
input to the column's date
type. The reverse does not take place when the values are loaded from the database because Postgres will send it as a DATE
(reasonably enough), which does not include the time zone offset needed to perform the inverse adjustment (nor is there, for that matter, any reliable indicator that such an adjustment is necessary by that point).
In short, Fluent and PostgresNIO both act, of necessity, on the assumption that Date
s are associated with TIMESTAMPTZ
columns (in your migration, change .date
to .datetime
). In fact, it is safe to say as a general statement that Fluent is not guaranteed to handle either DATE
or TIME
correctly regardless of which database driver one is using (for example, with MySQL, TIME
columns just flat-out don't work at all).
Note
If you really do need a DATE
column rather than a TIMESTAMPTZ
for your use case (I have found this to be rare in practice, but YMMV of course), you have a few options:
- Use SQLKit , and explicitly specify the column in the SQL as
"birthday"::"timestamptz"
, so that Postgres performs the reverse conversion for you. (I dare say this would rather defeat the purpose of the question, however.) - Sidestep the assumptions made by the
Date
-based logic by using one of the alternateTimestampFormat
s available with the@Timestamp
property wrapper - theISO8601TimestampFormat
would work nicely if you tweaked it to allow you configure it with the database's time zone (getting that info out of the database is left as an exercise for the reader 😅). - The second solution ignores that
@Timestamp
would be better named@OptionalTimestamp
, since it requires the value beDate?
rather than justDate
. This in turn is easily fixed: write a separate wrapper that doesn't require that, as shown by this sample code. This is working code that can be dropped into your project and used as-is (except that you will have to delete the last line of the file). I've used it without issue in production systems; the only reason I never included it in Fluent is it's not a common enough need to be worth adding even more confusion, especially when its naming is inconsistent 😅.
If none of those works for you, I'm afraid you're out of luck - we can't even look into changing Fluent's existing behavior in this respect unless we can also figure out a way to handle it better in PostgresNIO first (and even then the changes would end up happening in Fluent 5).
from postgres-nio.
(At the end of the day, it's just another "ORMs are the reason anyone who doesn't work with either audio equipment or electronics has ever even heard of an impedance mismatch" thing, and the last-second choice to shoehorn support for MongoDB into Fluent 4 immediately before the GM release elevated the mismatch to what I'd call almost a flat-out incompatibility after 3 or 4 years of fighting with it 😆. At least I can blame that particular call on @tanner0101, I was against it at the time 🤣.)
from postgres-nio.
Thank you for lightning-fast response. I now better understand where the issue is, and also recognize that changing Fluent 4 to make DATE
(i.e., .date
) work would also probably mean breaking existing applications that already found their own workarounds.
As to why the importance of DATE
(you mention you find this rare in practice)... I agree, but only if we assume we create the schemas ourselves and have the power to choose the field type. Sometimes we need to work with existing databases for which we can only use DML, but no DDL.
In any case, I appreciate your very detailed reply. You have given me many options to work with! Thank you! 😀
from postgres-nio.
Let's just say that this sort of issue, and the difficulty in addressing it in any helpful fashion even if Fluent 4's API could be more freely changed, is a notable entry on the long list of reasons that the very preliminary design work I've finally started for Fluent 5 centers around a much more SQLKit-like API than what we have now 😅.
Oh, and I forgot - there is one other option you could go with:
- Define a new type - or, in fact, for this example, let's use Foundation's
DateComponents
type, which is the closest thing Swift or Foundation has to a "date-only" or "time-only" representation in any case. The intent is toimport PostgresNIO
, and conform the type (whether a custom one or not) to thePostgresDecodable
andPostgresNonThrowingEncodable
protocols. Those implementations can then do what the same conformances that exist forDate
do not: recognize Postgres'sPostgresDataType.date
,.time
,.timestamp
, and.timestamptz
types inPostgresDataFormat.binary
format during decoding and choosing the most appropriate one to use during encoding. (Be careful - these are NOT the same as Fluent'sDatabaseSchema.DataType.time
,.date
,.datetime
etc. types; the Postgres ones directly refer to Postgres type OIDs, as exchanged via the wire protocol.) How you would handle the time zone adjustment for.date
. and.timestamp
is up to you - you can't perform a query during encoding or decoding, nor can you add a typecast to the query (although you could tweakRequiredTimestampProperty
'sAnyDatabaseProperty
conformance so that theDatabaseQuery.Value
s saved toDatabaseInput
and read fromDatabaseOutput
used the.custom()
case with an appropriateSQLExpression
... but that gets even more complicated 😅). Once you have a type conforming to thePostgresCodable
protocols, you can just use that type as the value of an@Field
property, and Fluent (via FluentPostgresDriver, then PostgresKit) will eventually call your implementations at the appropriate times 😅. Have a look atDate+PostgresCodable.swift
in PostgresNIO for an idea of what's needed.
(Or, you know, just use TIMESTAMPTZ
😂 😂 😂 But you're absolutely right that that isn't always a viable option.)
from postgres-nio.
I really like this new option. Probably too much for my project, because I can really go with TIMESTAMPTZ
or simply change the timezone of the database. However, I still want to try PostgresDecodable
, if only as an excercise to better understand the whole process. Thanks again! 😁
from postgres-nio.
Related Issues (20)
- Allow PostgresConnection creation without specifying an EventLoop
- `serverClosedConnection` error can never occur
- Merge `PostgresDynamicTypeThrowingEncodable` with `PostgresEncodable`
- Investigate adopting Tracing HOT 1
- Do not require passing a logger on every call HOT 1
- 1.18 crashes on Linux HOT 10
- CI: Investigate running tests in release mode HOT 1
- Successfully closing connection logs `PSQLError`
- Composable PostgresQuery HOT 4
- Allow changing Postgres auth credentials while ConnectionPool is alive HOT 2
- Support marking connection as going away
- Support changing the number of allowed streams on a connection HOT 1
- Error "How can we receive a read, if the connection is closed" HOT 7
- Double ping pong that crashes the app. HOT 2
- Connection Pool Crash: Precondition failure when ping and close happen at the same time
- ConnectionPool: Allow connection scoring/preference, for example for a certain EventLoop HOT 1
- Fatal error: How can we receive a read, if the connection is closed HOT 3
- TIMESTAMP columns don't return the same value as was inserted HOT 3
- PostgresNIO/ListenStateMachine.swift:182: Fatal error: Invalid state: initialized HOT 12
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from postgres-nio.