1
00:00:00,060 --> 00:00:02,280
Michael: Hello, hello and welcome
to Postgres.FM, weekly show

2
00:00:02,280 --> 00:00:03,340
about all things PostgreSQL.

3
00:00:03,520 --> 00:00:04,900
I am Michael, founder of pgMustard.

4
00:00:04,960 --> 00:00:07,700
I'm joined as usual by Nikolay,
founder of Postgres.AI.

5
00:00:07,700 --> 00:00:08,400
Hey Nikolay.

6
00:00:08,640 --> 00:00:09,360
Nikolay: Hey Michael.

7
00:00:09,360 --> 00:00:10,220
How are you?

8
00:00:10,380 --> 00:00:11,460
Michael: Nope, not falling for
that.

9
00:00:11,460 --> 00:00:16,240
And we are joined by a wonderful
guest, Lukas Eder, founder of

10
00:00:16,240 --> 00:00:20,820
Data Geekery and the creator of
jOOQ, a very popular framework.

11
00:00:21,100 --> 00:00:22,860
Thank you so much for joining us,
Lukas.

12
00:00:23,400 --> 00:00:24,840
Lukas: Thank you for having me,
Michael.

13
00:00:25,680 --> 00:00:26,780
Michael: It's our pleasure.

14
00:00:27,260 --> 00:00:31,080
So, how do you describe jOOQ and
what's it helping folks with?

15
00:00:31,160 --> 00:00:33,640
Lukas: So, jOOQ is an internal
domain-specific language written

16
00:00:33,640 --> 00:00:37,280
in Java where it models the SQL
language directly as a Java API.

17
00:00:37,900 --> 00:00:41,940
This helps Java developers to write
SQL queries more efficiently

18
00:00:42,180 --> 00:00:44,940
and more correctly within their
Java applications.

19
00:00:45,060 --> 00:00:48,860
There's a code generator that generates
the entire schema as

20
00:00:48,860 --> 00:00:51,740
Java objects and you can interact
with those objects and create

21
00:00:51,740 --> 00:00:52,800
type safe queries.

22
00:00:53,220 --> 00:00:55,240
That's the base of it.

23
00:00:55,520 --> 00:00:56,520
Michael: Yeah, it's super cool.

24
00:00:56,520 --> 00:01:00,040
But I'm super impressed with how
much you focus on developer

25
00:01:00,060 --> 00:01:01,160
experience in general.

26
00:01:01,380 --> 00:01:04,220
I hear quite a few people say they
really love jOOQ and they

27
00:01:04,220 --> 00:01:07,280
give about 15 different examples
of things they love about it.

28
00:01:07,280 --> 00:01:10,460
But what are the main things you
think that people particularly

29
00:01:10,520 --> 00:01:11,320
like about it?

30
00:01:11,320 --> 00:01:14,160
Lukas: Well, the first thing is,
and I've studied the market

31
00:01:14,160 --> 00:01:17,900
extensively before making jOOQ
and the first thing is it really

32
00:01:17,900 --> 00:01:22,900
looks as much as SQL as it's possible
to do when you model an

33
00:01:22,900 --> 00:01:24,900
internal DSL API in Java.

34
00:01:25,080 --> 00:01:26,620
So, of course, there's some limitations.

35
00:01:27,040 --> 00:01:30,020
But when you have in mind there's
a group by clause, you just

36
00:01:30,020 --> 00:01:32,960
start typing group by where you
expect it, and then it just starts

37
00:01:32,960 --> 00:01:33,460
compiling.

38
00:01:33,520 --> 00:01:34,660
So there's no surprises.

39
00:01:35,060 --> 00:01:37,860
There's almost no surprises in
terms of SQL syntax.

40
00:01:38,140 --> 00:01:41,180
So if you know SQL, then you immediately
know the jOOQ API.

41
00:01:41,600 --> 00:01:43,300
You don't really have to learn
it.

42
00:01:43,460 --> 00:01:47,220
I guess that's 1 of the most popular
things, But other than that,

43
00:01:47,220 --> 00:01:50,820
it's also very nicely integrated
into the Java language or Kotlin

44
00:01:50,820 --> 00:01:52,580
and Scala, that works as well.

45
00:01:52,900 --> 00:01:59,740
And if you've ever coded PL SQL
in Oracle or PL/pgSQL in Postgres,

46
00:02:00,240 --> 00:02:03,160
You kind of like the way how the
SQL language is embedded in

47
00:02:03,160 --> 00:02:04,280
the procedural language.

48
00:02:04,280 --> 00:02:08,940
So if you loop, if you iterate
over a result set in those procedural

49
00:02:09,000 --> 00:02:13,160
languages, you have the same kind
of type safety and you have

50
00:02:13,500 --> 00:02:16,480
the possibility, for instance,
to insert bind variables into

51
00:02:16,480 --> 00:02:19,120
your statements at the right spot,
and you don't have to worry

52
00:02:19,120 --> 00:02:22,240
about the infrastructure and the
logistics of connecting to the

53
00:02:22,240 --> 00:02:24,100
database, and it just feels embedded.

54
00:02:24,440 --> 00:02:27,900
And I think that's what a lot of
people really want to do, even

55
00:02:27,900 --> 00:02:31,280
more than having the type safety,
is this feeling of embeddedness

56
00:02:31,560 --> 00:02:32,940
into the target language.

57
00:02:32,980 --> 00:02:36,880
And when you map your result sets
to Java objects, that also

58
00:02:36,880 --> 00:02:38,000
feels very native.

59
00:02:38,000 --> 00:02:41,620
So it feels like the database is
part of the Java of the JVM.

60
00:02:42,720 --> 00:02:43,080
Michael: Yeah.

61
00:02:43,080 --> 00:02:46,360
Even to the point of getting like
auto-complete suggestions in

62
00:02:46,360 --> 00:02:49,520
IDEs, but like that is super nice.

63
00:02:50,180 --> 00:02:50,680
Lukas: Yeah.

64
00:02:50,740 --> 00:02:52,200
There are little details like this.

65
00:02:52,200 --> 00:02:55,640
So when you say autocomplete, there's
also possibility to comment

66
00:02:55,640 --> 00:02:58,900
on your database tables and comments
and that translates to Java

67
00:02:58,900 --> 00:02:59,400
doc.

68
00:02:59,440 --> 00:03:02,620
So whatever you comment directly
in the database, it's translated

69
00:03:02,640 --> 00:03:06,500
to documentation inside of your
job program you hardly ever have

70
00:03:06,500 --> 00:03:08,860
to go to the database to see what's
there so.

71
00:03:10,680 --> 00:03:11,180
Michael: Nice.

72
00:03:11,480 --> 00:03:11,980
Lukas: Yeah.

73
00:03:12,560 --> 00:03:15,620
Michael: In terms of the history
how like I did look it up in

74
00:03:15,620 --> 00:03:18,160
or at least the GitHub history
How long have you been working

75
00:03:18,160 --> 00:03:19,780
on it and how does that feel?

76
00:03:20,580 --> 00:03:23,360
Lukas: I think the first public
version started in 2009.

77
00:03:24,060 --> 00:03:28,580
There were prototypes before that
in 2008 I think but 2009 was

78
00:03:28,580 --> 00:03:31,840
the first public version, not on
GitHub then, it wasn't first

79
00:03:31,840 --> 00:03:32,340
port.

80
00:03:32,360 --> 00:03:34,900
I don't think GitHub was very popular
at the time.

81
00:03:35,380 --> 00:03:36,240
And then Subversion.

82
00:03:37,360 --> 00:03:39,020
That's how old it is already.

83
00:03:39,780 --> 00:03:43,940
I had the idea already back during
my university studies at EPFL

84
00:03:43,940 --> 00:03:44,600
in Lausanne.

85
00:03:45,180 --> 00:03:49,580
There was a professor who said
you could model any kind of programming

86
00:03:49,640 --> 00:03:51,560
language as an internal DSL.

87
00:03:51,680 --> 00:03:53,660
I'm not sure if you use that term
yet.

88
00:03:53,920 --> 00:03:56,180
I think DSL was more coined by...

89
00:03:57,180 --> 00:04:00,660
More recently, but he said you
could model any language in terms

90
00:04:00,660 --> 00:04:04,340
of an API if you just have Java
interfaces that return interfaces

91
00:04:04,500 --> 00:04:05,660
that are return interfaces.

92
00:04:05,820 --> 00:04:09,380
And this kind of idea stuck with
me for all this time, but I

93
00:04:09,380 --> 00:04:11,080
didn't have a use case back then.

94
00:04:11,460 --> 00:04:16,320
And when I started working in the
industry, in 2006 was my first

95
00:04:16,320 --> 00:04:17,180
Java job.

96
00:04:17,220 --> 00:04:20,440
I did some PHP work before, but
Java started in 2006.

97
00:04:21,340 --> 00:04:25,080
I've seen that all the companies
that I've applied to, and even

98
00:04:25,080 --> 00:04:29,700
the 1 that I worked at, they implemented
dynamic SQL libraries

99
00:04:30,400 --> 00:04:32,900
because this was, this was something
that everyone needs, right?

100
00:04:32,900 --> 00:04:34,980
So everyone has dynamic SQL queries.

101
00:04:35,020 --> 00:04:39,140
When you have a UI with various
input boxes and they're all optional,

102
00:04:39,140 --> 00:04:41,980
so you have to compose your SQL
queries in a dynamic way.

103
00:04:42,100 --> 00:04:44,860
And everyone wants to avoid string
concatenation because it's

104
00:04:44,860 --> 00:04:46,220
not safe and it's boring.

105
00:04:46,560 --> 00:04:50,440
So they did that for both, they
had one for SQL and they had one

106
00:04:50,440 --> 00:04:53,640
for Hibernate query language, which
is essentially the same thing,

107
00:04:53,640 --> 00:04:56,100
just more limited, but at the same
concept.

108
00:04:56,740 --> 00:05:00,840
And no one, no one actually thought
about making this a true DSL.

109
00:05:00,860 --> 00:05:03,600
It's well, it was always just a
query builder with some weird

110
00:05:03,600 --> 00:05:04,100
API.

111
00:05:04,120 --> 00:05:09,280
So you had Java style methods where
you add clauses to the query

112
00:05:09,280 --> 00:05:09,740
object.

113
00:05:09,740 --> 00:05:12,480
And it didn't feel like SQL.

114
00:05:12,500 --> 00:05:16,180
It felt like a tool to solve exactly
this dynamic SQL problem,

115
00:05:16,440 --> 00:05:19,940
but it didn't feel like you didn't
enjoy using this thing.

116
00:05:19,940 --> 00:05:23,600
And I think JPA still has this
thing with their criteria API,

117
00:05:23,600 --> 00:05:26,980
which is still very useful if you
want to do dynamic JPQL.

118
00:05:27,700 --> 00:05:31,220
But I've never heard anyone enjoy
using that API because it's

119
00:05:31,720 --> 00:05:33,740
just, you have to learn it.

120
00:05:33,740 --> 00:05:37,940
So you have to learn one more thing
and it only serves this one purpose

121
00:05:37,960 --> 00:05:39,960
or at least that's how, what it
feels like.

122
00:05:39,960 --> 00:05:42,460
It serves this purpose of dynamic
JPQL.

123
00:05:43,780 --> 00:05:46,400
And that's when I started prototyping
it.

124
00:05:46,600 --> 00:05:50,460
And in the very beginning, I had
to first implement the infrastructure

125
00:05:50,580 --> 00:05:56,320
wasn't very diesel style API either
so I created the query builder

126
00:05:56,320 --> 00:06:00,260
that could do some select from
where very simple conditions But

127
00:06:00,260 --> 00:06:04,180
then I started to really implement
this idea because I remembered,

128
00:06:04,180 --> 00:06:07,540
okay, this professor, he had this
idea and I'm going to try this.

129
00:06:07,540 --> 00:06:08,600
Is it really possible?

130
00:06:09,520 --> 00:06:10,920
And it turns out it was.

131
00:06:10,920 --> 00:06:14,700
And it's really crazy how many
SQL statements today we have in

132
00:06:14,700 --> 00:06:19,640
Juke that are all using this kind
of approach where you just

133
00:06:19,640 --> 00:06:22,420
start with the same object and
then you start typing and you

134
00:06:22,420 --> 00:06:27,440
auto complete your SQL syntax and
it's all one huge graph of methods

135
00:06:27,440 --> 00:06:29,880
that helps you construct your SQL
queries.

136
00:06:30,480 --> 00:06:33,720
So Juke has come a long way since
2009 I'd say.

137
00:06:34,340 --> 00:06:35,140
Michael: Yeah, right.

