Skip to content

Commit 697137d

Browse files
Antoine Leblanchasura-bot
authored andcommitted
Fix Postgres not padding timestamps correctly (fix hasura#8096)
## Description As identified in hasura#8096, the format string we used for timestamps was incorrect; we were using `%F`, which expands to `%Y-%m-%d`; but that meant that the year was not padded to four digits: `0001` would be represented simply as `1`. However, Postgres inteprets that `1` as `2001`, probably due to interpretation rules about two-digit years (in `25/12/01`, `01` is indeed `2001`). ``` # create table timestamp_test ( test timestamptz ); CREATE TABLE # insert into timestamp_test values ('1-01-01T00:00:57Z'); INSERT 0 1 # select * from timestamp_test; test ------------------------ 2001-01-01 00:00:57+00 (1 row) ``` To fix this, this PR changes the format string to use `%0Y`, which always pads the year number with zeroes. ## Remaining work - [x] write Changelog entry - [ ] copy timestamp tests from the python suite into the hspec tests PR-URL: hasura/graphql-engine-mono#3536 GitOrigin-RevId: fa144111358339fd4a35b32d888c1d2c5b418ea6
1 parent d1f8b9a commit 697137d

4 files changed

Lines changed: 114 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Bug fixes and improvements
66

7+
- server: fixed a bug where timestamp values sent to postgres would erroneously trim leading zeroes (#8096)
78
- server: fix bug when event triggers where defined on tables that contained non lower-case alphabet characters
89
- server: avoid encoding 'varchar' values to UTF8 in MSSQL backends
910
- server: add support for MSSQL event triggers (#7228)

server/graphql-engine.cabal

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,9 +1091,9 @@ test-suite tests-hspec
10911091
Harness.Backend.Sqlserver
10921092

10931093
-- Harness.Test
1094+
Harness.Test.BackendType
10941095
Harness.Test.Context
10951096
Harness.Test.Schema
1096-
Harness.Test.BackendType
10971097

10981098
-- Harness.Quoter
10991099
Harness.Quoter.Graphql
@@ -1107,30 +1107,31 @@ test-suite tests-hspec
11071107
Test.CustomFieldNamesSpec
11081108
Test.DirectivesSpec
11091109
Test.HelloWorldSpec
1110+
Test.InsertCheckPermissionSpec
1111+
Test.InsertEnumColumnSpec
11101112
Test.LimitOffsetSpec
11111113
Test.NestedRelationshipsSpec
11121114
Test.ObjectRelationshipsLimitSpec
11131115
Test.ObjectRelationshipsSpec
11141116
Test.OrderingSpec
1117+
Test.PostgresTypesSpec
11151118
Test.PrimaryKeySpec
1116-
Test.RemoteRelationship.MetadataAPI.Common
1119+
Test.RemoteRelationship.FromRemoteSchemaSpec
11171120
Test.RemoteRelationship.MetadataAPI.ClearMetadataSpec
1121+
Test.RemoteRelationship.MetadataAPI.Common
11181122
Test.RemoteRelationship.MetadataAPI.DropSource.DBtoDBRelationshipSpec
11191123
Test.RemoteRelationship.MetadataAPI.DropSource.RSToDBRelationshipSpec
1120-
Test.RemoteRelationship.FromRemoteSchemaSpec
11211124
Test.RemoteRelationship.XToDBArrayRelationshipSpec
11221125
Test.RemoteRelationship.XToDBObjectRelationshipSpec
11231126
Test.RemoteRelationship.XToRemoteSchemaRelationshipSpec
11241127
Test.RequestHeadersSpec
1125-
Test.SerializationSpec
1128+
Test.RunSQLSpec
1129+
Test.SQLServer.InsertVarcharColumnSpec
11261130
Test.SelectSpec
1131+
Test.SerializationSpec
11271132
Test.ServiceLivenessSpec
11281133
Test.ViewsSpec
11291134
Test.WhereSpec
1130-
Test.RunSQLSpec
1131-
Test.InsertCheckPermissionSpec
1132-
Test.InsertEnumColumnSpec
1133-
Test.SQLServer.InsertVarcharColumnSpec
11341135

11351136
test-suite tests-dc-api
11361137
import: common-all, common-exe

server/src-lib/Hasura/Backends/Postgres/SQL/Value.hs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,8 @@ pgScalarValueToJson = \case
120120
PGValText t -> toJSON t
121121
PGValCitext t -> toJSON t
122122
PGValDate d -> toJSON d
123-
PGValTimeStamp u ->
124-
toJSON $ formatTime defaultTimeLocale "%FT%T%QZ" u
125-
PGValTimeStampTZ u ->
126-
toJSON $ formatTime defaultTimeLocale "%FT%T%QZ" u
123+
PGValTimeStamp u -> String $ formatTimestamp u
124+
PGValTimeStampTZ u -> String $ formatTimestamp u
127125
PGValTimeTZ (ZonedTimeOfDay tod tz) ->
128126
toJSON (show tod ++ timeZoneOffsetString tz)
129127
PGNull _ -> Null
@@ -243,10 +241,8 @@ txtEncodedVal = \case
243241
PGValText t -> TELit t
244242
PGValCitext t -> TELit t
245243
PGValDate d -> TELit $ T.pack $ showGregorian d
246-
PGValTimeStamp u ->
247-
TELit $ T.pack $ formatTime defaultTimeLocale "%FT%T%QZ" u
248-
PGValTimeStampTZ u ->
249-
TELit $ T.pack $ formatTime defaultTimeLocale "%FT%T%QZ" u
244+
PGValTimeStamp u -> TELit $ formatTimestamp u
245+
PGValTimeStampTZ u -> TELit $ formatTimestamp u
250246
PGValTimeTZ (ZonedTimeOfDay tod tz) ->
251247
TELit $ T.pack (show tod ++ timeZoneOffsetString tz)
252248
PGNull _ ->
@@ -332,6 +328,9 @@ binEncoder = \case
332328
PGValLtxtquery t -> Q.toPrepVal t
333329
PGValUnknown t -> (PTI.auto, Just (TE.encodeUtf8 t, PQ.Text))
334330

331+
formatTimestamp :: FormatTime t => t -> Text
332+
formatTimestamp = T.pack . formatTime defaultTimeLocale "%0Y-%m-%dT%T%QZ"
333+
335334
txtEncoder :: PGScalarValue -> S.SQLExp
336335
txtEncoder colVal = case txtEncodedVal colVal of
337336
TENull -> S.SENull
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{-# LANGUAGE QuasiQuotes #-}
2+
{-# LANGUAGE RecordWildCards #-}
3+
4+
-- | Tests around Postgres-specific database types
5+
module Test.PostgresTypesSpec (spec) where
6+
7+
import Harness.Backend.Postgres as Postgres
8+
import Harness.GraphqlEngine qualified as GraphqlEngine
9+
import Harness.Quoter.Graphql
10+
import Harness.Quoter.Sql
11+
import Harness.Quoter.Yaml
12+
import Harness.Test.Context qualified as Context
13+
import Harness.TestEnvironment (TestEnvironment)
14+
import Test.Hspec
15+
import Prelude
16+
17+
--------------------------------------------------------------------------------
18+
-- Preamble
19+
20+
spec :: SpecWith TestEnvironment
21+
spec =
22+
Context.run
23+
[ Context.Context
24+
{ name = Context.Backend Context.Postgres,
25+
mkLocalTestEnvironment = Context.noLocalTestEnvironment,
26+
setup = postgresSetup,
27+
teardown = postgresTeardown,
28+
customOptions = Nothing
29+
}
30+
]
31+
tests
32+
33+
--------------------------------------------------------------------------------
34+
-- Postgres
35+
36+
postgresSetup :: (TestEnvironment, ()) -> IO ()
37+
postgresSetup (testEnvironment, _) = do
38+
-- Clear and reconfigure the metadata
39+
GraphqlEngine.setSource testEnvironment Postgres.defaultSourceMetadata
40+
41+
-- Setup tables
42+
Postgres.run_
43+
[sql|
44+
create table timestamp_test (
45+
id serial primary key,
46+
time timestamptz
47+
);
48+
|]
49+
50+
-- Track the tables
51+
GraphqlEngine.post_
52+
testEnvironment
53+
"/v1/metadata"
54+
[yaml|
55+
type: postgres_track_table
56+
args:
57+
source: postgres
58+
table:
59+
schema: hasura
60+
name: timestamp_test
61+
|]
62+
63+
postgresTeardown :: (TestEnvironment, ()) -> IO ()
64+
postgresTeardown _ = do
65+
Postgres.run_
66+
[sql|
67+
DROP TABLE hasura.timestamp_test;
68+
|]
69+
70+
--------------------------------------------------------------------------------
71+
-- Tests
72+
73+
tests :: Context.Options -> SpecWith TestEnvironment
74+
tests opts = do
75+
it "doesn't mess up timestamps on insertion" $ \testEnvironment ->
76+
shouldReturnYaml
77+
opts
78+
( GraphqlEngine.postGraphql
79+
testEnvironment
80+
[graphql|
81+
mutation {
82+
insert_hasura_timestamp_test(objects:[{
83+
time: "0001-01-01T00:00:57Z"
84+
}]) {
85+
returning {
86+
time
87+
}
88+
}
89+
}
90+
|]
91+
)
92+
[yaml|
93+
data:
94+
insert_hasura_timestamp_test:
95+
returning:
96+
- time: "0001-01-01T00:00:57+00:00"
97+
|]

0 commit comments

Comments
 (0)