138
00:06:35,380 --> 00:06:39,060
I was looking in your documentation
about how many different,

139
00:06:40,080 --> 00:06:44,060
how many different, you call them
families of SQL dialects that

140
00:06:44,060 --> 00:06:44,680
you support.

141
00:06:44,680 --> 00:06:47,200
So how many different databases,
but also the different versions

142
00:06:47,200 --> 00:06:50,180
of different databases that may
or may not support different

143
00:06:50,660 --> 00:06:52,660
syntax and SQL features.

144
00:06:53,940 --> 00:06:55,820
How do you maintain that?

145
00:06:56,200 --> 00:06:59,140
Lukas: Well, first off, luckily
the database products are all

146
00:06:59,140 --> 00:07:02,760
very, very backwards compatible,
If we take one or two of them, we

147
00:07:02,760 --> 00:07:06,600
don't really care as much, but
most of them really value backwards

148
00:07:06,600 --> 00:07:07,580
compatibility a lot.

149
00:07:07,580 --> 00:07:11,040
So supporting new versions is just
looking at the new features

150
00:07:11,040 --> 00:07:13,980
and adding support for them, but
the old stuff still works.

151
00:07:13,980 --> 00:07:16,860
So at least that's already covered
there, more or less.

152
00:07:17,220 --> 00:07:20,600
But other than that, I mean, supporting
so many dialect per se

153
00:07:20,600 --> 00:07:24,140
is, yeah, it's a lot of work and
a lot of testing, of course,

154
00:07:24,140 --> 00:07:27,680
a lot of automated regression testing
to make sure nothing breaks.

155
00:07:28,260 --> 00:07:31,960
And also a Juke really values backwards
compatibility a lot,

156
00:07:31,960 --> 00:07:33,200
just like the database products.

157
00:07:33,200 --> 00:07:36,360
So when you embed the joke query
into your database application,

158
00:07:36,880 --> 00:07:38,320
you don't want stuff to break.

159
00:07:38,320 --> 00:07:42,260
So if I'm not allowed to break
stuff, I'm probably not breaking

160
00:07:42,260 --> 00:07:42,640
stuff.

161
00:07:42,640 --> 00:07:47,180
So this kind of simplifies maintenance
as well, because stuff

162
00:07:47,180 --> 00:07:51,220
that once works is probably not
going to break just like that.

163
00:07:51,220 --> 00:07:55,280
I mean there's some internal refactorings
but there are no really

164
00:07:55,280 --> 00:08:00,800
major, how to put it, major paradigm
shifts where everything

165
00:08:00,800 --> 00:08:04,120
works in a different way now and
I was breaking everything.

166
00:08:04,120 --> 00:08:07,700
So with automated regression tests,
it's not...

167
00:08:08,300 --> 00:08:10,480
It's hard, but it's not that hard,
I think.

168
00:08:10,960 --> 00:08:15,540
The hard part is just to find out
some weird syntax that implements

169
00:08:15,560 --> 00:08:18,220
a standard feature on this particular
dialect.

170
00:08:19,040 --> 00:08:23,680
And that's quite creative at times,
especially with the newer

171
00:08:23,680 --> 00:08:28,580
dialects that have just been released
recently that are not that

172
00:08:28,580 --> 00:08:32,060
mature yet, or Some dialects are
very opinionated.

173
00:08:32,320 --> 00:08:36,960
So you kind of have to think in
terms of why did they make those

174
00:08:36,960 --> 00:08:40,360
decisions and how can I map standard
SQL onto this database?

175
00:08:40,560 --> 00:08:43,580
But I think that's 1 of the things
that people really like when

176
00:08:43,580 --> 00:08:46,520
they work, especially when they
work with different database

177
00:08:46,520 --> 00:08:46,920
products.

178
00:08:46,920 --> 00:08:51,100
So if you have a company that works
with maybe Postgres and SQL

179
00:08:51,100 --> 00:08:54,020
server, you don't actually have
to think about the differences

180
00:08:54,020 --> 00:08:54,720
all the time.

181
00:08:54,720 --> 00:08:58,860
So you can just write substring
in the jOOQ API and you don't

182
00:08:58,860 --> 00:09:02,440
care if it's subster or inster
or whatever the database product

183
00:09:02,440 --> 00:09:03,260
calls it.

184
00:09:03,260 --> 00:09:06,760
It's just top string and jOOQ will
translate it for you.

185
00:09:07,080 --> 00:09:09,100
Nikolay: But there are differences
as well, right?

186
00:09:10,440 --> 00:09:12,260
And JSON is a big topic, right?

187
00:09:12,740 --> 00:09:13,480
Lukas: Like huge.

188
00:09:13,700 --> 00:09:14,200
JSON?

189
00:09:14,340 --> 00:09:14,840
Nikolay: Yeah.

190
00:09:15,320 --> 00:09:15,700
Yeah.

191
00:09:15,700 --> 00:09:17,250
Those functions and yeah.

192
00:09:17,250 --> 00:09:18,140
Lukas: It's crazy.

193
00:09:18,140 --> 00:09:21,360
I mean, Postgres was 1 of the first
to actually support JSON,

194
00:09:21,380 --> 00:09:22,860
but it didn't actually

195
00:09:22,990 --> 00:09:24,120
Nikolay: before standardizing

196
00:09:25,080 --> 00:09:25,460
Lukas: stuff.

197
00:09:25,460 --> 00:09:28,860
So someone has standardized it,
Oracle did a couple of years

198
00:09:28,860 --> 00:09:32,720
ago, and now Postgres started implementing
those standards from

199
00:09:32,720 --> 00:09:33,280
what I've seen.

200
00:09:33,280 --> 00:09:38,360
I've still not engaged with that
part yet, but you think now

201
00:09:38,360 --> 00:09:41,400
that there's a standard, things
have stabilized, but still every

202
00:09:41,400 --> 00:09:42,980
Database product does it differently.

203
00:09:43,440 --> 00:09:44,140
It's crazy.

204
00:09:44,140 --> 00:09:45,560
The devil is in the details.

205
00:09:46,420 --> 00:09:46,920
Nikolay: Yeah.

206
00:09:47,440 --> 00:09:50,420
And have you, like, do you need
sometimes to check the standard

207
00:09:50,420 --> 00:09:52,108
or like you don't need it at all?

208
00:09:52,108 --> 00:09:53,092
Lukas: I do it

209
00:09:53,092 --> 00:09:53,480
Nikolay: all the time.

210
00:09:53,480 --> 00:09:53,709
Lukas: Yeah.

211
00:09:53,709 --> 00:09:53,937
Okay.

212
00:09:53,937 --> 00:09:57,840
I'm trying to, the Juke API, if
it's not about a vendor-specific

213
00:09:57,880 --> 00:10:00,840
feature, the Juke API really tries
to follow the SQL standard

214
00:10:00,840 --> 00:10:01,860
in terms of syntax.

215
00:10:01,920 --> 00:10:06,040
So a vendor-specific feature would
be Postgres on conflict clause,

216
00:10:06,320 --> 00:10:08,100
which I think was a mistake.

217
00:10:08,680 --> 00:10:11,780
They should have done merge from
the beginning and not invent

218
00:10:11,780 --> 00:10:14,140
something new, but now we have
both.

219
00:10:14,140 --> 00:10:18,440
And in that case, Juke also models
the on-conflict part like

220
00:10:18,440 --> 00:10:22,340
Postgres did, and then SQLite copied
it and a couple of others,

221
00:10:22,340 --> 00:10:22,980
I think.

222
00:10:23,560 --> 00:10:29,540
But for instance, merge is a standard
SQL feature and many database

223
00:10:29,540 --> 00:10:33,480
products have some extensions which
I then study, but I always

224
00:10:33,480 --> 00:10:34,740
first look at the standard.

225
00:10:34,760 --> 00:10:36,040
What does the standard do?

226
00:10:36,040 --> 00:10:38,940
Because that's a thing that's going
to still be there in 50 years.

227
00:10:39,620 --> 00:10:42,680
And different implementations might
have their quirks, which

228
00:10:43,060 --> 00:10:46,880
sometimes I hope they deprecate
eventually and move towards the

229
00:10:46,880 --> 00:10:47,260
standard.

230
00:10:47,260 --> 00:10:49,680
Because usually I think the standard
is quite nicely...

231
00:10:49,840 --> 00:10:53,940
Nikolay: Well, they behave differently,
so many people got used

232
00:10:53,940 --> 00:10:54,640
to on conflict.

233
00:10:56,400 --> 00:10:58,840
Lukas: Yeah, I get the point of
doing on conflict.

234
00:10:59,180 --> 00:11:03,140
I mean, it's simpler for 80% of
the use cases, obviously.

235
00:11:03,400 --> 00:11:07,760
Nikolay: Yeah, also merge only recently
received support of returning

236
00:11:08,040 --> 00:11:10,680
clause, which is not standard at
all, right?

237
00:11:10,680 --> 00:11:11,500
As I remember.

238
00:11:11,580 --> 00:11:15,700
Lukas: The SQL standard has the
table, the data changed out of

239
00:11:15,700 --> 00:11:18,200
the table, which was implemented
by DB2.

240
00:11:19,540 --> 00:11:22,340
And then some minor database products
like H2 implemented as

241
00:11:22,340 --> 00:11:22,840
well.

242
00:11:23,360 --> 00:11:26,880
And it's not as powerful as returning,
especially because it

243
00:11:26,880 --> 00:11:32,420
only allows you to fetch either
the data before or after the

244
00:11:32,420 --> 00:11:36,140
updates for update statements,
which is also part of merge.

245
00:11:36,280 --> 00:11:39,680
You maybe want to return both versions
to the version before

246
00:11:39,680 --> 00:11:41,020
and after the update.

247
00:11:41,880 --> 00:11:44,700
With returning in principle, that's,
that's a feasible.

248
00:11:44,700 --> 00:11:46,800
So Oracle implemented this recently.

249
00:11:47,280 --> 00:11:48,680
I'm not in follow.

250
00:11:48,680 --> 00:11:54,140
Did Postgres allow for accessing
both versions of the row?

251
00:11:54,140 --> 00:11:55,520
Nikolay: I don't remember as well.

252
00:11:55,520 --> 00:12:00,360
I'm only like returning, well, if you DELETE, definitely you

253
00:12:00,360 --> 00:12:03,580
have ability to return old thing you deleted.

254
00:12:03,600 --> 00:12:05,140
Lukas: Well, there's only the old thing.

255
00:12:05,340 --> 00:12:06,360
Old thing is deleted.

256
00:12:06,360 --> 00:12:07,380
Nikolay: Let me quickly check.

257
00:12:07,800 --> 00:12:08,160
Lukas: Why not?

258
00:12:08,160 --> 00:12:12,840
But with updates, I mean, SQL Server always supported both versions.

259
00:12:12,900 --> 00:12:14,160
So before and after.

260
00:12:15,220 --> 00:12:19,200
And DB2 with the standard syntax supports only, you have to choose

261
00:12:19,200 --> 00:12:21,180
which one, but you can't return both.

262
00:12:21,180 --> 00:12:25,460
And Oracle 23 AI now supports both as well, like SQL Server.

263
00:12:26,040 --> 00:12:29,380
Oracle uses the Postgres syntax, the non-standard syntax also

264
00:12:29,380 --> 00:12:33,300
with the returning keyword, so maybe you can start from there

265
00:12:34,300 --> 00:12:36,680
if you don't do it yet in Postgres.

266
00:12:36,980 --> 00:12:41,920
Nikolay: I think in Postgres for updates we cannot return old

267
00:12:41,920 --> 00:12:42,420
data.

268
00:12:42,740 --> 00:12:45,780
We have access to it in triggers but it's different, right?

269
00:12:47,780 --> 00:12:53,200
So only the new value can be seen there, but for deletes, it's

270
00:12:53,200 --> 00:12:54,260
Lukas: probably more useful.

271
00:12:54,620 --> 00:12:57,560
Nikolay: Well, I can imagine some cases when we want to report,

272
00:12:57,560 --> 00:12:59,680
for example, what happened.

273
00:13:00,100 --> 00:13:00,540
Lukas: Yeah.

274
00:13:00,540 --> 00:13:02,860
When you do auditing, you want to have both.

275
00:13:03,280 --> 00:13:03,780
Nikolay: Yeah.

276
00:13:03,820 --> 00:13:04,640
Maybe let's add it.

277
00:13:04,640 --> 00:13:04,840
Because

278
00:13:04,840 --> 00:13:09,160
Lukas: standard supports this data change delta table, and it

279
00:13:09,160 --> 00:13:10,760
accepts a merge statement as well.

280
00:13:10,760 --> 00:13:14,640
So in principle, in the standard, you could have returning with

281
00:13:14,640 --> 00:13:15,180
a different.

282
00:13:15,180 --> 00:13:19,400
Nikolay: But it feels like more like a heavier approach in terms

283
00:13:19,400 --> 00:13:20,280
of manipulation.

284
00:13:20,640 --> 00:13:22,620
Returning is just like one line, right?

285
00:13:22,660 --> 00:13:24,780
Return a star, that's it.

286
00:13:24,960 --> 00:13:25,600
Lukas: Yes, yes.

287
00:13:25,600 --> 00:13:26,420
Super easy.

288
00:13:27,260 --> 00:13:28,760
It seems easier, yeah.

289
00:13:29,600 --> 00:13:31,260
Also, there are tons of limitations.

290
00:13:31,640 --> 00:13:35,720
I mean, you put your data change delta table inside of a SELECT

291
00:13:35,720 --> 00:13:38,740
query, but then you can hardly do anything with that SELECT query

292
00:13:38,760 --> 00:13:42,260
for instance, I'm not sure what exactly what is forbidden, but

293
00:13:42,260 --> 00:13:45,900
unions I think are forbidden and joins are forbidden, so maybe

294
00:13:45,900 --> 00:13:46,600
even aggregations.

295
00:13:46,780 --> 00:13:49,920
I'm not sure anymore, but you're very limited with what you can

296
00:13:49,920 --> 00:13:50,140
do.

297
00:13:50,140 --> 00:13:53,320
So I'm not sure if there's any benefit with allowing that in

298
00:13:53,320 --> 00:13:55,620
the SELECT statement as opposed to just returning.

299
00:13:56,760 --> 00:13:59,620
But ultimately it's kind of the same thing as what Postgres does

300
00:13:59,620 --> 00:14:02,440
when you put the ML statements in, in with.

301
00:14:02,940 --> 00:14:06,780
So that that kind of behaves the same way, at least from a user

302
00:14:06,820 --> 00:14:07,320
perspective.

303
00:14:07,760 --> 00:14:10,160
Nikolay: Speaking of with, I know Juke supports CTEs.

304
00:14:10,440 --> 00:14:12,060
Does it support recursive CTEs?

305
00:14:13,040 --> 00:14:13,780
Yeah, yeah.

306
00:14:14,060 --> 00:14:14,560
Yeah.

307
00:14:15,060 --> 00:14:17,820
So everything, lateral join and so on, everything.

308
00:14:18,040 --> 00:14:18,540
Yeah.

309
00:14:18,900 --> 00:14:19,300
Cool.

310
00:14:19,300 --> 00:14:22,500
Lukas: There's a high chance if SQL supports something and it's

311
00:14:22,500 --> 00:14:26,280
not really very funky like the match_recognize clause, then Juke

312
00:14:26,280 --> 00:14:27,340
will support it as well.

313
00:14:27,340 --> 00:14:30,900
So match_recognize could be supported as well in Juke, but it's

314
00:14:30,900 --> 00:14:35,180
such an edge case and still only supported in Oracle and some

315
00:14:35,180 --> 00:14:38,220
very esoteric database products that Juke doesn't support yet.

316
00:14:38,220 --> 00:14:42,480
So, so I'm skipping this for now, but with is, is everyone uses

317
00:14:42,480 --> 00:14:48,340
with, so Juke supported as well, including recursive with, I

318
00:14:48,340 --> 00:14:51,360
mean, there's some table valued functions that are like generate

319
00:14:51,360 --> 00:14:54,740
series in Postgres, which have to be emulated elsewhere.

320
00:14:54,760 --> 00:14:58,580
So if you want to ever, probably hardly anyone does that migrate

321
00:14:58,580 --> 00:15:02,580
from Postgres to whatever, and you want to translate your old

322
00:15:02,580 --> 00:15:06,000
generate series queries, then Juke will translate that to a recursive

323
00:15:06,000 --> 00:15:06,500
with.

324
00:15:06,880 --> 00:15:07,380
Michael: Cool.

325
00:15:07,540 --> 00:15:08,040
Nice.

326
00:15:08,180 --> 00:15:08,680
Nice.

327
00:15:10,080 --> 00:15:12,440
In fact, I feel like you skipped over that a little bit, but

328
00:15:12,440 --> 00:15:15,220
that's such a cool feature that basically...

329
00:15:15,360 --> 00:15:15,860
Translation?

330
00:15:16,380 --> 00:15:17,140
Yeah, exactly.

331
00:15:17,320 --> 00:15:23,460
We can get access to standard features that Postgres hasn't implemented,

332
00:15:24,160 --> 00:15:27,100
or that whatever database we're using hasn't implemented yet

333
00:15:27,100 --> 00:15:29,980
because you do the transformation, I guess.

334
00:15:30,120 --> 00:15:32,060
How do you, translation did you say?

335
00:15:32,120 --> 00:15:33,780
Lukas: Yeah, I call it translation.

336
00:15:33,900 --> 00:15:37,580
So there's even, you can use Juke as a purely translate product.

337
00:15:37,640 --> 00:15:39,440
So some customers actually do that.

338
00:15:39,480 --> 00:15:42,840
Mostly when they migrate from Oracle to Postgres, you can just

339
00:15:42,840 --> 00:15:46,120
embed Juke as a JDBC driver into, into your Java application.

340
00:15:46,120 --> 00:15:48,180
And you don't actually have to use Juke directly.

341
00:15:48,420 --> 00:15:52,420
So let's say this is a legacy application and uses JDBC or something

342
00:15:52,420 --> 00:15:56,180
JDBC based like Hibernate or MyBatis, and you just put Juke

343
00:15:56,180 --> 00:16:00,240
in the middle as a translating layer and probably 80% of your

344
00:16:00,240 --> 00:16:02,460
queries will work directly on Postgres.

345
00:16:02,680 --> 00:16:05,660
Of course, there's always edge cases and you have to do testing,

346
00:16:05,740 --> 00:16:09,140
but at least your migration project will go much, much faster

347
00:16:09,140 --> 00:16:13,180
because you can directly use Oracle queries on those other database

348
00:16:13,180 --> 00:16:13,680
products.

349
00:16:13,860 --> 00:16:17,220
Like for instance, Oracle has connect by and that can be translated

350
00:16:17,680 --> 00:16:19,340
to with queries with recursive.

351
00:16:19,820 --> 00:16:20,660
That's very hard.

352
00:16:20,660 --> 00:16:24,280
So some cases don't work, but maybe many cases do.

353
00:16:24,280 --> 00:16:26,980
And you don't have to actually look at those anymore.

354
00:16:27,340 --> 00:16:31,400
Or Oracle special outer join syntax can be translated to left

355
00:16:31,400 --> 00:16:33,680
joins in, in, in standard SQL.

356
00:16:33,680 --> 00:16:36,720
So your migration just goes much faster.

357
00:16:37,260 --> 00:16:40,440
You can also use it not as an embedded library, but just as the

358
00:16:40,440 --> 00:16:44,540
web, the website is free or you use it locally as a CLI tool,

359
00:16:44,540 --> 00:16:47,860
which is to have files, you have input files and Juke translates

360
00:16:47,880 --> 00:16:49,020
those directly.

361
00:16:49,060 --> 00:16:51,420
And you can manually check if it's correct.

362
00:16:51,820 --> 00:16:54,840
So translation is a, is a, I'd say minor use case.

363
00:16:54,840 --> 00:16:59,440
So a lot of companies work with only 1 database product and maybe

364
00:16:59,720 --> 00:17:03,740
10 years, they, they might reconsider their choices and migrate.

365
00:17:03,740 --> 00:17:07,120
But even then most, most companies stick with the database product

366
00:17:07,120 --> 00:17:08,200
they started with.

367
00:17:09,060 --> 00:17:12,840
And so many Juke users don't actually use this feature, but some

368
00:17:12,840 --> 00:17:13,340
do.

369
00:17:14,340 --> 00:17:17,740
Nikolay: May I ask slightly off topic question about JDBC since

370
00:17:17,740 --> 00:17:19,940
you spent a lot of time with Java, obviously.

371
00:17:20,740 --> 00:17:22,780
There is such thing as extra float digits.

372
00:17:23,560 --> 00:17:24,060
Right?

373
00:17:24,140 --> 00:17:25,020
Lukas: Extra what?

374
00:17:25,280 --> 00:17:26,760
Nikolay: Extra float digits.

375
00:17:27,340 --> 00:17:27,840
Okay.

376
00:17:27,980 --> 00:17:30,840
There is such setting, and by default it's 0 in Postgres.

377
00:17:32,520 --> 00:17:33,180
Or 1.

378
00:17:33,180 --> 00:17:34,020
Yeah, no, 1.

379
00:17:34,020 --> 00:17:34,740
It's 1.

380
00:17:34,900 --> 00:17:36,300
Lukas: I've never heard of it.

381
00:17:36,600 --> 00:17:42,240
Nikolay: Okay, but yeah, I remember some bugs in my code, and

382
00:17:42,240 --> 00:17:48,820
I usually try to use pure SQL and psql, and you know, if I can.

383
00:17:49,400 --> 00:17:53,520
I Remember that Java developers implemented the very same thing,

384
00:17:53,520 --> 00:17:55,820
same queries, behavior was very different.

385
00:17:56,400 --> 00:18:01,020
And then I noticed that the DBeaver, who else, other Java-based

386
00:18:01,840 --> 00:18:05,880
IDEs also behaved the same way Java application did.

387
00:18:06,400 --> 00:18:09,600
It was not the same as I saw in psql.

388
00:18:10,240 --> 00:18:12,480
It happened with these extra float digits.

389
00:18:12,700 --> 00:18:16,080
If you didn't notice this, this is maybe some very edge corner

390
00:18:16,080 --> 00:18:16,580
case.

391
00:18:17,080 --> 00:18:22,700
But I wonder like, why did the JDBC keep different default is

392
00:18:22,700 --> 00:18:24,620
like no layers of some defaults.

393
00:18:25,080 --> 00:18:26,780
This can be very annoying.

394
00:18:27,160 --> 00:18:28,020
But okay.

395
00:18:28,140 --> 00:18:31,680
Lukas: Well, the main thing that you always see The main difference

396
00:18:31,680 --> 00:18:37,440
when you work with JDBC is, as opposed to psql is, in psql you

397
00:18:37,440 --> 00:18:40,380
usually don't work with bind variables, so you don't have the

398
00:18:40,380 --> 00:18:43,660
whole kind of effect that you get when you have bind variables,

399
00:18:43,660 --> 00:18:47,240
although in Postgres This might not be that big of a difference,

400
00:18:47,240 --> 00:18:50,880
but when you work with JDBC, you always have prepared statements

401
00:18:52,360 --> 00:18:53,600
and bind variables.

402
00:18:53,600 --> 00:18:57,080
And let's say my background is
mostly Oracle.

403
00:18:57,080 --> 00:19:00,800
So with bind variables, you have
an execution plan cache and

404
00:19:00,800 --> 00:19:04,020
the cache will store some assumptions
about your bind variables,

405
00:19:04,020 --> 00:19:08,220
like a normalized distribution
and expectation regarding cardinalities,

406
00:19:08,560 --> 00:19:09,180
et cetera.

407
00:19:09,380 --> 00:19:12,700
So your actual bind variable might
be, if your dataset is skewed

408
00:19:12,700 --> 00:19:15,280
and your actual bind variable may
be completely different from

409
00:19:15,280 --> 00:19:16,180
what Oracle expects.

410
00:19:16,180 --> 00:19:18,780
And, and then the execution plan
will be bad.

411
00:19:19,060 --> 00:19:21,660
But from what I take, Postgres
doesn't have this problem because

412
00:19:21,660 --> 00:19:22,860
it doesn't have this feature.

413
00:19:24,720 --> 00:19:26,740
So this would be 1 of the main
differences.

414
00:19:27,740 --> 00:19:30,680
But you were referring to some
logical error, not...

415
00:19:31,420 --> 00:19:34,900
Nikolay: Yeah, Default and values
returned are different from

416
00:19:34,900 --> 00:19:36,500
what I see in PC.

417
00:19:36,500 --> 00:19:36,820
Yeah, but

418
00:19:36,820 --> 00:19:38,220
Lukas: I've never heard of this.

419
00:19:38,480 --> 00:19:41,040
Nikolay: Well, okay, maybe it's
already fixed by the way.

420
00:19:41,040 --> 00:19:43,760
It was like 5 years ago or so I
saw it.

421
00:19:43,940 --> 00:19:44,840
So, yeah.

422
00:19:45,820 --> 00:19:47,460
Well, on the topic of JDBC,

423
00:19:47,720 --> 00:19:50,140
Michael: I mean, do you have any
issues with it?

424
00:19:50,140 --> 00:19:52,840
It seems like it's a remarkable
piece of software that's just

425
00:19:52,840 --> 00:19:55,840
chugging along, but equally it
doesn't get much love.

426
00:19:55,840 --> 00:19:57,040
It doesn't get much love.

427
00:19:57,040 --> 00:20:00,040
Like I only, I generally only hear
negative things about it,

428
00:20:00,040 --> 00:20:03,220
but it must be amazing given how
much it's used.

429
00:20:03,600 --> 00:20:06,900
Lukas: I think it's the best API
that Java has, even better than

430
00:20:06,900 --> 00:20:08,500
collections and everything else.

431
00:20:08,500 --> 00:20:12,400
It's really, I mean, okay, maybe
they stole it from Microsoft

432
00:20:12,400 --> 00:20:15,840
from ODBC, it's kind of the same
thing, but It's really the best

433
00:20:15,840 --> 00:20:18,460
thing that Java has because it's
such a good standard.

434
00:20:18,560 --> 00:20:22,320
I mean, everything can build on
top of it and you don't even

435
00:20:22,320 --> 00:20:23,440
have to write SQL queries.

436
00:20:23,440 --> 00:20:26,760
So there's, for instance, Neo4j,
which is a NoSQL database with

437
00:20:26,760 --> 00:20:30,020
their own query language and you
can use JDBC with that database

438
00:20:30,020 --> 00:20:34,080
as well as long as you wanna have
some tabular results And it's

439
00:20:34,080 --> 00:20:37,580
a very, very good abstraction of
the network protocol layer.

440
00:20:37,900 --> 00:20:43,120
And the reason why people hate
it, it's not targeted at developers.

441
00:20:43,260 --> 00:20:47,040
So you can use it directly if you
want, But it's a bit hard to

442
00:20:47,040 --> 00:20:47,220
use.

443
00:20:47,220 --> 00:20:48,340
You have a lot of objects.

444
00:20:48,340 --> 00:20:50,940
You have the connection object
and you have to create a statement

445
00:20:50,940 --> 00:20:53,200
object and you have to remember
to close it.

446
00:20:53,200 --> 00:20:56,380
And you have to remember to create
a result set object and close

447
00:20:56,380 --> 00:20:57,260
that as well.

448
00:20:57,660 --> 00:21:00,700
And it's not integrated with the
rest of the JDK libraries.

449
00:21:00,700 --> 00:21:03,240
Like for instance, when you iterate
the result set, you have

450
00:21:03,240 --> 00:21:06,040
your own API methods instead of
just a list or iterable.

451
00:21:06,040 --> 00:21:09,640
So you can't use the for each loop
on a result set, for instance.

452
00:21:09,960 --> 00:21:13,340
So the ergonomics of JDBC is not,
not up to date.

453
00:21:13,340 --> 00:21:18,000
I once tried to convince the spec
expert group, it's a mailing

454
00:21:18,000 --> 00:21:20,500
list to, to update their API.

455
00:21:20,740 --> 00:21:24,600
There's a couple of things that
I think in, in at the time it

456
00:21:24,600 --> 00:21:27,880
was Java 8, but even now you could
improve a couple of things

457
00:21:27,880 --> 00:21:30,460
without changing anything fundamental
in JDBC.

458
00:21:30,860 --> 00:21:32,900
For instance, you can skip the
statement part.

459
00:21:32,900 --> 00:21:35,700
A lot of times you don't actually
have to think about preparing

460
00:21:35,740 --> 00:21:37,040
the statement explicitly.

461
00:21:37,660 --> 00:21:40,580
You have to do it when you want
to reuse the prepared statement.

462
00:21:40,940 --> 00:21:44,860
So you can save some time in case
there's a resource or some

463
00:21:44,860 --> 00:21:46,160
cache connection directly.

464
00:21:46,860 --> 00:21:50,380
But a lot of times that's not the
case in some database products

465
00:21:50,380 --> 00:21:52,540
and a lot of times it doesn't even
matter at all.

466
00:21:52,540 --> 00:21:55,100
So you could just have a connection
and run the query.

467
00:21:55,640 --> 00:22:00,060
So that's a minor improvement that
would just reduce 1 step for

468
00:22:00,060 --> 00:22:01,520
each statement you write.

469
00:22:02,080 --> 00:22:06,760
Yeah, but Oracle couldn't allocate
any resources to such a project,

470
00:22:06,760 --> 00:22:11,300
even if the people on the list
agreed that many ideas were reasonable.

471
00:22:12,180 --> 00:22:13,260
So I guess that's it.

472
00:22:13,260 --> 00:22:16,340
It's an amazing piece of software
for integration products like,

473
00:22:16,340 --> 00:22:20,080
like Juke or Hibernate or MyBatis
and everything else to build

474
00:22:20,080 --> 00:22:22,680
on top of and all the drivers they
just work.

475
00:22:22,680 --> 00:22:27,360
So a database company can just
publish a driver and it will work

476
00:22:27,360 --> 00:22:28,680
in all kind of software.

477
00:22:29,020 --> 00:22:31,840
For instance, if you, if you work
with DBeaver, it just works with

478
00:22:31,840 --> 00:22:33,160
all the database products, right?

479
00:22:33,160 --> 00:22:36,260
So you don't have to think about
how do you, how do I connect

480
00:22:36,260 --> 00:22:40,120
to this new database thing, like
the cows or, or DuckDB or whatever,

481
00:22:40,200 --> 00:22:41,100
it just works.

482
00:22:41,520 --> 00:22:43,860
All these database products, they
just work because they all

483
00:22:43,860 --> 00:22:46,000
use the same API in Java.

484
00:22:46,000 --> 00:22:50,380
So it's really a wonderful piece
of technology, I think.

485
00:22:50,380 --> 00:22:51,440
So I totally agree.

486
00:22:51,880 --> 00:22:55,660
Nikolay: Yeah, this is exactly
where I saw this problem, comparing

487
00:22:55,760 --> 00:22:58,680
to, I mean in DBeaver, comparing
to Java.

488
00:22:58,680 --> 00:23:02,180
And I realized, okay, it's also
Java application, so yeah.

489
00:23:02,880 --> 00:23:03,380
Good.

490
00:23:04,280 --> 00:23:06,460
Lukas: Yeah, but that was probably
just the driver bug.

491
00:23:06,460 --> 00:23:09,900
I don't think it has to do with
the database as an API.

492
00:23:10,520 --> 00:23:12,740
I mean, obviously the drivers can
have bugs.

493
00:23:13,820 --> 00:23:14,440
Nikolay: Right, right.

494
00:23:15,360 --> 00:23:16,760
So You see bugs often, right?

495
00:23:16,760 --> 00:23:18,940
I mean, in this...

496
00:23:18,940 --> 00:23:20,680
Lukas: Yeah, not necessarily in
the drivers.

497
00:23:20,680 --> 00:23:23,480
I mean, the drivers, they really
solve a very basic problem.

498
00:23:23,480 --> 00:23:26,820
So it's mostly about network connections
and these kind of things

499
00:23:26,820 --> 00:23:31,200
that no 1 actually cares about,
unless you have to optimize something,

500
00:23:31,500 --> 00:23:33,360
like multiplexing or whatever.

501
00:23:33,360 --> 00:23:36,080
But I think this stuff kind of
just works.

502
00:23:36,140 --> 00:23:39,060
And when I find bugs, it's more
SQL related.

503
00:23:39,140 --> 00:23:43,620
So it's inside of the database
and I do find a ton of them very

504
00:23:43,620 --> 00:23:44,640
rarely in Postgres.

505
00:23:45,060 --> 00:23:49,140
I have to say, I think in my career
I found 2 or 3 bugs in Postgres

506
00:23:49,720 --> 00:23:53,940
only, as opposed to hundreds.

507
00:23:54,520 --> 00:23:56,140
Michael: We didn't pay you to say
that.

508
00:23:56,400 --> 00:23:56,900
Lukas: No.

509
00:23:57,800 --> 00:23:59,060
It's really very good.

510
00:23:59,060 --> 00:24:00,000
It's surprisingly good.

511
00:24:00,000 --> 00:24:03,820
I mean, also, absolutely no regressions
at all.

512
00:24:03,820 --> 00:24:06,580
So if there's a bug, it's an edge
case that no one thought about,

513
00:24:06,580 --> 00:24:09,180
then it's really a very weird kind
of thing.

514
00:24:10,080 --> 00:24:10,580
Nikolay: Yeah.

515
00:24:10,680 --> 00:24:12,280
By the way, how do you check regression?

516
00:24:12,440 --> 00:24:14,280
Just conversion from...

517
00:24:15,280 --> 00:24:16,500
Lukas: In the database products?

518
00:24:17,280 --> 00:24:17,780
Nikolay: Yeah.

519
00:24:17,780 --> 00:24:21,980
For Juke, for example, some code
is written and then you just

520
00:24:21,980 --> 00:24:25,660
check the conversion to SQL or
you check results as well.

521
00:24:26,040 --> 00:24:29,240
Lukas: No, you can't just search
the SQL statement.

522
00:24:29,240 --> 00:24:33,960
I mean, Juke's integration tasks
have a standard database, which

523
00:24:33,960 --> 00:24:36,200
has about 5 tables.

524
00:24:36,200 --> 00:24:37,120
It's very simple.

525
00:24:37,120 --> 00:24:40,680
So there's a bookstore with books
and authors, and it's not a

526
00:24:40,680 --> 00:24:41,940
big database.

527
00:24:41,940 --> 00:24:46,780
So it has 4 books and 2 authors,
4 languages, this kind of thing.

528
00:24:47,080 --> 00:24:50,600
But then I just know there are
4 books and they have these titles

529
00:24:50,600 --> 00:24:54,400
and every kind of query has to
return exactly the same result.

530
00:24:54,400 --> 00:24:59,440
So there are so many queries being
run in each database product,

531
00:25:00,040 --> 00:25:03,520
maybe about 10, 000 queries, I
think, and also update statements

532
00:25:03,520 --> 00:25:07,160
and all kinds of statements that
make the assumption that if

533
00:25:07,160 --> 00:25:10,320
you have this kind of input database,
then this must be the output.

534
00:25:10,320 --> 00:25:14,620
Or also, if I create a table with
Juke, then it has to have these

535
00:25:14,620 --> 00:25:18,480
properties and throughout the API,
it has to be the same thing

536
00:25:18,480 --> 00:25:19,840
for every database product.

537
00:25:20,320 --> 00:25:20,820
Nikolay: Right.

538
00:25:21,040 --> 00:25:24,900
I just wonder, earlier you mentioned
that usually database systems

539
00:25:25,080 --> 00:25:28,420
maintain reverse compatibility,
but sometimes it's broken.

540
00:25:28,420 --> 00:25:33,620
For example, in Postgres 12, CTEs,
before they were materialized,

541
00:25:33,840 --> 00:25:35,520
like optimization fans.

542
00:25:35,580 --> 00:25:40,240
So every step is materialized before
12, but in 12 default behavior

543
00:25:40,240 --> 00:25:40,740
changed.

544
00:25:41,440 --> 00:25:41,940
Right.

545
00:25:42,120 --> 00:25:46,320
So, so is it something that Juke
should care about or no?

546
00:25:46,800 --> 00:25:49,800
Lukas: I don't think that's a logical
regression.

547
00:25:49,960 --> 00:25:53,260
I mean, it was a conscious
decision and it's only affects

548
00:25:53,300 --> 00:25:54,400
the performance.

549
00:25:56,200 --> 00:25:58,360
There's no logical difference of
the result.

550
00:25:58,400 --> 00:26:02,440
So I mean, databases are allowed
to do these kinds of things.

551
00:26:02,980 --> 00:26:07,280
I re I recall there was once a logical change in the UPDATE statement

552
00:26:07,280 --> 00:26:11,980
in Postgres, which was a incompatible change when you UPDATE

553
00:26:12,040 --> 00:26:12,740
a row.

554
00:26:12,740 --> 00:26:15,860
So you had used the row syntax, you use parentheses And you UPDATE

555
00:26:15,860 --> 00:26:20,020
2 Columns at once and suddenly both required the row keyword,

556
00:26:20,020 --> 00:26:21,140
but only there.

557
00:26:21,500 --> 00:26:23,040
This was a very weird.

558
00:26:23,040 --> 00:26:27,160
Nikolay: 1 more recently, 1 more change related to subqueries.

559
00:26:27,240 --> 00:26:28,440
I don't remember exactly.

560
00:26:30,180 --> 00:26:33,660
It was a demonstration included in the generate series, I remember.

561
00:26:34,300 --> 00:26:37,840
And yeah, maybe it was like up to 5 years ago, but there was

562
00:26:37,840 --> 00:26:40,460
some change which was logical as well.

563
00:26:40,840 --> 00:26:44,100
But I understand, you're like SQL standard, don't care about

564
00:26:44,100 --> 00:26:46,300
Indexes, care only about results.

565
00:26:46,300 --> 00:26:46,800
Yeah.

566
00:26:46,880 --> 00:26:48,440
Right, I see, I see.

567
00:26:49,200 --> 00:26:52,200
Lukas: No, I mean, a juke doesn't make any guarantees at all

568
00:26:52,200 --> 00:26:53,800
with respect to performance either.

569
00:26:53,800 --> 00:26:56,760
So, so from a Juke perspective, Juke doesn't try to fix your

570
00:26:56,760 --> 00:26:57,040
SQL.

571
00:26:57,040 --> 00:26:59,880
If you write a really crap query with, I mean, crap, what does

572
00:26:59,880 --> 00:27:00,480
it even mean?

573
00:27:00,480 --> 00:27:04,700
So if you write a bad query with a lot of nested subqueries,

574
00:27:04,920 --> 00:27:07,400
et cetera, et cetera, in principle, there's nothing wrong with

575
00:27:07,400 --> 00:27:07,560
it.

576
00:27:07,560 --> 00:27:07,900
Right?

577
00:27:07,900 --> 00:27:11,540
So there's, you will find hundreds of blog posts that, especially

578
00:27:11,580 --> 00:27:15,240
from, from old times when optimizers were really bad still, where

579
00:27:15,240 --> 00:27:18,480
people advised against using derived tables, then they advised

580
00:27:18,480 --> 00:27:21,780
against using correlated subqueries, and then, but there's nothing

581
00:27:21,780 --> 00:27:23,940
logically wrong with these things, right?

582
00:27:23,940 --> 00:27:27,720
So you can nest as many levels as you want, as long as it's correct,

583
00:27:27,720 --> 00:27:30,040
it's correct, and the optimizer should figure it out.

584
00:27:30,280 --> 00:27:33,960
And Who is Juke to judge you for your query style?

585
00:27:34,440 --> 00:27:38,000
So Juke doesn't fix these things or even assert anything, and

586
00:27:38,000 --> 00:27:40,680
it would be completely wrong for Juke to do these kind of things,

587
00:27:40,680 --> 00:27:43,480
because you want to write exactly this SQL, and Juke should render

588
00:27:43,480 --> 00:27:44,700
exactly this SQL.

589
00:27:45,020 --> 00:27:46,360
You have your reasons, right?

590
00:27:46,360 --> 00:27:46,860
So...

591
00:27:47,360 --> 00:27:48,020
Nikolay: I understand.

592
00:27:48,960 --> 00:27:55,260
And my question is, I'm curious, how do people usually approach

593
00:27:55,280 --> 00:27:55,780
performance?

594
00:27:56,640 --> 00:28:01,500
For example, if some query is written using Juke with a chain

595
00:28:01,500 --> 00:28:06,360
of SELECTs from blah blah, And then it turns out it's slow.

596
00:28:07,360 --> 00:28:11,040
And then we find the fix, and we need to adjust something in

597
00:28:11,040 --> 00:28:13,540
the query if it was a rock SQL.

598
00:28:13,820 --> 00:28:17,000
Is it usually simple to adjust a Juke version of.

599
00:28:17,540 --> 00:28:17,960
Lukas: Yeah.

600
00:28:17,960 --> 00:28:19,200
I mean, there's no difference.

601
00:28:19,200 --> 00:28:24,140
I mean, you're probably going to
use the, I'm not, I'm not sure

602
00:28:24,140 --> 00:28:26,660
anymore what the Postgres version
is called, but you have your

603
00:28:26,660 --> 00:28:29,540
statistics views in your database,
your performance schema, and

604
00:28:29,540 --> 00:28:33,220
you query those to see what's,
what went wrong and then you make

605
00:28:33,220 --> 00:28:36,180
assumptions, you rewrite the query
and you test again.

606
00:28:36,700 --> 00:28:40,240
Maybe even using a benchmark, of
course then the benchmark is

607
00:28:40,240 --> 00:28:42,940
written in Java, but I don't see
a difference when you write

608
00:28:42,940 --> 00:28:46,140
jOOQ query or native SQL query,
It's the same process.

609
00:28:46,240 --> 00:28:49,860
Michael: I was watching a really
good video by Kevin Davin, who

610
00:28:49,860 --> 00:28:52,900
did a talk on jOOQ, but 1 of the
things that he really liked

611
00:28:52,900 --> 00:28:57,640
about the product was it logs out
to the console, to the actual

612
00:28:57,720 --> 00:28:59,700
SQL statement as well that it ran.

613
00:29:00,040 --> 00:29:02,060
Lukas: When you do a debug level.

614
00:29:02,440 --> 00:29:04,600
Michael: Yeah, which is super helpful
for performance.

615
00:29:04,600 --> 00:29:07,700
So if that was slow, you can then
run it in your favorite editor

616
00:29:07,700 --> 00:29:11,740
in PSQL wherever with EXPLAIN,
ANALYZE, et cetera, et cetera.

617
00:29:11,740 --> 00:29:12,240
BUFFERS.

618
00:29:12,740 --> 00:29:15,520
Yeah, always BUFFERS, hot topic
this week.

619
00:29:15,660 --> 00:29:20,660
But yeah, so that seems like a
really developer friendly way

620
00:29:20,660 --> 00:29:24,360
of getting back to the SQL and
then diagnosing the performance

621
00:29:24,360 --> 00:29:24,860
issue.

622
00:29:24,960 --> 00:29:26,600
Lukas: This was a feature from
day 1.

623
00:29:26,600 --> 00:29:29,480
My assumption was that when you
develop, you have debug mode

624
00:29:29,480 --> 00:29:29,980
logging.

625
00:29:30,060 --> 00:29:33,540
And when you go to production,
you switch that to info or even

626
00:29:33,540 --> 00:29:34,040
warning.

627
00:29:34,200 --> 00:29:37,320
So you're not going to have the
performance penalty of re-rendering

628
00:29:37,440 --> 00:29:37,900
the query.

629
00:29:37,900 --> 00:29:40,840
It's even formatted, so you can
actually see the query.

630
00:29:40,840 --> 00:29:44,640
It's not just 1 line, which is
when you execute the query, it's

631
00:29:44,640 --> 00:29:47,820
just 1 line of SQL string, so to
have it more compact.

632
00:29:47,880 --> 00:29:51,940
But when you debug log it, the
version contains the bind variables.

633
00:29:51,960 --> 00:29:54,860
So you can just copy paste the
whole query with the bind variables

634
00:29:54,860 --> 00:29:56,980
in a formatted way and study it.

635
00:29:57,540 --> 00:29:59,680
I thought that was what everyone
wanted.

636
00:29:59,680 --> 00:30:03,240
And Also you get the first 5 records
of the result set also in

637
00:30:03,240 --> 00:30:06,180
the debug log, because that's probably
also what you want while

638
00:30:06,180 --> 00:30:07,540
you develop at least.

639
00:30:07,660 --> 00:30:07,940
Michael: Yeah.

640
00:30:07,940 --> 00:30:09,400
In a pretty little table.

641
00:30:09,400 --> 00:30:10,120
Nikolay: It's nice.

642
00:30:10,600 --> 00:30:12,900
Lukas: So you can immediately see
what's going on.

643
00:30:13,080 --> 00:30:16,260
And even then, I mean, 1 of the
things, 1 of the reasons why

644
00:30:16,260 --> 00:30:19,200
I implemented it this way is when
you have a huge result set

645
00:30:19,200 --> 00:30:22,620
with a lot of columns, that bothers
you while developing.

646
00:30:22,860 --> 00:30:25,440
So you kind of start thinking,
do I really actually need all

647
00:30:25,440 --> 00:30:26,140
these columns?

648
00:30:26,540 --> 00:30:28,780
Because if you don't, then you
have a better debug log.

649
00:30:28,780 --> 00:30:32,560
So I kind of think you have to
punish developers as early as

650
00:30:32,560 --> 00:30:34,420
possible for their performance
problems.

651
00:30:35,140 --> 00:30:36,780
I mean, jOOQ doesn't judge you.

652
00:30:37,740 --> 00:30:40,900
It just gives you some tools.

653
00:30:41,260 --> 00:30:44,560
Nikolay: Just giving you some convenience
looking at logs.

654
00:30:44,560 --> 00:30:45,060
Okay.

655
00:30:46,160 --> 00:30:47,480
Yeah, that's interesting.

656
00:30:48,220 --> 00:30:49,700
So, reduce number of columns.

657
00:30:49,780 --> 00:30:50,280
Good.

658
00:30:51,460 --> 00:30:54,020
Michael: Changing tact a little
bit, I think you've got a really

659
00:30:54,020 --> 00:30:58,680
interesting perspective on Postgres
from a broader landscape

660
00:30:58,980 --> 00:31:02,060
of SQL dialects and databases in
general.

661
00:31:02,500 --> 00:31:05,440
What kind of things do you see
that are relatively unique to

662
00:31:05,440 --> 00:31:05,860
Postgres?

663
00:31:05,860 --> 00:31:08,520
Or do you see people in the Postgres
world particularly using

664
00:31:08,520 --> 00:31:09,260
or liking?

665
00:31:09,640 --> 00:31:12,340
Lukas: I mean, the 1 thing that
is very unique to Postgres, there

666
00:31:12,340 --> 00:31:14,200
are 2 things from my perspective.

667
00:31:14,260 --> 00:31:16,360
The first 1 is it's very developer-centric.

668
00:31:17,420 --> 00:31:20,780
So you can see that with other
database products like Oracle,

669
00:31:20,860 --> 00:31:24,640
DB2 or SQL Server, they're very
production centric, very operation

670
00:31:24,840 --> 00:31:25,320
centric.

671
00:31:25,320 --> 00:31:29,180
They used to base their entire
sales on these kinds of things.

672
00:31:29,440 --> 00:31:32,080
So this means you have a lot of
production tools as well, which

673
00:31:32,080 --> 00:31:32,960
are great actually.

674
00:31:32,960 --> 00:31:36,660
So I'm still waiting for the like
of Oracle Enterprise Manager,

675
00:31:36,680 --> 00:31:39,760
if you know it, for Postgres where
you can analyze production

676
00:31:39,760 --> 00:31:43,040
workloads and, and, and query what,
what kind of workload you

677
00:31:43,040 --> 00:31:45,980
had 5 months ago, and it's still
there.

678
00:31:45,980 --> 00:31:46,410
It is still happy.

679
00:31:46,410 --> 00:31:49,020
I mean, you have to pay for these
kinds of extras, but it's still

680
00:31:49,020 --> 00:31:49,340
there.

681
00:31:49,340 --> 00:31:52,320
You can still analyze anomalies
in terms of performance, what

682
00:31:52,320 --> 00:31:53,560
happened 5 months ago.

683
00:31:53,560 --> 00:31:57,840
So these kinds of tools are a bit
lacking, but on the other hand,

684
00:31:57,940 --> 00:32:01,420
because it's so developer focused,
you have this whole extension

685
00:32:01,640 --> 00:32:04,060
system, which is unprecedented.

686
00:32:04,280 --> 00:32:06,480
And I haven't seen it since either.

687
00:32:06,480 --> 00:32:10,900
So anyone can extend anything in
any layer of the, of the database

688
00:32:10,900 --> 00:32:11,320
product.

689
00:32:11,320 --> 00:32:14,800
So you have these wonderful extensions
like PostGIS for instance,

690
00:32:14,800 --> 00:32:17,860
which is, It's not really an extension
because it's also part

691
00:32:17,860 --> 00:32:20,720
of the SQL standard, but it has
been implemented as an extension

692
00:32:20,740 --> 00:32:25,180
just, I guess, to show what is
possible and such a huge additional

693
00:32:25,240 --> 00:32:27,480
product can be made as an extension.

694
00:32:27,540 --> 00:32:32,320
And you have indexes and data types
and all kinds of things that

695
00:32:32,320 --> 00:32:36,100
other vendors have to either offer
out of the box or they don't

696
00:32:36,100 --> 00:32:36,800
have it.

697
00:32:37,040 --> 00:32:38,880
You can't extend Oracle really.

698
00:32:38,880 --> 00:32:43,040
So that's really something very,
very amazing.

699
00:32:43,660 --> 00:32:46,320
But as I said, from the other perspective,
from an operations

700
00:32:46,480 --> 00:32:49,780
perspective, this is something
that personally, I think Postgres

701
00:32:49,780 --> 00:32:52,700
is still very much behind Oracle, for instance.

702
00:32:52,800 --> 00:32:53,860
So I'm an Oracle guy.

703
00:32:53,860 --> 00:32:57,180
I can't really comment on SQL Server or others, but the Oracle

704
00:32:57,180 --> 00:33:01,020
database where I worked at before I made jOOQ, it could easily

705
00:33:01,080 --> 00:33:04,960
handle very, very complex queries that produced 500 lines of

706
00:33:04,960 --> 00:33:09,500
execution plan on billions of rows, and it would run in milliseconds.

707
00:33:09,660 --> 00:33:11,720
And I don't think you can do that with Postgres.

708
00:33:11,720 --> 00:33:12,980
I wouldn't risk it.

709
00:33:13,080 --> 00:33:17,440
So we all remember the days when there was a hard limit of number

710
00:33:17,440 --> 00:33:21,040
of joins in Postgres, from which, starting from where, you didn't

711
00:33:21,040 --> 00:33:24,660
have any smart optimizer anymore to reorder the joins in the

712
00:33:24,660 --> 00:33:25,360
right order.

713
00:33:25,920 --> 00:33:30,060
I forgot what the limit was, but I think you had to be very careful

714
00:33:30,060 --> 00:33:33,420
if you have more than 10 joins or something like that, because

715
00:33:33,420 --> 00:33:36,100
then it would just join from left to right syntactically, and

716
00:33:36,100 --> 00:33:36,920
this is horrible.

717
00:33:37,120 --> 00:33:38,920
Nikolay: Join collapse limit should be 8.

718
00:33:39,340 --> 00:33:40,580
Lukas: Yeah, that's the join collapse limit.

719
00:33:40,900 --> 00:33:44,220
So, once you know these kind of things, obviously, make the right

720
00:33:44,220 --> 00:33:48,680
choices, and you probably won't run into big issues, but it's

721
00:33:48,680 --> 00:33:53,000
just, it's a very unique focus that I often missed in the past

722
00:33:53,000 --> 00:33:56,360
from Oracle, for instance, or completely ignored developer experience

723
00:33:56,780 --> 00:33:57,680
for decades.

724
00:33:58,140 --> 00:34:00,600
It was like, you don't even have a Boolean type, right?

725
00:34:00,600 --> 00:34:03,040
So this is hard for me to understand.

726
00:34:03,040 --> 00:34:04,600
It's such a simple thing.

727
00:34:04,600 --> 00:34:06,540
You think it's such a simple thing.

728
00:34:06,580 --> 00:34:10,280
So very, very useful, but Oracle wouldn't have implemented it

729
00:34:10,280 --> 00:34:11,140
until recently.

730
00:34:11,280 --> 00:34:16,840
So this is a focus that Postgres has and few of the others had,

731
00:34:16,840 --> 00:34:20,180
even MySQL wasn't very developer friendly from that perspective.

732
00:34:22,160 --> 00:34:23,540
So this is very unique.

733
00:34:24,100 --> 00:34:26,880
If developers were the only ones to choose the database product,

734
00:34:26,880 --> 00:34:30,480
it's always Postgres, because I mean, you're just gonna implement

735
00:34:30,480 --> 00:34:31,700
everything with Postgres.

736
00:34:31,720 --> 00:34:34,160
You can write stored procedures, you can have your own indexes,

737
00:34:34,160 --> 00:34:35,700
you can extend it yourself.

738
00:34:36,040 --> 00:34:38,720
Probably you don't have to because someone already wrote an extension

739
00:34:38,720 --> 00:34:39,940
for whatever you need.

740
00:34:40,200 --> 00:34:43,640
Oh, the second thing is, it's amazing how standards compliant

741
00:34:43,700 --> 00:34:44,380
it is.

742
00:34:44,380 --> 00:34:48,020
So a lot of times, Postgres really waits until something is standardized

743
00:34:48,420 --> 00:34:50,820
before they implement it instead of innovating.

744
00:34:51,420 --> 00:34:55,040
Nikolay: Right now we are waiting on UID version 7 because of

745
00:34:55,040 --> 00:34:55,540
standards.

746
00:34:55,640 --> 00:34:59,960
Like it didn't get into version 17 of Postgres because RFC was

747
00:34:59,960 --> 00:35:00,660
not finalized.

748
00:35:01,480 --> 00:35:04,680
Every library already started to support it, but Postgres decided

749
00:35:04,680 --> 00:35:08,440
to wait on RFC, which is like very conservative decision, right?

750
00:35:09,280 --> 00:35:10,120
Lukas: Most of the times.

751
00:35:10,120 --> 00:35:10,620
Yes.

752
00:35:10,760 --> 00:35:14,180
So I think that's a good thing
in the end, because the standard,

753
00:35:14,180 --> 00:35:17,380
in my opinion, in most parts is
well-written.

754
00:35:17,720 --> 00:35:20,980
It's a very complicated document,
of course, but I think in terms

755
00:35:20,980 --> 00:35:25,740
of syntax, it's kind of everything
feels like SQL.

756
00:35:26,040 --> 00:35:29,940
So some database products think
they have to invent some syntax

757
00:35:30,040 --> 00:35:32,040
And it just doesn't look like SQL
anymore.

758
00:35:32,040 --> 00:35:37,120
It's just very weird, funky thing
that doesn't fit the rest of

759
00:35:37,120 --> 00:35:37,720
the language.

760
00:35:37,720 --> 00:35:42,420
And the standard is very, very
consistent, I think, in most parts.

761
00:35:42,440 --> 00:35:45,660
And to wait for the standard to
appear is a good thing.

762
00:35:45,840 --> 00:35:48,280
So sometimes there's an exception
to the rule, as I said, on

763
00:35:48,280 --> 00:35:49,860
conflict was 1 of these exceptions.

764
00:35:50,500 --> 00:35:54,860
JSON as well, in case of which
I think it was something everyone

765
00:35:54,860 --> 00:35:58,120
needed and the standard was severely
lacking.

766
00:35:58,620 --> 00:36:00,160
And Postgres just did their own
thing.

767
00:36:00,160 --> 00:36:04,960
So I kind of think a lot of people
kind of regret the heterogeneity

768
00:36:05,320 --> 00:36:06,560
of the JSON API.

769
00:36:06,620 --> 00:36:11,600
So you have to always consult the
docs to see if something uses

770
00:36:11,600 --> 00:36:15,380
a JSON path, which is a standard
thing in the JSON world, or

771
00:36:15,380 --> 00:36:20,080
if it's just an array of path elements,
or if this function returns

772
00:36:20,080 --> 00:36:23,620
a table or a set of something,
or whatever data structure.

773
00:36:23,620 --> 00:36:29,020
So I think now with the JSON standard,
this is going to be much

774
00:36:29,020 --> 00:36:31,280
cleaner once it's completely rolled
out.

775
00:36:31,280 --> 00:36:34,120
So I'm not up to date where the
Postgres stands.

776
00:36:34,120 --> 00:36:37,860
I'm still not supporting that syntax,
but from what I've seen,

777
00:36:37,860 --> 00:36:39,520
it's going into the right direction.

778
00:36:39,860 --> 00:36:42,280
Nikolay: Yeah, some parts support
it, some parts I think are

779
00:36:42,280 --> 00:36:43,040
still bending.

780
00:36:43,260 --> 00:36:43,760
Yeah.

781
00:36:43,860 --> 00:36:44,820
Yeah, because I

782
00:36:44,820 --> 00:36:47,660
Michael: think A lot went in in
17.

783
00:36:47,960 --> 00:36:48,400
Nikolay: Right.

784
00:36:48,400 --> 00:36:48,820
Lukas: Yeah.

785
00:36:48,820 --> 00:36:51,180
And speaking of this, I'm going
to be really curious.

786
00:36:51,180 --> 00:36:55,460
I saw a commit recently regarding
temporal primary keys.

787
00:36:55,460 --> 00:36:56,260
Is that correct?

788
00:36:56,520 --> 00:36:58,600
And it is absolutely not the standard.

789
00:36:58,900 --> 00:37:00,720
So this is very curious.

790
00:37:00,720 --> 00:37:02,180
I'm very curious about this.

791
00:37:02,240 --> 00:37:03,780
Are you aware of this commit?

792
00:37:03,900 --> 00:37:04,540
Michael: I'm not.

793
00:37:04,540 --> 00:37:05,720
Lukas: It's still on a branch.

794
00:37:05,720 --> 00:37:08,480
I think it's not, it's not going
to be merged, but I mean, Postgres

795
00:37:08,480 --> 00:37:09,780
has these range types.

796
00:37:10,380 --> 00:37:10,880
Right.

797
00:37:11,280 --> 00:37:11,520
Nikolay: Right.

798
00:37:11,520 --> 00:37:15,060
Lukas: And they're useful obviously,
but the SQL standard since

799
00:37:15,060 --> 00:37:19,920
2011 defined how you should write
temporal databases, both by

800
00:37:19,920 --> 00:37:20,420
temporal.

801
00:37:20,460 --> 00:37:23,280
So you have system versioning and then logical versioning.

802
00:37:24,400 --> 00:37:26,760
And this is something that is still lacking in Postgres.

803
00:37:26,820 --> 00:37:29,120
And I think a lot of people would like to see that.

804
00:37:29,540 --> 00:37:29,760
Nikolay: Yeah.

805
00:37:29,760 --> 00:37:33,900
In Postgres, there is an opinion that it's in the past, because

806
00:37:33,900 --> 00:37:34,200
it was

807
00:37:34,200 --> 00:37:34,760
Lukas: in the past,

808
00:37:34,760 --> 00:37:35,700
Nikolay: it was removed.

809
00:37:36,760 --> 00:37:39,180
Michael: So that's a nice temporal joke.

810
00:37:39,920 --> 00:37:41,320
Lukas: An extension I think?

811
00:37:41,320 --> 00:37:45,320
Michael: It was in Postgres, the original Berkeley version.

812
00:37:46,320 --> 00:37:50,420
Nikolay: This is how MVCC inherited from these versions

813
00:37:50,500 --> 00:37:51,320
of tuples.

814
00:37:52,860 --> 00:37:55,800
So your time travel basically was supported very, very

815
00:37:55,800 --> 00:37:57,640
long ago, but then it was removed.

816
00:37:57,640 --> 00:37:58,640
So like interesting idea.

817
00:37:58,780 --> 00:37:59,280
Michael: I think for

818
00:37:59,280 --> 00:38:00,320
performance reasons.

819
00:38:01,560 --> 00:38:03,200
Nikolay: Yeah, So I think so.

820
00:38:03,820 --> 00:38:07,240
But yeah, this is a super interesting, like specific topic.

821
00:38:07,800 --> 00:38:10,140
Sometimes we do need to implement something with triggers and

822
00:38:10,140 --> 00:38:11,460
so on, like, which is very

823
00:38:11,460 --> 00:38:11,720
Lukas: Yeah.

824
00:38:11,720 --> 00:38:11,980
Yeah.

825
00:38:11,980 --> 00:38:13,520
It's very hard to do manually.

826
00:38:13,520 --> 00:38:16,960
I think the standard helps a lot with, with, I mean, you can

827
00:38:16,960 --> 00:38:20,040
write an update statement and it's automatically transformed

828
00:38:20,320 --> 00:38:21,940
into a delete and insert.

829
00:38:21,940 --> 00:38:25,040
So if you have to split the record logically, this is really

830
00:38:25,040 --> 00:38:26,520
a pain to do manually, right?

831
00:38:26,520 --> 00:38:27,040
So it's

832
00:38:27,040 --> 00:38:29,880
Nikolay: not super pain because you can have, for example, Postgres,

833
00:38:29,880 --> 00:38:32,760
you know, like I remember your very old post, everything is a

834
00:38:32,760 --> 00:38:33,260
table.

835
00:38:33,340 --> 00:38:36,880
Or maybe it was a post of some 10 tips or something, and 1 of

836
00:38:36,880 --> 00:38:39,120
the tips was, consider everything as a table.

837
00:38:39,640 --> 00:38:43,720
In Postgres, you can collapse all columns into a single record

838
00:38:43,820 --> 00:38:44,820
type and insert.

839
00:38:45,860 --> 00:38:48,880
For example, in JSON form right now, I would insert it and I

840
00:38:48,880 --> 00:38:50,580
would have a shadow table.

841
00:38:50,580 --> 00:38:52,400
I implemented this several times.

842
00:38:52,500 --> 00:38:56,280
You have a shadow table which serves as like a guy for all tables,

843
00:38:56,280 --> 00:38:59,600
which has, has trigger to track changes.

844
00:38:59,600 --> 00:39:00,100
Right.

845
00:39:00,240 --> 00:39:05,140
And then you, you just track timestamp action, like it was delete

846
00:39:05,140 --> 00:39:08,400
or update, who did it, and then whole row you pack it in a single

847
00:39:08,400 --> 00:39:10,700
column, breaking first normal form.

848
00:39:10,760 --> 00:39:14,620
Lukas: But then you normalize in a very bad way, and secondly,

849
00:39:14,680 --> 00:39:16,360
this sounds like a lot of work.

850
00:39:16,580 --> 00:39:20,280
Nikolay: It's not a lot of work, it's like 3, 5 lines of code,

851
00:39:20,280 --> 00:39:21,060
that's it.

852
00:39:21,500 --> 00:39:24,380
But it's not possible in jOOQ 3
version because triggers are

853
00:39:24,380 --> 00:39:25,420
not supported, right?

854
00:39:25,640 --> 00:39:28,700
Triggers are supported only in
Enterprise version.

855
00:39:28,980 --> 00:39:32,580
Lukas: I mean, if you want to write
the trigger with jOOQ, yes,

856
00:39:32,600 --> 00:39:36,820
then you have to use the commercial
additions, but hardly anyone

857
00:39:36,820 --> 00:39:38,080
writes triggers with jOOQ.

858
00:39:38,080 --> 00:39:41,600
I mean, this is only useful when
you have to have, when you support

859
00:39:41,600 --> 00:39:45,240
5 database products and you want
to have, you want to write the

860
00:39:45,240 --> 00:39:47,540
trigger only once you write it
with jOOQ.

861
00:39:47,960 --> 00:39:50,600
But I mean, jOOQ doesn't care if
there's a trigger on the database

862
00:39:50,600 --> 00:39:51,180
table, right?

863
00:39:51,180 --> 00:39:54,740
So if there's 1, so it's transparent
to jOOQ.

864
00:39:55,140 --> 00:39:56,060
Yeah, okay.

865
00:39:56,280 --> 00:39:58,380
You can't really say jOOQ doesn't
support them.

866
00:39:58,380 --> 00:40:00,980
It's just, you can't create them
with jOOQ.

867
00:40:01,200 --> 00:40:01,980
Nikolay: Great answer.

868
00:40:02,220 --> 00:40:03,600
Free version, you mean like an

869
00:40:03,600 --> 00:40:04,160
Lukas: enterprise version?

870
00:40:04,160 --> 00:40:04,960
Free version, yeah.

871
00:40:04,960 --> 00:40:05,460
Nikolay: Yeah.

872
00:40:06,400 --> 00:40:07,200
I'm very curious.

873
00:40:07,200 --> 00:40:08,460
I'm not against this.

874
00:40:08,480 --> 00:40:09,140
It's good.

875
00:40:09,140 --> 00:40:13,040
I mean, it's great that you build
both like open source product

876
00:40:13,040 --> 00:40:15,560
on Apache tool license and business.

877
00:40:16,340 --> 00:40:21,300
I was just curious, like, have
you ever had thoughts about going

878
00:40:21,300 --> 00:40:24,900
full closed source or fully open
source?

879
00:40:25,900 --> 00:40:26,100
Well,

880
00:40:26,100 --> 00:40:28,500
Lukas: it was fully open source
in the beginning.

881
00:40:28,500 --> 00:40:31,960
So when I started this, I had no
idea this could be a business.

882
00:40:31,960 --> 00:40:35,500
I mean, I just had to try it and
then put it out there for people

883
00:40:35,500 --> 00:40:37,220
to see and give me feedback.

884
00:40:37,280 --> 00:40:38,220
Is this a good idea?

885
00:40:38,220 --> 00:40:39,520
Should I do it like that?

886
00:40:39,520 --> 00:40:43,260
And there was a very active early
community of people who worked

887
00:40:43,260 --> 00:40:44,780
with the all open source jOOQ.

888
00:40:45,100 --> 00:40:48,420
And then I started seeing even
more and more banks and insurance

889
00:40:48,420 --> 00:40:51,800
companies using it, but never contributing
as you know, the story.

890
00:40:51,820 --> 00:40:53,320
And I thought it was my fault.

891
00:40:53,320 --> 00:40:54,080
Nikolay: I get it.

892
00:40:54,080 --> 00:40:56,780
Pong Jones, let's, let's generalize
Oracle users.

893
00:40:57,260 --> 00:40:59,680
Lukas: Well, that was the choice
I made, but, but it doesn't

894
00:40:59,680 --> 00:40:59,960
matter.

895
00:40:59,960 --> 00:41:02,320
You know, It doesn't matter what
they're using in the end.

896
00:41:02,320 --> 00:41:03,660
I just never got any...

897
00:41:04,060 --> 00:41:07,320
I mean, I got contributions, but
I didn't care about code.

898
00:41:07,360 --> 00:41:09,460
I cared about money much more than
code.

899
00:41:09,760 --> 00:41:14,620
So, to make them a sustainable
business, in the end, I thought...

900
00:41:15,320 --> 00:41:19,340
There are models where you pay
for support, but in my opinion,

901
00:41:19,340 --> 00:41:22,740
if you want to make money with
support, then you have to earn

902
00:41:22,740 --> 00:41:25,020
your money by working for it.

903
00:41:25,200 --> 00:41:28,300
So I earn money when I work, and
if I have a licensed model,

904
00:41:28,300 --> 00:41:30,560
then I earn money when customers
work, right?

905
00:41:30,920 --> 00:41:33,580
So I thought this was going to
scale much better.

906
00:41:33,900 --> 00:41:34,740
Michael: I like that a lot.

907
00:41:34,740 --> 00:41:38,720
I also think if you charge for
support, you're incentivized to

908
00:41:38,720 --> 00:41:39,940
hurt the experience.

909
00:41:40,160 --> 00:41:43,100
Lukas: And I think, but you're
going to say that, but that's

910
00:41:43,100 --> 00:41:43,940
how I think.

911
00:41:43,980 --> 00:41:47,040
Michael: But I think, I think it's
not surprising that as somebody

912
00:41:47,040 --> 00:41:50,080
that focuses so much on developer
experience and wants to keep

913
00:41:50,080 --> 00:41:53,400
pushing that forward and making
that good, it's unsurprising

914
00:41:53,600 --> 00:41:57,380
that you don't want to be incentivized
to make it bad like that.

915
00:41:57,380 --> 00:41:58,940
It makes perfect sense to me.

916
00:41:59,680 --> 00:41:59,860
Lukas: Yeah.

917
00:41:59,860 --> 00:42:02,640
I mean, that's a, that's my thought
as well.

918
00:42:02,640 --> 00:42:06,900
I, I don't want to name companies
or names, but I've heard some

919
00:42:07,240 --> 00:42:11,260
open source products are very,
very unusable and you really need

920
00:42:11,260 --> 00:42:12,800
the support and not all of them.

921
00:42:12,800 --> 00:42:13,260
Of course.

922
00:42:13,260 --> 00:42:16,400
I mean, Postgres and Linux are
examples, which aren't, which

923
00:42:16,400 --> 00:42:19,820
this isn't the case, but there
are some products that are super

924
00:42:19,820 --> 00:42:23,760
complicated and they're just pushed
out there and you're free

925
00:42:23,760 --> 00:42:25,800
to use it, but you won't figure
it out.

926
00:42:25,800 --> 00:42:30,160
So you have to call someone even
to install it and that's what

927
00:42:30,160 --> 00:42:30,640
I'm thinking.

928
00:42:30,640 --> 00:42:34,960
So in those cases, you need a sales
representative that pushes

929
00:42:34,960 --> 00:42:37,680
the product down someone's throat
and they make a lot of money

930
00:42:37,680 --> 00:42:38,820
with the support contract.

931
00:42:38,860 --> 00:42:43,940
And I think it's less honest than
licensing, which people think

932
00:42:43,940 --> 00:42:45,560
about, okay, is this a good product?

933
00:42:45,560 --> 00:42:48,280
Should I really spend money on
it because I have to pay it up

934
00:42:48,280 --> 00:42:52,400
front right from the day 1 I have
to pay money to use this product

935
00:42:52,440 --> 00:42:55,840
and they're gonna test it really
really really well and there's

936
00:42:55,840 --> 00:42:59,080
a lot of competition so I mean
people could use hibernate or

937
00:42:59,080 --> 00:43:03,240
whatever but people choose juke
despite the cost which in the

938
00:43:03,240 --> 00:43:04,800
end is very cheap for a company.

939
00:43:04,800 --> 00:43:09,240
So I think cost is never the issue,
but, they're still going

940
00:43:09,240 --> 00:43:12,220
to evaluate it because they have
to still go through purchasing

941
00:43:12,280 --> 00:43:16,560
and make it a business case for,
for, for the purchase.

942
00:43:16,560 --> 00:43:20,260
So I really want to have a very,
very good experience.

943
00:43:20,460 --> 00:43:24,140
And 1 part of this is I also always
answered all the questions

944
00:43:24,140 --> 00:43:27,540
on Stack Overflow within a short
period of time, because if I

945
00:43:27,540 --> 00:43:30,520
answer it, it's gonna be authoritative
and I'm gonna answer with

946
00:43:30,520 --> 00:43:33,900
all the details and all the caveats
and all the edge cases.

947
00:43:34,200 --> 00:43:37,660
So anyone else who finds the question,
we'll find this question

948
00:43:37,660 --> 00:43:38,200
and the answer.

949
00:43:38,200 --> 00:43:39,720
And they're not going to ask me
again.

950
00:43:39,720 --> 00:43:39,960
Right.

951
00:43:39,960 --> 00:43:43,660
So Google is my friend and now
Chat GPT is my friend as well,

952
00:43:43,660 --> 00:43:46,480
because Chat GPT can answer jOOQ
questions very well because

953
00:43:46,480 --> 00:43:48,620
of all the answers I've given on
Stack Overflow.

954
00:43:49,080 --> 00:43:51,260
Nikolay: Yeah, but open knowledge,
right?

955
00:43:51,460 --> 00:43:51,960
Lukas: Yeah.

956
00:43:52,580 --> 00:43:54,290
I don't want to earn money with
knowledge.

957
00:43:54,290 --> 00:43:57,480
I mean, I could sell books and
all these kind of things, but

958
00:43:57,700 --> 00:44:00,900
it's just the wrong kind of effort,
I think.

959
00:44:00,980 --> 00:44:02,120
Nikolay: Destruction, right?

960
00:44:02,640 --> 00:44:03,140
Yeah.

961
00:44:03,400 --> 00:44:04,520
From the core.

962
00:44:04,940 --> 00:44:11,180
I wonder for enterprise, like paid
versions, is source available

963
00:44:11,260 --> 00:44:12,360
or it's like kind of

964
00:44:12,360 --> 00:44:13,660
Lukas: yeah you get the source
code

965
00:44:13,660 --> 00:44:14,380
Nikolay: well that's great

966
00:44:14,380 --> 00:44:17,620
Lukas: the right to modify so you
can if you have a bargain and

967
00:44:17,620 --> 00:44:19,980
Nikolay: you can fix it yourself
and propose a fix.

968
00:44:20,460 --> 00:44:23,240
Lukas: Or maybe it's not even a
bug, but you think it's a bug,

969
00:44:25,080 --> 00:44:26,980
but you disagree, you can fix it.

970
00:44:26,980 --> 00:44:28,080
And some companies do that.

971
00:44:28,080 --> 00:44:28,520
So this

972
00:44:28,520 --> 00:44:32,460
Nikolay: is what we like in commercial
software, like full transparency

973
00:44:32,720 --> 00:44:34,140
and see how it works

974
00:44:34,280 --> 00:44:34,780
Lukas: inside.

975
00:44:34,840 --> 00:44:37,460
I mean, most people don't actually
fix stuff, but maybe they

976
00:44:37,460 --> 00:44:39,740
want to debug stuff to better understand
it.

977
00:44:39,900 --> 00:44:43,540
And if you've ever worked with
a commercial JDBC driver, like

978
00:44:43,540 --> 00:44:46,720
I do, with the Oracle driver, it's
always like, Oh my God, there's

979
00:44:46,720 --> 00:44:50,080
a null pointer exception in the
middle of the driver and I have

980
00:44:50,080 --> 00:44:52,780
no idea what I'm doing wrong from
an API perspective.

981
00:44:52,920 --> 00:44:55,960
So I have to reverse engineer it.

982
00:44:55,960 --> 00:44:59,440
Not, not with the code, but just
try an error with API calls

983
00:44:59,440 --> 00:45:02,880
until I find out why the exception,
why the bug inside of the

984
00:45:02,880 --> 00:45:03,740
driver happens.

985
00:45:03,740 --> 00:45:06,000
And with source code, it would
be much easier, right?

986
00:45:06,260 --> 00:45:09,400
So bugs happen, but if you know
exactly why the bug happens,

987
00:45:09,400 --> 00:45:13,300
then you have a much easier life
than if you have to guess.

988
00:45:13,780 --> 00:45:14,140
Nikolay: Yeah.

989
00:45:14,140 --> 00:45:15,840
Last question from me.

990
00:45:16,000 --> 00:45:18,180
What do you think about nulls in
general?

991
00:45:18,720 --> 00:45:19,220
Lukas: Nulls?

992
00:45:19,760 --> 00:45:20,780
Nulls, yeah.

993
00:45:21,900 --> 00:45:23,480
Or they're a blessing in a curse.

994
00:45:23,480 --> 00:45:26,800
I mean, I understand why they have
been introduced, because it

995
00:45:26,800 --> 00:45:28,180
was easy to do, right?

996
00:45:28,260 --> 00:45:31,480
And I mean, There are 2 takes here.

997
00:45:31,480 --> 00:45:36,600
So nulls in most languages are
mostly a pain because they lead

998
00:45:36,600 --> 00:45:37,280
to exceptions.

999
00:45:37,960 --> 00:45:41,480
And null in SQL is mostly a pain
because it doesn't lead to exceptions.

1000
00:45:41,480 --> 00:45:45,420
It's just a value like many others,
but it's a special value

1001
00:45:45,420 --> 00:45:47,060
and it just propagates.

1002
00:45:47,160 --> 00:45:50,880
It's these kind of philosophies
and both are right and wrong.

1003
00:45:51,420 --> 00:45:53,600
But what else would you want to
do, right?

1004
00:45:53,760 --> 00:45:57,880
I mean, you kind of need an absent
value, right?

1005
00:45:57,880 --> 00:46:02,320
I'm not sure if the idea of the
unknown value is so useful, but

1006
00:46:02,320 --> 00:46:04,100
an absent value is very useful.

1007
00:46:04,840 --> 00:46:07,840
I mean, sometimes you just, you
can't normalize everything to

1008
00:46:07,840 --> 00:46:09,940
6 normal form to avoid NULLs, right?

1009
00:46:09,940 --> 00:46:12,180
So it's very pragmatic.

1010
00:46:12,900 --> 00:46:14,360
Nikolay: It's very pragmatic indeed.

1011
00:46:14,380 --> 00:46:14,620
Yeah.

1012
00:46:14,620 --> 00:46:18,820
But in SQL, we have so many places
where people make mistakes

1013
00:46:18,820 --> 00:46:19,620
all the time.

1014
00:46:20,020 --> 00:46:21,760
Lukas: Yeah, SQL is weird about
it.

1015
00:46:21,760 --> 00:46:25,080
I mean, this unknown value, I used
to do a SQL training and it

1016
00:46:25,080 --> 00:46:29,900
was about 30 minutes about it was
just NULLs and how it's different

1017
00:46:29,900 --> 00:46:30,720
here from there.

1018
00:46:30,720 --> 00:46:34,400
For instance, 2 NULLs are not distinct,
right?

1019
00:46:34,400 --> 00:46:34,900
Nikolay: Yes.

1020
00:46:35,140 --> 00:46:36,600
Lukas: But they're not the same
either.

1021
00:46:36,600 --> 00:46:39,880
So there are different terms in
the SQL standard, same and distinctness.

1022
00:46:41,100 --> 00:46:43,020
And then even operators are inconsistent.

1023
00:46:43,360 --> 00:46:48,460
So personally, I think Oracle did
the pragmatic thing when they

1024
00:46:48,460 --> 00:46:51,940
concatenate a NULL value to a string,
you probably don't want

1025
00:46:51,940 --> 00:46:53,800
the result to be NULL, I think.

1026
00:46:54,140 --> 00:46:56,020
You would just want to ignore the
concatenation.

1027
00:46:56,280 --> 00:47:00,480
This is pragmatic, but it's very
unlogical and leads to a ton

1028
00:47:00,480 --> 00:47:04,540
of side effects, which probably
means that it was a bad idea

1029
00:47:04,540 --> 00:47:05,420
to start with.

1030
00:47:05,740 --> 00:47:08,660
Nikolay: And there are NULLs in
other languages, I mean, in regular

1031
00:47:08,660 --> 00:47:09,720
application languages.

1032
00:47:09,960 --> 00:47:14,580
And I remember how I struggled
trying to explain this 3 value

1033
00:47:14,580 --> 00:47:18,700
logic and like, observing what
people do in code, in regular

1034
00:47:18,700 --> 00:47:20,140
application code with NULLs.

1035
00:47:20,140 --> 00:47:23,400
They do things I would never do
like in SQL, right?

1036
00:47:23,400 --> 00:47:25,540
So different philosophies, right?

1037
00:47:25,580 --> 00:47:26,880
Lukas: Yeah, there are different
philosophies.

1038
00:47:27,620 --> 00:47:30,320
But I'm not sure if the SQL philosophy
is really useful.

1039
00:47:30,320 --> 00:47:34,640
I mean, I'm trying to explain how
it works by explaining NOT IN

1040
00:47:34,640 --> 00:47:36,140
in the NOT IN predicate.

1041
00:47:36,900 --> 00:47:39,920
When you have a NULL in NOT IN,
everything falls apart.

1042
00:47:39,920 --> 00:47:41,880
I mean, it's very logical and consistent.

1043
00:47:42,100 --> 00:47:45,380
You can explain it with 3 value
logic, but it's never useful,

1044
00:47:45,380 --> 00:47:45,880
right?

1045
00:47:46,220 --> 00:47:48,220
This kind of invalidates the whole predicate.

1046
00:47:48,340 --> 00:47:49,840
Nikolay: Yeah, it's a bomb.

1047
00:47:50,460 --> 00:47:51,360
Lukas: It's a bomb?

1048
00:47:52,440 --> 00:47:55,860
And I mean, usually people don't
put a NULL value in a NOT IN

1049
00:47:55,860 --> 00:47:57,440
list, but what about a subquery?

1050
00:47:57,440 --> 00:48:00,040
So NOT IN SELECT something, and
then you have a NULL value in

1051
00:48:00,040 --> 00:48:01,500
there, and you don't get results.

1052
00:48:00,720 --> 00:48:04,120
And it's not obvious from just
reading the code or the query.

1053
00:48:00,040 --> 00:48:01,500
there, and you don't get results.

1054
00:48:01,560 --> 00:48:02,060
Why?

1055
00:48:02,720 --> 00:48:03,480
It's horrible.

1056
00:48:03,900 --> 00:48:04,400
Horrible.

1057
00:48:05,500 --> 00:48:05,660
We

1058
00:48:05,660 --> 00:48:09,140
Nikolay: had the whole episode about no, so if Michael long ago,

1059
00:48:09,140 --> 00:48:12,860
yeah, yeah, let's stop because definitely it's not, it's not

1060
00:48:12,860 --> 00:48:14,620
going to go away.

1061
00:48:14,620 --> 00:48:18,440
And I, I'm quite sure I will make more mistakes with nulls in

1062
00:48:18,440 --> 00:48:19,140
my life.

1063
00:48:19,340 --> 00:48:22,080
Lukas: But maybe the 1 thing I really think SQL should have done

1064
00:48:22,080 --> 00:48:24,400
differently, and there are some database products that actually

1065
00:48:24,400 --> 00:48:28,180
do it differently, is the default data type should be not null,

1066
00:48:28,180 --> 00:48:28,660
right?

1067
00:48:28,660 --> 00:48:31,280
So when you create a table, When you don't write anything at

1068
00:48:31,280 --> 00:48:33,060
all, then it should be not null.

1069
00:48:33,280 --> 00:48:35,960
The fact that the default is nullable is wrong.

1070
00:48:36,740 --> 00:48:38,820
Nikolay: This forces you to define default.

1071
00:48:39,280 --> 00:48:40,740
Or, well, no.

1072
00:48:42,320 --> 00:48:44,160
Michael: The default word is confusing.

1073
00:48:44,240 --> 00:48:50,680
You mean, if I create id int8, I shouldn't have to say not

1074
00:48:50,680 --> 00:48:51,180
null.

1075
00:48:51,340 --> 00:48:55,280
Lukas: Yes, you should have to say I want this to be nullable,

1076
00:48:56,060 --> 00:48:58,780
because then it's a conscious decision and they know what you're

1077
00:48:58,780 --> 00:48:59,060
doing.

1078
00:48:59,060 --> 00:49:02,320
But the fact that maybe by accident you have something not null,

1079
00:49:02,320 --> 00:49:03,220
something nullable.

1080
00:49:03,420 --> 00:49:03,840
Yeah.

1081
00:49:03,840 --> 00:49:04,700
It's just wrong.

1082
00:49:04,920 --> 00:49:06,520
Nikolay: And this isn't standard, right?

1083
00:49:07,040 --> 00:49:08,340
Or if you remember?

1084
00:49:09,060 --> 00:49:10,300
Lukas: I don't remember.

1085
00:49:10,600 --> 00:49:13,880
I think maybe the standard says it's implementation specific.

1086
00:49:13,940 --> 00:49:15,660
Nikolay: If it's a standard, We definitely

1087
00:49:15,660 --> 00:49:17,060
Michael: can't change it now though.

1088
00:49:18,100 --> 00:49:19,040
Lukas: No, you can't.

1089
00:49:19,900 --> 00:49:23,140
But I remember Sybase is 1 of the database parts that does it

1090
00:49:23,140 --> 00:49:26,660
differently and ClickHouse as well.

1091
00:49:27,720 --> 00:49:28,220
Interesting.

1092
00:49:28,780 --> 00:49:29,780
Another 1 I forgot.

1093
00:49:30,040 --> 00:49:32,860
Nikolay: So in ClickHouse every column is by default not null?

1094
00:49:33,440 --> 00:49:33,940
Yes.

1095
00:49:34,020 --> 00:49:34,760
That's cool.

1096
00:49:34,760 --> 00:49:35,220
Okay.

1097
00:49:35,220 --> 00:49:36,940
Lukas: Although they messed it up as well.

1098
00:49:36,940 --> 00:49:39,600
So you can still insert null in a not null column.

1099
00:49:39,620 --> 00:49:40,820
You just get a 0.

1100
00:49:41,300 --> 00:49:41,640
What?

1101
00:49:41,640 --> 00:49:42,840
It makes it worse.

1102
00:49:43,140 --> 00:49:43,640
Nikolay: Okay.

1103
00:49:44,100 --> 00:49:44,440
Okay.

1104
00:49:44,440 --> 00:49:45,260
Lukas: That's terrible.

1105
00:49:45,260 --> 00:49:45,760
Amazing.

1106
00:49:47,540 --> 00:49:49,700
I think this is a performance idea.

1107
00:49:49,900 --> 00:49:53,160
You actually, if you just ignore
the check, you kind of don't

1108
00:49:53,160 --> 00:49:57,100
check for NULLs, then you're faster,
but whatever.

1109
00:49:58,260 --> 00:50:00,260
Michael: Lukas, I'm super conscious
of your time.

1110
00:50:00,260 --> 00:50:02,460
Is there any last things you wanted
to say?

1111
00:50:03,420 --> 00:50:06,660
Lukas: I hope there are many many
more exciting Postgres releases

1112
00:50:06,660 --> 00:50:07,440
in the near future.

1113
00:50:07,440 --> 00:50:09,820
I'm looking forward to all the
good stuff that's coming.

1114
00:50:10,080 --> 00:50:14,840
Especially if it's ever coming,
temporal tables from SQL 2011,

1115
00:50:15,140 --> 00:50:16,001
that will be very exciting.

1116
00:50:16,001 --> 00:50:16,004
Michael: Nice, okay cool.

1117
00:50:16,004 --> 00:50:16,011
Nikolay: Thank you for coming, I enjoyed.

1118
00:50:16,011 --> 00:50:16,014
Lukas: Thank you for having me.

1119
00:50:16,014 --> 00:50:16,017
Nikolay: Thank you.

1120
00:50:16,017 --> 00:50:16,028
Michael: Yeah, very much so, and catch you
next week Nikolay.

1121
00:50:16,028 --> 00:50:16,034
Nikolay: Bye bye, have a great week.