WEBVTT

1
00:00:00.080 --> 00:00:02.720
<v Speaker 1>Okay, let's unpack this. If you think of Postgres School

2
00:00:02.720 --> 00:00:05.040
<v Speaker 1>server is just you know, place to store data like

3
00:00:05.080 --> 00:00:07.879
<v Speaker 1>a big passive bucket, you're missing out on a tremendous

4
00:00:07.919 --> 00:00:12.439
<v Speaker 1>amount of power. Today we're diving into Postgres School server programming.

5
00:00:12.880 --> 00:00:15.839
<v Speaker 1>We're drawing from some really insightful excerpts from the technical

6
00:00:15.880 --> 00:00:19.600
<v Speaker 1>book Postgreschool Server Programming. Our mission really is to show

7
00:00:19.640 --> 00:00:21.760
<v Speaker 1>you how Postgres School can actually be seen as an

8
00:00:21.800 --> 00:00:23.359
<v Speaker 1>application development framework.

9
00:00:23.839 --> 00:00:23.960
<v Speaker 2>Now.

10
00:00:24.039 --> 00:00:28.079
<v Speaker 1>Pushing logic directly to the database layer offers well shortcuts

11
00:00:28.120 --> 00:00:33.359
<v Speaker 1>to building faster, more secure, and highly maintainable applications. Yeah.

12
00:00:33.359 --> 00:00:36.240
<v Speaker 2>And what's fascinating here is we're not just talking about

13
00:00:36.320 --> 00:00:39.679
<v Speaker 2>database administration and the traditional sense, not at all. We're

14
00:00:39.719 --> 00:00:44.000
<v Speaker 2>exploring how the database itself can become like a proactive,

15
00:00:44.039 --> 00:00:46.200
<v Speaker 2>intelligent part of your application's core logic.

16
00:00:46.359 --> 00:00:46.560
<v Speaker 1>Right.

17
00:00:46.640 --> 00:00:48.960
<v Speaker 2>It stops being just passive storage and starts being an

18
00:00:48.960 --> 00:00:52.079
<v Speaker 2>active participant, your single source of truth for business rules.

19
00:00:52.079 --> 00:00:55.039
<v Speaker 2>You know, hashtag tag the transformative power of server programming.

20
00:00:55.119 --> 00:00:59.000
<v Speaker 1>So let's challenge that whole data bucket philosophy. Yeah, imagine

21
00:00:59.000 --> 00:01:02.439
<v Speaker 1>a common situation like a bank transfer. Say Bob wants

22
00:01:02.479 --> 00:01:04.920
<v Speaker 1>to send I don't know fourteen dollars to Mary h

23
00:01:05.000 --> 00:01:07.439
<v Speaker 1>The typical way you might write separate sequl commands from

24
00:01:07.439 --> 00:01:10.719
<v Speaker 1>your application, right, want to take money from Bob a debit,

25
00:01:10.799 --> 00:01:13.280
<v Speaker 1>another to give to Mary a credit? But I mean

26
00:01:13.319 --> 00:01:16.000
<v Speaker 1>think about the problems there at first security, Do your

27
00:01:16.000 --> 00:01:19.519
<v Speaker 1>app really need to know Bob's balance or just can't

28
00:01:19.519 --> 00:01:20.239
<v Speaker 1>he send the money?

29
00:01:20.319 --> 00:01:21.000
<v Speaker 2>That's a big one.

30
00:01:21.120 --> 00:01:23.959
<v Speaker 1>Then speed, you've got multiple round trips to the database

31
00:01:24.120 --> 00:01:27.879
<v Speaker 1>slows things down. And the big one integrity. What if

32
00:01:28.439 --> 00:01:31.159
<v Speaker 1>the system crashes halfway through, Bob's money is gone, Mary

33
00:01:31.200 --> 00:01:31.799
<v Speaker 1>never gets it.

34
00:01:32.280 --> 00:01:35.840
<v Speaker 2>Nightmare scenario exactly, And that's precisely why server programming is

35
00:01:35.840 --> 00:01:39.280
<v Speaker 2>such a well a game changer. By executing that transfer

36
00:01:39.359 --> 00:01:42.680
<v Speaker 2>logic directly on the database server, you're centralizing it. It's

37
00:01:42.719 --> 00:01:46.439
<v Speaker 2>not just tidy code. It's a fundamental shift. You're putting

38
00:01:46.439 --> 00:01:50.480
<v Speaker 2>the rules in the most robust place, the database itself.

39
00:01:50.680 --> 00:01:52.560
<v Speaker 2>It becomes this active.

40
00:01:52.120 --> 00:01:53.200
<v Speaker 1>Partner active partner.

41
00:01:53.239 --> 00:01:55.280
<v Speaker 2>I like that, and it cuts out all those network

42
00:01:55.359 --> 00:01:58.599
<v Speaker 2>round trips, so it's way faster. You write a function.

43
00:01:59.040 --> 00:02:01.959
<v Speaker 2>Maybe in plpg's it does all the checks, does Bob

44
00:02:02.040 --> 00:02:05.719
<v Speaker 2>exist enough funds, is Merry valid? And it just returns okay,

45
00:02:06.719 --> 00:02:09.919
<v Speaker 2>or you know, not enough funds. The client app doesn't

46
00:02:09.919 --> 00:02:12.759
<v Speaker 2>need the details, just the success or failure. No sensitive

47
00:02:12.840 --> 00:02:13.520
<v Speaker 2>data leakage.

48
00:02:13.639 --> 00:02:18.960
<v Speaker 1>That is incredibly powerful for security, for consistency, yeah yeah,

49
00:02:19.000 --> 00:02:21.439
<v Speaker 1>But what if you want the database to react automatically

50
00:02:21.639 --> 00:02:26.439
<v Speaker 1>like inventory management, stock levels, change offers, need updating instantly,

51
00:02:26.840 --> 00:02:28.319
<v Speaker 1>no app involved triggers.

52
00:02:28.360 --> 00:02:31.680
<v Speaker 2>That's exactly what triggers are for magic pretty much. They

53
00:02:31.759 --> 00:02:35.439
<v Speaker 2>let operations in the database automatically cause other things to happen.

54
00:02:35.520 --> 00:02:37.520
<v Speaker 2>So say you've got your fruit sinstock and your fruit

55
00:02:37.520 --> 00:02:40.159
<v Speaker 2>offer tables, A trigger can make sure that when you

56
00:02:40.240 --> 00:02:43.120
<v Speaker 2>update an offer amount for apples, uh huh, the reserved

57
00:02:43.159 --> 00:02:46.639
<v Speaker 2>count in fruits in stock just adjusts automatically. It stops

58
00:02:46.639 --> 00:02:49.360
<v Speaker 2>you over reserving, stops you selling stock that's already spoken for,

59
00:02:49.759 --> 00:02:52.319
<v Speaker 2>even across different tables. It enforces that link.

60
00:02:52.360 --> 00:02:53.159
<v Speaker 1>Okay, that's clever.

61
00:02:53.439 --> 00:02:58.000
<v Speaker 2>And building on that automatic reaction idea, triggers are fantastic

62
00:02:58.080 --> 00:03:01.919
<v Speaker 2>for auditing, indispensable. You can set one up to log

63
00:03:02.039 --> 00:03:05.680
<v Speaker 2>every single action on important tables like imagine a salary's table.

64
00:03:06.080 --> 00:03:09.080
<v Speaker 2>An audit trigger can grab the username, the timestamp, the

65
00:03:09.280 --> 00:03:13.080
<v Speaker 2>operation type, insert, update, delete, and even capture the old

66
00:03:13.159 --> 00:03:15.840
<v Speaker 2>salary and the new salary. Maybe store the whole old

67
00:03:15.879 --> 00:03:18.639
<v Speaker 2>and new row as JSON for a complete record. It's

68
00:03:18.639 --> 00:03:22.319
<v Speaker 2>a rock solid audit trail. It's automatic, totally automatic. Plus

69
00:03:22.360 --> 00:03:27.759
<v Speaker 2>triggers can enforce data consistency, simple things like automatically converting

70
00:03:27.800 --> 00:03:30.919
<v Speaker 2>an employee name to uppercase whenever it's inserted or updated.

71
00:03:31.039 --> 00:03:34.400
<v Speaker 2>Ensures uniformity. You know, no need to rely on every

72
00:03:34.439 --> 00:03:36.000
<v Speaker 2>client app remembering the rule.

73
00:03:36.400 --> 00:03:41.280
<v Speaker 1>So functions, centralized logic, triggers automate reactions. But what about

74
00:03:41.360 --> 00:03:45.879
<v Speaker 1>teaching postcress new tricks entirely? What does extensibility really mean here?

75
00:03:46.000 --> 00:03:47.240
<v Speaker 1>Is it just more functions?

76
00:03:47.319 --> 00:03:50.280
<v Speaker 2>Oh, It's much more profound than just functions. Extensibility means

77
00:03:50.280 --> 00:03:53.919
<v Speaker 2>you can actually extend postgres school itself. It's language, it's capabilities.

78
00:03:54.159 --> 00:03:57.759
<v Speaker 2>You can define your own custom data types like uh

79
00:03:58.000 --> 00:04:01.240
<v Speaker 2>fruity maybe that understands bushels. And you can teach it

80
00:04:01.280 --> 00:04:05.400
<v Speaker 2>custom operators like how to compare apples and oranges directly

81
00:04:05.439 --> 00:04:10.319
<v Speaker 2>in a query? H seriously, well metaphorically maybe, But you

82
00:04:10.319 --> 00:04:14.199
<v Speaker 2>can define operators for your custom types. And yes, user

83
00:04:14.240 --> 00:04:17.879
<v Speaker 2>define functions. UDFs are a huge part, and these functions

84
00:04:17.920 --> 00:04:21.360
<v Speaker 2>can get sophisticated. They can handle complex arguments like XML

85
00:04:21.439 --> 00:04:25.240
<v Speaker 2>or JSON or return results in those formats too great

86
00:04:25.279 --> 00:04:28.480
<v Speaker 2>for modern architectures like SOA or micro services.

87
00:04:28.680 --> 00:04:32.160
<v Speaker 1>Right makes sense, and you mentioned performance earlier. Extensibility ties

88
00:04:32.160 --> 00:04:34.560
<v Speaker 1>into that too, especially server side caching.

89
00:04:34.720 --> 00:04:38.240
<v Speaker 2>Absolutely, server side casing is a classic pattern enabled by functions.

90
00:04:38.560 --> 00:04:42.399
<v Speaker 2>The logic is simple. Really, inside your function, first check

91
00:04:42.439 --> 00:04:44.560
<v Speaker 2>if the value you need is already cashed, maybe in

92
00:04:44.600 --> 00:04:47.920
<v Speaker 2>a table. Is it there, is it recent enough? If yes, great,

93
00:04:47.959 --> 00:04:50.519
<v Speaker 2>return it. If not, compute it, store it in the cash,

94
00:04:50.560 --> 00:04:51.319
<v Speaker 2>then retun it.

95
00:04:51.240 --> 00:04:52.279
<v Speaker 1>So next time it's fast.

96
00:04:52.519 --> 00:04:56.319
<v Speaker 2>Exactly. Think about calculating total company sales that might involve

97
00:04:56.399 --> 00:05:00.920
<v Speaker 2>querying thousands of rows cash the result. There's this great

98
00:05:00.959 --> 00:05:04.560
<v Speaker 2>story about a Java e email system. It was sending

99
00:05:04.560 --> 00:05:08.000
<v Speaker 2>maybe a few hundred emails a second, okay, but not amazing.

100
00:05:08.600 --> 00:05:12.079
<v Speaker 2>Then someone rewrote the email generation part as ap peel

101
00:05:12.120 --> 00:05:14.199
<v Speaker 2>Peril function inside.

102
00:05:13.720 --> 00:05:15.639
<v Speaker 1>Postgres school, inside the database.

103
00:05:15.720 --> 00:05:18.600
<v Speaker 2>Inside the database, suddenly it wasn't sending hundreds, It was

104
00:05:18.639 --> 00:05:21.199
<v Speaker 2>spewing out tens of thousands of emails per second.

105
00:05:21.360 --> 00:05:23.839
<v Speaker 1>Wow, that's that's incredible.

106
00:05:23.879 --> 00:05:27.680
<v Speaker 2>It was all because the computation happened near data minimal

107
00:05:27.720 --> 00:05:30.480
<v Speaker 2>network latency. It went from like a two point two

108
00:05:30.519 --> 00:05:33.079
<v Speaker 2>millisecond query from the client to zero point one to

109
00:05:33.160 --> 00:05:36.279
<v Speaker 2>two milliseconds inside the database. Huge difference.

110
00:05:36.319 --> 00:05:38.319
<v Speaker 1>That is a massive jump, and you can see how

111
00:05:38.360 --> 00:05:41.639
<v Speaker 1>that ties into maintenance and security too. If all your

112
00:05:41.639 --> 00:05:45.879
<v Speaker 1>core data logic is in database functions or views, upgrading

113
00:05:45.959 --> 00:05:49.040
<v Speaker 1>is just running a script, a detail.

114
00:05:48.720 --> 00:05:51.720
<v Speaker 2>Script, YEP, data definition language. You update the function in

115
00:05:51.759 --> 00:05:52.839
<v Speaker 2>the database.

116
00:05:52.519 --> 00:05:55.560
<v Speaker 1>No downtime, no complex roll out across all your different.

117
00:05:55.279 --> 00:05:57.839
<v Speaker 2>Client apps, exactly. All clients just start using the new

118
00:05:57.879 --> 00:06:01.839
<v Speaker 2>logic immediately. It simplifies maintenance enormously. It's security big win

119
00:06:01.879 --> 00:06:05.319
<v Speaker 2>there too. If you grant database users permission to execute

120
00:06:05.480 --> 00:06:09.319
<v Speaker 2>only specific functions, not direct access to tables, then even

121
00:06:09.319 --> 00:06:12.360
<v Speaker 2>if an attacker compromises your web server, say, they can't

122
00:06:12.399 --> 00:06:15.920
<v Speaker 2>just run select from users and dump all your data.

123
00:06:16.000 --> 00:06:17.839
<v Speaker 1>They can only run the functions you've a looked.

124
00:06:17.720 --> 00:06:20.519
<v Speaker 2>What you've designed to be safe right to only return

125
00:06:20.639 --> 00:06:25.720
<v Speaker 2>specific non sensitive info or perform validated actions. It drastically

126
00:06:25.759 --> 00:06:28.839
<v Speaker 2>reduces the attack surface hashtag tag tech trag y. Postgress

127
00:06:28.839 --> 00:06:30.839
<v Speaker 2>School the environment advantage.

128
00:06:31.360 --> 00:06:34.240
<v Speaker 1>So, okay, putting lodging in the database is powerful, we

129
00:06:34.279 --> 00:06:37.639
<v Speaker 1>get that, but why postgres school specifically, what makes it

130
00:06:38.160 --> 00:06:42.040
<v Speaker 1>the go to platform for this kind of server programming.

131
00:06:42.879 --> 00:06:45.240
<v Speaker 2>Well, first off, the cost of acquisition.

132
00:06:44.800 --> 00:06:46.600
<v Speaker 1>It's zero. Can't be free, right.

133
00:06:46.720 --> 00:06:51.079
<v Speaker 2>Compare that to commercial databases SEQL server Oracle. You're talking

134
00:06:51.199 --> 00:06:55.240
<v Speaker 2>potentially thousands, even tens of thousands in licensing fees. Postgress

135
00:06:55.240 --> 00:06:57.959
<v Speaker 2>will costs nothing to download, nothing to use, and this

136
00:06:58.079 --> 00:07:00.680
<v Speaker 2>directly leads to a low cost of failure. When you're

137
00:07:00.680 --> 00:07:03.959
<v Speaker 2>starting a project things are uncertain, you might experiment change direction.

138
00:07:04.600 --> 00:07:06.879
<v Speaker 2>Being able to try things out, even scrap an idea

139
00:07:07.240 --> 00:07:10.600
<v Speaker 2>without being locked into expensive long term licenses, that's a

140
00:07:10.720 --> 00:07:12.160
<v Speaker 2>huge advantage. It frees you up to.

141
00:07:12.079 --> 00:07:16.120
<v Speaker 1>Innovate definitely, and that ties into licensing more broadly, doesn't it.

142
00:07:16.199 --> 00:07:20.120
<v Speaker 1>I remember the unease when Oracle bought my sequel that

143
00:07:20.439 --> 00:07:22.240
<v Speaker 1>vast and thunderous sucking sound.

144
00:07:22.240 --> 00:07:25.279
<v Speaker 2>Does someone put it huh, yes, exactly. There is a

145
00:07:25.319 --> 00:07:28.040
<v Speaker 2>lot of uncertainty about the future of my squels licensing.

146
00:07:28.399 --> 00:07:33.480
<v Speaker 2>Postgres School's license, though, is incredibly liberal bs D style. Basically,

147
00:07:33.800 --> 00:07:36.600
<v Speaker 2>you can have the source code, do anything with it

148
00:07:36.639 --> 00:07:40.240
<v Speaker 2>that you want, redistribute it however you jolly well please,

149
00:07:40.279 --> 00:07:42.560
<v Speaker 2>and those rights extend indefinitely.

150
00:07:42.800 --> 00:07:43.160
<v Speaker 1>Wow.

151
00:07:43.319 --> 00:07:46.759
<v Speaker 2>For companies, that removes massive legal risk. You know, the

152
00:07:46.879 --> 00:07:49.279
<v Speaker 2>terms won't suddenly change. You know, you can build products

153
00:07:49.279 --> 00:07:51.319
<v Speaker 2>on it without unexpected fees popping up.

154
00:07:51.399 --> 00:07:52.639
<v Speaker 1>That's huge peace of mind.

155
00:07:52.759 --> 00:07:56.040
<v Speaker 2>Then there's predictability. Postgreds School is known for sticking really

156
00:07:56.079 --> 00:07:59.000
<v Speaker 2>closely to SQL standards. Now, sometimes that might feel a

157
00:07:59.000 --> 00:08:01.920
<v Speaker 2>bit stricter, maybe less forgiving than other databases if you

158
00:08:01.959 --> 00:08:07.319
<v Speaker 2>write slightly nonstandard sequel, but the upside is fewer unintended behaviors,

159
00:08:07.639 --> 00:08:10.199
<v Speaker 2>fewer surprises. You know it's going to work the way

160
00:08:10.240 --> 00:08:13.399
<v Speaker 2>the documentation says it works. That means less chance of

161
00:08:13.439 --> 00:08:16.079
<v Speaker 2>your application breaking in subtle ways down the road because

162
00:08:16.120 --> 00:08:19.800
<v Speaker 2>of some weird database quirk. It provides a really stable foundation.

163
00:08:19.920 --> 00:08:20.600
<v Speaker 2>You can trust it.

164
00:08:21.079 --> 00:08:26.120
<v Speaker 1>Okay, stability, predictability, Yeah, what else? You mentioned something about

165
00:08:26.120 --> 00:08:27.240
<v Speaker 1>the community earlier.

166
00:08:27.319 --> 00:08:30.040
<v Speaker 2>Ah, yeah, the community aspect is actually really interesting and

167
00:08:30.120 --> 00:08:33.759
<v Speaker 2>often overlooked. With commercial databases, trying to talk to the

168
00:08:33.799 --> 00:08:38.159
<v Speaker 2>actual core developers, good luck, you're usually dealing with layers of.

169
00:08:38.080 --> 00:08:39.879
<v Speaker 1>Support stat right ticketing systems.

170
00:08:40.000 --> 00:08:43.759
<v Speaker 2>Exactly with postgres School, many of the core developers hang

171
00:08:43.799 --> 00:08:46.360
<v Speaker 2>out on IRC channels, they go to conferences, you can

172
00:08:46.399 --> 00:08:47.399
<v Speaker 2>actually talk to them.

173
00:08:47.320 --> 00:08:48.440
<v Speaker 1>And they like beer apparently.

174
00:08:48.879 --> 00:08:51.279
<v Speaker 2>Well the book mentions that, But the point is there's

175
00:08:51.320 --> 00:08:55.080
<v Speaker 2>this direct connection. It fosters a real sense of shared

176
00:08:55.120 --> 00:08:58.759
<v Speaker 2>ownership and concern for the project's health. Expert help is

177
00:08:58.840 --> 00:09:00.600
<v Speaker 2>often just a question of a way. It's a very

178
00:09:00.720 --> 00:09:01.799
<v Speaker 2>vibrant ecosystem.

179
00:09:02.000 --> 00:09:03.200
<v Speaker 1>That's a really different feel.

180
00:09:03.480 --> 00:09:07.039
<v Speaker 2>Definitely, and technically a huge advantage is its support for

181
00:09:07.240 --> 00:09:11.200
<v Speaker 2>multiple procedural languages. It's not just plpgcquall the built in one.

182
00:09:11.519 --> 00:09:15.440
<v Speaker 2>You can write server side functions in Python, Perl, TCL,

183
00:09:15.639 --> 00:09:16.039
<v Speaker 2>even C.

184
00:09:16.519 --> 00:09:18.360
<v Speaker 1>And you can add or remove these languages on.

185
00:09:18.360 --> 00:09:21.799
<v Speaker 2>A running server, no downtime needed to install, Say peel

186
00:09:21.799 --> 00:09:25.159
<v Speaker 2>Python support and think back to our earlier example. The

187
00:09:25.240 --> 00:09:28.799
<v Speaker 2>bad way of doing things maybe with PHP connecting, pulling data,

188
00:09:29.240 --> 00:09:32.279
<v Speaker 2>figuring things out client side, then sending an update back.

189
00:09:32.200 --> 00:09:35.600
<v Speaker 1>Right, the multiple roundtrips, the potential integrity issues exactly.

190
00:09:35.639 --> 00:09:39.720
<v Speaker 2>That leads to terrible scalability. The right way is pushing

191
00:09:39.759 --> 00:09:42.720
<v Speaker 2>that logic into a database function using one of these

192
00:09:42.759 --> 00:09:45.600
<v Speaker 2>powerful languages. A single efficient.

193
00:09:45.200 --> 00:09:48.720
<v Speaker 1>Call makes sense. What about transactions? How does post grace

194
00:09:48.720 --> 00:09:49.200
<v Speaker 1>handle those?

195
00:09:49.279 --> 00:09:52.559
<v Speaker 2>It's very sensible, very safe. By default. The standard isolation

196
00:09:52.679 --> 00:09:56.360
<v Speaker 2>level is read committed me, meaning your transaction will only

197
00:09:56.399 --> 00:09:58.960
<v Speaker 2>ever see data that has been fully committed, fully saved

198
00:09:59.000 --> 00:10:03.000
<v Speaker 2>by other transactions. You don't get dirty reads seeing incomplete

199
00:10:03.080 --> 00:10:05.279
<v Speaker 2>changes that might get rolled back later. That avoids a

200
00:10:05.279 --> 00:10:06.279
<v Speaker 2>whole class of bugs.

201
00:10:06.399 --> 00:10:08.120
<v Speaker 1>That sounds safer it is.

202
00:10:08.360 --> 00:10:12.120
<v Speaker 2>And by default, each individual seql statement you send acts

203
00:10:12.159 --> 00:10:15.399
<v Speaker 2>as its own little transaction. It commits immediately, though client

204
00:10:15.440 --> 00:10:19.159
<v Speaker 2>tools like fusequal often wrap things in a transaction block automatically.

205
00:10:19.200 --> 00:10:22.039
<v Speaker 2>If you're typing multiple commands okay, and one more thing

206
00:10:22.039 --> 00:10:26.120
<v Speaker 2>on control postgrescool gives you more control over how your

207
00:10:26.120 --> 00:10:31.120
<v Speaker 2>functions execute, specifically around security, their security and voker. That's

208
00:10:31.159 --> 00:10:34.039
<v Speaker 2>the default. The function runs with the permissions of the

209
00:10:34.159 --> 00:10:37.960
<v Speaker 2>user calling it, but there's also security definer. This is

210
00:10:38.000 --> 00:10:40.159
<v Speaker 2>really useful. The function runs with the permissions of the

211
00:10:40.279 --> 00:10:41.200
<v Speaker 2>user who created it.

212
00:10:41.440 --> 00:10:45.159
<v Speaker 1>Ah, so you could let a less privileged user run

213
00:10:45.200 --> 00:10:49.720
<v Speaker 1>a function that needs higher privileges for a very specific controlled.

214
00:10:49.240 --> 00:10:54.919
<v Speaker 2>Task, precisely temporary safe privilege escalation. They can't access the

215
00:10:55.000 --> 00:10:57.720
<v Speaker 2>underlying table directly, but they can call the function which

216
00:10:57.759 --> 00:11:00.639
<v Speaker 2>performs a specific safe action on their behalf. And you

217
00:11:00.639 --> 00:11:03.120
<v Speaker 2>can also define a cost for a function. This is

218
00:11:03.159 --> 00:11:05.320
<v Speaker 2>like giving a hint to the query planner, the.

219
00:11:05.200 --> 00:11:07.519
<v Speaker 1>Part of the database that figures out how to run queries.

220
00:11:07.840 --> 00:11:09.799
<v Speaker 2>Yeah, you can tell it. Hey, this function is really

221
00:11:09.840 --> 00:11:12.000
<v Speaker 2>expensive to run, so the planner will try to avoid

222
00:11:12.039 --> 00:11:14.360
<v Speaker 2>calling it too often if it can find a cheaper

223
00:11:14.399 --> 00:11:18.240
<v Speaker 2>way to get the result. Helps optimize overall performance. Hashtag

224
00:11:18.480 --> 00:11:21.799
<v Speaker 2>hashtag deep dive into language capabilities and advanced techniques.

225
00:11:22.039 --> 00:11:23.960
<v Speaker 1>Okay, let's dive into some of the actual coding. Then

226
00:11:24.080 --> 00:11:27.240
<v Speaker 1>the warkhorse so PLPG SQUL you called it the workhoorse.

227
00:11:27.279 --> 00:11:30.279
<v Speaker 1>It's built in, no extra setup needed. What makes it

228
00:11:30.320 --> 00:11:34.000
<v Speaker 1>so fundamental for server side logic and postgress.

229
00:11:34.039 --> 00:11:39.120
<v Speaker 2>It's just incredibly well suited for combining SQL with procedural logic,

230
00:11:39.200 --> 00:11:43.360
<v Speaker 2>you know, if statements, loops, variables. It's great for encapsulating

231
00:11:43.399 --> 00:11:46.399
<v Speaker 2>those multi step operations we talked about, like the bank transfer.

232
00:11:46.720 --> 00:11:49.399
<v Speaker 2>You can access function arguments easily, either by their position

233
00:11:49.519 --> 00:11:51.720
<v Speaker 2>or by a name. You've got your standard of penols,

234
00:11:51.799 --> 00:11:54.559
<v Speaker 2>your case. Statements for conditional logic loops are there too.

235
00:11:54.799 --> 00:11:59.279
<v Speaker 2>But a really important warning. Looping through query results inside

236
00:11:59.320 --> 00:12:04.919
<v Speaker 2>plpg'sl Generally that's considered doing it wrong. It's usually very inefficient,

237
00:12:05.159 --> 00:12:08.960
<v Speaker 2>high processor cost, high memory use. SQL is designed for

238
00:12:09.080 --> 00:12:12.840
<v Speaker 2>set based operations. You should almost always try to find

239
00:12:12.879 --> 00:12:16.279
<v Speaker 2>a way to express your logic using SQL sets rather

240
00:12:16.360 --> 00:12:20.399
<v Speaker 2>than row biro looping in plpg sql let the database

241
00:12:20.440 --> 00:12:22.279
<v Speaker 2>engine optimize the set operation.

242
00:12:22.519 --> 00:12:25.519
<v Speaker 1>Good tip. Okay, what about getting results out of functions?

243
00:12:25.840 --> 00:12:29.279
<v Speaker 2>Right? You use perform if you just want to execute

244
00:12:29.279 --> 00:12:31.600
<v Speaker 2>a query for its side effects like an insert or update,

245
00:12:31.639 --> 00:12:33.440
<v Speaker 2>and you don't care about the result. If you do

246
00:12:33.519 --> 00:12:35.840
<v Speaker 2>need the result, you use select into to put it

247
00:12:35.919 --> 00:12:36.720
<v Speaker 2>into a variable.

248
00:12:36.919 --> 00:12:39.360
<v Speaker 1>Makes sense. And you can return more than just single

249
00:12:39.440 --> 00:12:41.720
<v Speaker 1>values right like lists or tables?

250
00:12:41.799 --> 00:12:44.279
<v Speaker 2>Oh yeah, absolutely, you can return a set of integer

251
00:12:44.639 --> 00:12:48.240
<v Speaker 2>like generating the Fibonacci sequence. You can return set of

252
00:12:48.399 --> 00:12:51.919
<v Speaker 2>table name, which effectively returns rows matching that table structure.

253
00:12:52.399 --> 00:12:55.559
<v Speaker 2>You can define functions with out parameters which become part

254
00:12:55.559 --> 00:12:58.639
<v Speaker 2>of the result row, or use the very clear returns

255
00:12:58.679 --> 00:13:01.360
<v Speaker 2>table syntax to define the output columns right there.

256
00:13:01.440 --> 00:13:03.279
<v Speaker 1>What if the structure isn't known beforehand?

257
00:13:03.440 --> 00:13:06.720
<v Speaker 2>Good question. You can use return set off record, but

258
00:13:06.759 --> 00:13:09.039
<v Speaker 2>then you have to provide a column definition list when

259
00:13:09.080 --> 00:13:12.399
<v Speaker 2>you call the function. More flexibly, you can use polymorphic

260
00:13:12.440 --> 00:13:14.840
<v Speaker 2>types like set off an element or set off any

261
00:13:14.879 --> 00:13:18.360
<v Speaker 2>compatible for functions that can work with or return different

262
00:13:18.480 --> 00:13:22.440
<v Speaker 2>but related types. And there's variatic arguments, which lets a

263
00:13:22.480 --> 00:13:25.320
<v Speaker 2>function accept an arbitrary number of arguments of a certain

264
00:13:25.360 --> 00:13:27.639
<v Speaker 2>type past as an array super flexible.

265
00:13:27.720 --> 00:13:32.039
<v Speaker 1>You also mentioned cursors briefly before for large results.

266
00:13:31.799 --> 00:13:35.320
<v Speaker 2>Right reef Cursors specifically, cursors are internal structures that let

267
00:13:35.320 --> 00:13:39.519
<v Speaker 2>you fetch query results incrementally lazily, instead of the database

268
00:13:39.519 --> 00:13:42.080
<v Speaker 2>computing the entire results set and sending it all at once,

269
00:13:42.120 --> 00:13:45.279
<v Speaker 2>which could take ages and lots of memory. For huge queries,

270
00:13:45.519 --> 00:13:48.919
<v Speaker 2>you get a pointer kinda yeah, a reference. Then your

271
00:13:48.960 --> 00:13:52.440
<v Speaker 2>application can fetch rows from that cursor as needed. It's

272
00:13:52.480 --> 00:13:54.679
<v Speaker 2>great if you only need the first few rows quickly,

273
00:13:54.960 --> 00:13:57.600
<v Speaker 2>or if you want to return multiple independent result sets

274
00:13:57.600 --> 00:14:02.919
<v Speaker 2>from a single function. Call PLPGCQ trigger functions Automated guardians no.

275
00:14:03.039 --> 00:14:05.639
<v Speaker 1>Get back to triggers. These automatic guardians you said, they

276
00:14:05.679 --> 00:14:08.720
<v Speaker 1>get old and NW records. How does that actually work

277
00:14:08.759 --> 00:14:09.360
<v Speaker 1>in the code.

278
00:14:09.519 --> 00:14:12.519
<v Speaker 2>So when you write a trigger function one that returns

279
00:14:12.559 --> 00:14:17.519
<v Speaker 2>trigger postgresscool automatically makes special variables available inside it. Old

280
00:14:17.759 --> 00:14:20.000
<v Speaker 2>is a record variable holding the row values before the

281
00:14:20.039 --> 00:14:23.559
<v Speaker 2>operation for update and delete. New holds the values after

282
00:14:23.559 --> 00:14:26.639
<v Speaker 2>the operation for insert and update. And you also get

283
00:14:26.720 --> 00:14:29.720
<v Speaker 2>tg variables like TGP tells you if it was an insert,

284
00:14:29.799 --> 00:14:32.440
<v Speaker 2>update or delete tg table name gives you the table

285
00:14:32.480 --> 00:14:34.559
<v Speaker 2>name and so on. Lots of context, so.

286
00:14:34.559 --> 00:14:37.759
<v Speaker 1>That audit trigger example, it uses those exactly.

287
00:14:37.840 --> 00:14:40.360
<v Speaker 2>It would check TGOP. If it's an insert, it logs

288
00:14:40.360 --> 00:14:42.960
<v Speaker 2>the new record maybe converted to Jason. If delete, it

289
00:14:43.000 --> 00:14:45.840
<v Speaker 2>logs old. If update, it logs both old and new,

290
00:14:46.440 --> 00:14:51.159
<v Speaker 2>along with the username from current user timestamp now table

291
00:14:51.240 --> 00:14:54.120
<v Speaker 2>name and operation type creates that perfect audit law.

292
00:14:54.480 --> 00:14:56.399
<v Speaker 1>Very cool, and you mentioned preventing actions.

293
00:14:56.840 --> 00:14:58.879
<v Speaker 2>Yeah, you could have a simple trigger function that just

294
00:14:59.000 --> 00:15:02.080
<v Speaker 2>raises an exception if t GOP is delete or truncate

295
00:15:02.159 --> 00:15:04.960
<v Speaker 2>on certain critical tables, just stops the operation cold or

296
00:15:05.240 --> 00:15:08.360
<v Speaker 2>more subtly. In a before trigger one that runs before

297
00:15:08.399 --> 00:15:11.360
<v Speaker 2>the change is actually made, you can modify the new w.

298
00:15:11.480 --> 00:15:14.320
<v Speaker 1>Record uh like the upper case name example.

299
00:15:14.080 --> 00:15:17.559
<v Speaker 2>Precisely we're setting timestamps. A common pattern is a function

300
00:15:17.600 --> 00:15:20.039
<v Speaker 2>that sets create at and created by only on insert,

301
00:15:20.399 --> 00:15:23.639
<v Speaker 2>and updated at and updated by on update, ensuring those

302
00:15:23.679 --> 00:15:27.519
<v Speaker 2>created fields are immutable after the initial insert. But again,

303
00:15:27.559 --> 00:15:31.320
<v Speaker 2>the reminder triggers are powerful, but use them judiciously. For

304
00:15:31.399 --> 00:15:35.120
<v Speaker 2>complex application logic, they can make debugging harder because the

305
00:15:35.159 --> 00:15:38.559
<v Speaker 2>logic isn't always obvious in the main application code. Keep

306
00:15:38.559 --> 00:15:39.200
<v Speaker 2>them focused.

307
00:15:39.240 --> 00:15:41.480
<v Speaker 1>If you can seeing inside.

308
00:15:41.039 --> 00:15:44.159
<v Speaker 2>That debugging point is key. If you've got complex logic

309
00:15:44.240 --> 00:15:47.039
<v Speaker 2>hidden away and functions and triggers, how do you troubleshoot

310
00:15:47.039 --> 00:15:48.879
<v Speaker 2>when things go wrong. It's not like setting a break

311
00:15:48.919 --> 00:15:50.919
<v Speaker 2>point in your Java or Python code.

312
00:15:50.679 --> 00:15:53.559
<v Speaker 1>Is it. It's definitely different. The simplest approach is what you

313
00:15:53.639 --> 00:15:56.720
<v Speaker 1>might call manual debugging. Using raise notice, you just sprinkle

314
00:15:56.799 --> 00:16:02.440
<v Speaker 1>raise notice reached point a with value my variable throughout

315
00:16:02.480 --> 00:16:05.519
<v Speaker 1>your code. The messages show up in your client like

316
00:16:05.679 --> 00:16:07.799
<v Speaker 1>psdes col like print statements.

317
00:16:07.919 --> 00:16:08.200
<v Speaker 2>Basic.

318
00:16:08.360 --> 00:16:13.120
<v Speaker 1>It's exactly simple, no installation needed. Great for checking values

319
00:16:13.120 --> 00:16:16.320
<v Speaker 1>in regression test scripts. The downside is it can clutter

320
00:16:16.360 --> 00:16:19.919
<v Speaker 1>your output if you leave them in. For actual error handling,

321
00:16:19.960 --> 00:16:22.919
<v Speaker 1>you use reez exception. You can provide a custom error

322
00:16:23.000 --> 00:16:26.480
<v Speaker 1>message and even a specific SQL state code which client

323
00:16:26.519 --> 00:16:30.519
<v Speaker 1>applications can catch and react to programmatically. Okay, And if

324
00:16:30.559 --> 00:16:32.679
<v Speaker 1>you want messages to go to the server's log file

325
00:16:32.759 --> 00:16:35.600
<v Speaker 1>instead of the client maybe for background tasks or just

326
00:16:35.679 --> 00:16:37.240
<v Speaker 1>less noise, you use.

327
00:16:37.159 --> 00:16:40.799
<v Speaker 2>Raise luck right, So print debugging essentially, what about stepping

328
00:16:40.799 --> 00:16:41.440
<v Speaker 2>through code?

329
00:16:41.720 --> 00:16:45.440
<v Speaker 1>For that? You need the visual PLPG SQL debugger usually

330
00:16:45.440 --> 00:16:49.320
<v Speaker 1>integrates with GUI tools like PG admin once it's set up,

331
00:16:49.320 --> 00:16:50.720
<v Speaker 1>and the setup can be a bit of a pain.

332
00:16:50.799 --> 00:16:53.600
<v Speaker 1>Fair warning is pretty powerful. You can set break points

333
00:16:53.600 --> 00:16:54.919
<v Speaker 1>in your plpg sql.

334
00:16:54.720 --> 00:16:57.759
<v Speaker 2>Code like in a normal id YEP, step.

335
00:16:57.440 --> 00:17:00.279
<v Speaker 1>Through line by line, step into, step over, inspective values

336
00:17:00.320 --> 00:17:02.879
<v Speaker 1>of variables, even change variable values on the fly to

337
00:17:02.919 --> 00:17:06.519
<v Speaker 1>test different scenarios. You can also set global breakpoints like

338
00:17:06.759 --> 00:17:10.000
<v Speaker 1>stop whenever any code calls. This specific function really handy

339
00:17:10.039 --> 00:17:11.839
<v Speaker 1>for tracking down unexpected.

340
00:17:11.319 --> 00:17:12.759
<v Speaker 2>Calls and the advantages.

341
00:17:13.240 --> 00:17:15.400
<v Speaker 1>The big advantage over raised notice is it doesn't use

342
00:17:15.400 --> 00:17:18.440
<v Speaker 1>server resources when you're not actively debugging, and you don't

343
00:17:18.440 --> 00:17:21.440
<v Speaker 1>need to modify your function code with print statements. The

344
00:17:21.519 --> 00:17:26.119
<v Speaker 1>disadvantage that installation process can be fiddly. Using unrestricted languages

345
00:17:26.720 --> 00:17:29.160
<v Speaker 1>expanding horizons PL Python.

346
00:17:28.880 --> 00:17:32.640
<v Speaker 2>Okay, let's shift gears beyond PLTG squel. You mentioned unrestricted

347
00:17:32.720 --> 00:17:37.160
<v Speaker 2>or untrusted languages like pl python. That untrusted label, why

348
00:17:37.200 --> 00:17:38.599
<v Speaker 2>is it there? What's the deal? Right?

349
00:17:38.640 --> 00:17:43.079
<v Speaker 1>The untrusted language is pl python, PEEL, perileu, PLTCLU, peel, JAVAU.

350
00:17:43.279 --> 00:17:45.799
<v Speaker 1>They're called that because they break out of the database sandbox.

351
00:17:46.359 --> 00:17:50.680
<v Speaker 1>They can by default access the server's file system, make

352
00:17:50.799 --> 00:17:55.640
<v Speaker 1>network connections, send signals to other processes, basically do anything

353
00:17:55.680 --> 00:17:58.240
<v Speaker 1>the underlying language, Python, Perol, etc. Could do on the

354
00:17:58.279 --> 00:17:59.279
<v Speaker 1>host operating system.

355
00:17:59.359 --> 00:18:01.079
<v Speaker 2>Ahotential security risk.

356
00:18:01.119 --> 00:18:03.680
<v Speaker 1>Huge potential security risk. That's why you absolutely should not

357
00:18:03.720 --> 00:18:06.839
<v Speaker 1>grant permission to create functions in these languages to arbitrary

358
00:18:06.920 --> 00:18:09.799
<v Speaker 1>database users. You don't fully trust. They could potentially read

359
00:18:09.839 --> 00:18:11.920
<v Speaker 1>sensitive files or disrupt the server.

360
00:18:12.079 --> 00:18:13.279
<v Speaker 2>Got it, so why use them?

361
00:18:13.319 --> 00:18:16.920
<v Speaker 1>Power and convenience, Python, for example, has vast libraries for

362
00:18:16.960 --> 00:18:21.200
<v Speaker 1>everything imaginable. It's often much easier and requires less boilerplate

363
00:18:21.240 --> 00:18:24.799
<v Speaker 1>code to do complex text processing, interact with web services,

364
00:18:25.200 --> 00:18:27.680
<v Speaker 1>or use external libraries and peel Python that it would

365
00:18:27.720 --> 00:18:32.359
<v Speaker 1>be in plpg sql or especially C plus. Many developers

366
00:18:32.400 --> 00:18:35.440
<v Speaker 1>are already comfortable with Python and peel Python handles the

367
00:18:35.559 --> 00:18:39.319
<v Speaker 1>data type conversions between postcrescool types and Python types pretty

368
00:18:39.319 --> 00:18:40.160
<v Speaker 1>transparently for you.

369
00:18:40.480 --> 00:18:42.720
<v Speaker 2>Okay, so if I'm writing a pel Python function, how

370
00:18:42.720 --> 00:18:45.559
<v Speaker 2>do I run SQL queries from within that Python code.

371
00:18:45.640 --> 00:18:49.519
<v Speaker 1>Peel python provides a dedicated module usually accessed as plea high.

372
00:18:49.920 --> 00:18:53.039
<v Speaker 1>It has functions like peelpi dot execute to run arbitrary

373
00:18:53.039 --> 00:18:56.440
<v Speaker 1>SQL queries. It also has peelpi dot prepare, which lets

374
00:18:56.480 --> 00:18:59.440
<v Speaker 1>you create a prepared statement plan. This is really important

375
00:18:59.440 --> 00:19:01.240
<v Speaker 1>for performing. So if you're running the same kind of

376
00:19:01.279 --> 00:19:04.319
<v Speaker 1>query multiple times, maybe just with different parameters.

377
00:19:03.880 --> 00:19:06.559
<v Speaker 2>Prepare ones execute many times exactly.

378
00:19:06.880 --> 00:19:10.200
<v Speaker 1>Preparing can be costly, so caching that plan makes subsequent

379
00:19:10.240 --> 00:19:13.000
<v Speaker 1>executions much faster. And when you're writing a trigger function

380
00:19:13.000 --> 00:19:16.440
<v Speaker 1>in peel Python, instead of old and new to W variables,

381
00:19:16.519 --> 00:19:19.559
<v Speaker 1>you get a Python dictionary usually called TD, which contains

382
00:19:19.559 --> 00:19:22.599
<v Speaker 1>all the trigger context td ol, td new, tdvn td, win,

383
00:19:22.680 --> 00:19:25.880
<v Speaker 1>et cetera. Right, what about dynamic sqel building queris on

384
00:19:25.880 --> 00:19:29.799
<v Speaker 1>the fly? Huge risk of seql injection There absolutely critical point.

385
00:19:29.920 --> 00:19:32.960
<v Speaker 1>You should never just concatenate strings together with user input

386
00:19:33.119 --> 00:19:36.240
<v Speaker 1>to build SQL queries in any language, including PL Python.

387
00:19:36.599 --> 00:19:40.359
<v Speaker 1>Plupy provides functions specifically for safe quoting Peelpi dot quote

388
00:19:40.400 --> 00:19:43.359
<v Speaker 1>item for table or column names, peelpi dot quote literal

389
00:19:43.480 --> 00:19:46.839
<v Speaker 1>for string values, and peelpi dot quote nullable which handles

390
00:19:46.920 --> 00:19:51.200
<v Speaker 1>nls correctly. Always use these when incorporating external data into

391
00:19:51.279 --> 00:19:54.480
<v Speaker 1>dynamic queries. It's your primary defense. Good advice. How does

392
00:19:54.559 --> 00:19:57.160
<v Speaker 1>error handling work within PL Python? Does it interact with

393
00:19:57.240 --> 00:20:01.240
<v Speaker 1>database transactions? You use standard Python t dot accept blocks

394
00:20:01.480 --> 00:20:04.160
<v Speaker 1>to catch errors that might occur within your Python code

395
00:20:04.480 --> 00:20:08.839
<v Speaker 1>or from SQL queries executed via PLI. For transactional control

396
00:20:08.880 --> 00:20:13.480
<v Speaker 1>within your function, pl Python provides PLPI dot subtransaction. This

397
00:20:13.559 --> 00:20:16.119
<v Speaker 1>lets you create save points. You can wrap a block

398
00:20:16.119 --> 00:20:18.960
<v Speaker 1>of code and a subtransaction context manager, and if an

399
00:20:18.960 --> 00:20:22.559
<v Speaker 1>exception occurs inside that block, only the database changes made

400
00:20:22.640 --> 00:20:24.680
<v Speaker 1>within that block are rolled back ah.

401
00:20:24.599 --> 00:20:26.559
<v Speaker 2>So you can contain failures exactly.

402
00:20:26.920 --> 00:20:29.079
<v Speaker 1>It's important to note, though, that this only rolls back

403
00:20:29.160 --> 00:20:32.240
<v Speaker 1>database changes. Any changes to your Python variables within that

404
00:20:32.279 --> 00:20:35.240
<v Speaker 1>block will persist unless you explicitly handle them in your

405
00:20:35.240 --> 00:20:39.640
<v Speaker 1>accept clause. Python's atomicity is separate from the databases.

406
00:20:39.400 --> 00:20:43.400
<v Speaker 2>Right and debugging pl pythons still no visible debugger I guess.

407
00:20:43.240 --> 00:20:45.799
<v Speaker 1>Not built in no, so you fall back to similar

408
00:20:45.839 --> 00:20:49.480
<v Speaker 1>techniques as with plpg sqel, but using pythons tools dot

409
00:20:49.519 --> 00:20:52.799
<v Speaker 1>PLIPI dot notice, PLPI dot warning, pelpi dot error to

410
00:20:52.799 --> 00:20:55.279
<v Speaker 1>send messages standard Python A certain statements are useful too.

411
00:20:55.799 --> 00:20:58.440
<v Speaker 1>You can also redirect pythonsys dot sit out and syst

412
00:20:58.480 --> 00:21:00.720
<v Speaker 1>dot sitch a dor within your function and to send

413
00:21:00.759 --> 00:21:03.240
<v Speaker 1>output to the postgrec will server log file, which could

414
00:21:03.279 --> 00:21:05.799
<v Speaker 1>be helpful. But the real power here is doing things

415
00:21:05.880 --> 00:21:08.319
<v Speaker 1>you just wouldn't normally think of doing inside a database,

416
00:21:08.759 --> 00:21:13.680
<v Speaker 1>generating image thumbnails using Python's PIL library, sending emails using SENTIMENTPLIP,

417
00:21:13.960 --> 00:21:18.119
<v Speaker 1>Interacting with external APIs all possible directly from database functions.

418
00:21:18.160 --> 00:21:21.759
<v Speaker 1>With pl python. It really encourages thinking outside the SQL box,

419
00:21:22.200 --> 00:21:23.960
<v Speaker 1>writing advanced functions and c raw power.

420
00:21:24.400 --> 00:21:26.319
<v Speaker 2>Okay, peel python opens up a lot, But then there's

421
00:21:26.319 --> 00:21:28.480
<v Speaker 2>C writing functions directly and C that sounds like the

422
00:21:28.559 --> 00:21:33.119
<v Speaker 2>ultimate level of integration and probably complexity. When you absolutely

423
00:21:33.119 --> 00:21:34.200
<v Speaker 2>need C, you go to C.

424
00:21:34.839 --> 00:21:38.119
<v Speaker 1>When you need the absolute bare metal performance, or what

425
00:21:38.119 --> 00:21:41.000
<v Speaker 1>do you need to interact with Postgrescol's internal APIs at

426
00:21:41.000 --> 00:21:44.960
<v Speaker 1>a level that interprets languages just can't reach. Think extremely

427
00:21:45.000 --> 00:21:49.160
<v Speaker 1>computationally intensive algorithms or functions that need to process millions

428
00:21:49.200 --> 00:21:52.319
<v Speaker 1>of rows per second with minimal overhead, or maybe tightly

429
00:21:52.359 --> 00:21:54.000
<v Speaker 1>integrate with operating.

430
00:21:53.559 --> 00:21:56.279
<v Speaker 2>System features, and how does it work? Is it complicated?

431
00:21:56.640 --> 00:22:00.200
<v Speaker 1>There's definitely more ceremony involved than with plpg sql or

432
00:22:00.240 --> 00:22:03.799
<v Speaker 1>peel Python. You need to include specific postgress will header

433
00:22:03.799 --> 00:22:07.359
<v Speaker 1>files postgress dot HfN gr dot H. You need a

434
00:22:07.400 --> 00:22:11.359
<v Speaker 1>special macro pg modumi magic to identify the compiled object

435
00:22:11.400 --> 00:22:14.200
<v Speaker 1>as compatible. Each C function needs to be declared with

436
00:22:14.240 --> 00:22:17.079
<v Speaker 1>PGF function in ov one and to use specific C

437
00:22:17.240 --> 00:22:20.119
<v Speaker 1>macros like pg targin thirty two to get arguments and

438
00:22:20.160 --> 00:22:23.720
<v Speaker 1>pg turn in thirty two to return values. Some more boilerplate,

439
00:22:24.119 --> 00:22:26.519
<v Speaker 1>A bit more, yes, but the built process is actually

440
00:22:26.599 --> 00:22:30.359
<v Speaker 1>quite streamlined now thanks to pdxs posts wll's extension building infrastructure.

441
00:22:30.599 --> 00:22:32.640
<v Speaker 1>You typically just write a simple make file, run make

442
00:22:32.680 --> 00:22:35.160
<v Speaker 1>and then pseudo make install and postcress will knows how

443
00:22:35.160 --> 00:22:37.279
<v Speaker 1>to compile and link your C code correctly. Then you

444
00:22:37.359 --> 00:22:38.839
<v Speaker 1>just create function and SQL to link it.

445
00:22:39.000 --> 00:22:42.400
<v Speaker 2>Okay, not too bad. What about advanced features in C functions?

446
00:22:42.480 --> 00:22:43.640
<v Speaker 2>Handling NL's, For.

447
00:22:43.599 --> 00:22:48.480
<v Speaker 1>Instance, C requires explicit handling of nls. Unlike plpg sql,

448
00:22:48.799 --> 00:22:52.400
<v Speaker 1>you use the pgr nl macro to check if a

449
00:22:52.440 --> 00:22:55.559
<v Speaker 1>specific argument is NLL before you try to access its

450
00:22:55.599 --> 00:22:58.000
<v Speaker 1>value with pg turner, and if you need to return

451
00:22:58.119 --> 00:23:01.240
<v Speaker 1>NL you use pgr turnal forgetting. This as a common

452
00:23:01.279 --> 00:23:03.680
<v Speaker 1>source of crashes, C is less forgiving.

453
00:23:03.799 --> 00:23:06.079
<v Speaker 2>What about very attic arguments.

454
00:23:05.759 --> 00:23:08.119
<v Speaker 1>Those arrive in your CE function as a Postgres school

455
00:23:08.200 --> 00:23:10.880
<v Speaker 1>array type. You need to use specific C functions to

456
00:23:10.920 --> 00:23:14.240
<v Speaker 1>deconstruct that array, getting its dimensions, checking its element type,

457
00:23:14.279 --> 00:23:16.720
<v Speaker 1>and then iterating through the elements. A bit more work

458
00:23:16.720 --> 00:23:18.000
<v Speaker 1>than in popg sqel.

459
00:23:18.039 --> 00:23:20.559
<v Speaker 2>Memory management must be crucial in SEA leaks.

460
00:23:20.440 --> 00:23:24.599
<v Speaker 1>Crashes absolutely vital. You must use postgrescols memory management functions

461
00:23:24.839 --> 00:23:28.200
<v Speaker 1>primarily pallic and free, not standard Sea mallek and free.

462
00:23:28.799 --> 00:23:33.200
<v Speaker 1>Palic allocates memory within postgres School's current memory context. The

463
00:23:33.240 --> 00:23:36.400
<v Speaker 1>beauty is that memory allocated in shorter lived contexts like

464
00:23:36.440 --> 00:23:41.079
<v Speaker 1>the protuple or perfunction call context, is automatically freed when

465
00:23:41.160 --> 00:23:44.680
<v Speaker 1>that context is destroyed. This prevents many common memory leaks.

466
00:23:44.680 --> 00:23:48.079
<v Speaker 1>Ah that's clever it is and always zero fyll allocated

467
00:23:48.119 --> 00:23:51.759
<v Speaker 1>structures using memset to avoid subtle bugs from uninitialized memory.

468
00:23:52.359 --> 00:23:55.200
<v Speaker 1>For air reporting, you don't just print. You use postgres

469
00:23:55.279 --> 00:23:59.119
<v Speaker 1>School's air function dot airport is very structured. You specify

470
00:23:59.119 --> 00:24:02.759
<v Speaker 1>at the severity level error warning notice, log et cetera,

471
00:24:03.119 --> 00:24:07.319
<v Speaker 1>and soul state code, primary message detail hint. It handles

472
00:24:07.359 --> 00:24:10.759
<v Speaker 1>localization and routes the message correctly to client or log

473
00:24:11.559 --> 00:24:15.720
<v Speaker 1>error level aborts the current transaction. Importantly, unlike exceptions in

474
00:24:15.720 --> 00:24:18.759
<v Speaker 1>plpg sql, air report messages are generally sent to the

475
00:24:18.759 --> 00:24:21.480
<v Speaker 1>client immediately, not buffered until the end of the function.

476
00:24:21.640 --> 00:24:24.960
<v Speaker 2>Okay, and how do these C functions run SQL queries

477
00:24:25.000 --> 00:24:25.680
<v Speaker 2>back into.

478
00:24:25.559 --> 00:24:29.200
<v Speaker 1>The database that's done using the SBI, the server programming interface.

479
00:24:29.359 --> 00:24:32.160
<v Speaker 1>Inside your C function, you call SBI connect that you

480
00:24:32.160 --> 00:24:35.440
<v Speaker 1>can execute queries using functions like spy ASEC or SPIKESIGI

481
00:24:35.480 --> 00:24:38.920
<v Speaker 1>plan for prepared statements. You process the results, and critically,

482
00:24:38.920 --> 00:24:41.680
<v Speaker 1>you must call SBI finish before your C function returns.

483
00:24:42.400 --> 00:24:46.880
<v Speaker 1>SBI handers transaction context correctly, but understanding data visibility rules.

484
00:24:46.960 --> 00:24:49.400
<v Speaker 1>What changes your function can see from other transactions or

485
00:24:49.440 --> 00:24:52.319
<v Speaker 1>even earlier commands within the same transaction is important for

486
00:24:52.359 --> 00:24:53.839
<v Speaker 1>complex scenarios, so.

487
00:24:54.000 --> 00:24:58.640
<v Speaker 2>C offers the ultimate power what other advanced things become possible.

488
00:24:58.839 --> 00:25:02.000
<v Speaker 1>You can do some really sophistic icated stuff. Write extremely

489
00:25:02.039 --> 00:25:06.000
<v Speaker 1>high performance triggers in C for logging changes with minimal overhead.

490
00:25:06.319 --> 00:25:10.200
<v Speaker 1>Register C functions to be called automatically on command or

491
00:25:10.240 --> 00:25:13.240
<v Speaker 1>on rollback of a transaction. Great for cleanup or post

492
00:25:13.279 --> 00:25:16.599
<v Speaker 1>commit actions. You can even interact directly with postcres schools

493
00:25:16.599 --> 00:25:20.440
<v Speaker 1>shared memory and use lightweight locks for synchronization between different

494
00:25:20.480 --> 00:25:26.920
<v Speaker 1>back end processes, enabling complex interprocess communication managed by the database. Honestly,

495
00:25:26.960 --> 00:25:29.839
<v Speaker 1>the best way to learn advanced SEA extension techniques.

496
00:25:29.400 --> 00:25:30.400
<v Speaker 2>Is let me guess.

497
00:25:30.799 --> 00:25:33.720
<v Speaker 1>Use the source looke aha exactly. Look at the code

498
00:25:33.720 --> 00:25:36.880
<v Speaker 1>for Postcrescol's own contrip modules. They cover a huge range

499
00:25:36.880 --> 00:25:39.799
<v Speaker 1>of examples, from custom data types to procedural languages. It's

500
00:25:39.799 --> 00:25:42.519
<v Speaker 1>all there, open source, ready to be studied, hashtag tag

501
00:25:42.559 --> 00:25:45.279
<v Speaker 1>tax stealing your database with po proxy and polishing extensions.

502
00:25:45.319 --> 00:25:48.599
<v Speaker 2>Okay, let's shift focus again. We've built these powerful functions,

503
00:25:48.599 --> 00:25:51.400
<v Speaker 2>maybe even in c our single server is humming. But

504
00:25:51.480 --> 00:25:53.960
<v Speaker 2>what happens when the load gets truly massive? How do

505
00:25:53.960 --> 00:25:55.640
<v Speaker 2>you scale postcrescool horizontally?

506
00:25:55.839 --> 00:25:59.440
<v Speaker 1>Right? So you've optimized your single server, added indexes, tune queries,

507
00:25:59.440 --> 00:26:02.119
<v Speaker 1>maybe even us some of those c functions, but your

508
00:26:02.200 --> 00:26:06.640
<v Speaker 1>database is still creaking under the load. Maybe CPU is

509
00:26:06.680 --> 00:26:10.079
<v Speaker 1>pegged at eighty percent plus four hours a day. Traditional

510
00:26:10.079 --> 00:26:14.759
<v Speaker 1>scaling options have limits buying bigger server Vertical scaling gets expensive.

511
00:26:14.839 --> 00:26:18.720
<v Speaker 1>Fast master slave replication helps with read loads, but not

512
00:26:18.759 --> 00:26:24.519
<v Speaker 1>write loads. Multi master replication adds complexity for really heavy workloads,

513
00:26:24.640 --> 00:26:28.400
<v Speaker 1>especially right heavy ones. Horizontal partitioning or scharding becomes the

514
00:26:28.480 --> 00:26:32.480
<v Speaker 1>logical step. You split your data across multiple independent postgress

515
00:26:32.480 --> 00:26:33.680
<v Speaker 1>wile servers partitions.

516
00:26:34.039 --> 00:26:35.960
<v Speaker 2>How do you manage that split? Doesn't that make the

517
00:26:36.000 --> 00:26:37.799
<v Speaker 2>application logic way more complex?

518
00:26:38.039 --> 00:26:40.680
<v Speaker 1>It can if you're not careful that's where proxy databases

519
00:26:40.720 --> 00:26:43.720
<v Speaker 1>come in. The application talks to a proxy layer which

520
00:26:43.759 --> 00:26:46.119
<v Speaker 1>knows how to route queries to the correct data partitions.

521
00:26:46.400 --> 00:26:49.640
<v Speaker 1>This keeps the partitioning logic transparent to the application itself.

522
00:26:49.720 --> 00:26:53.400
<v Speaker 2>Okay, let's ground this. You mentioned a chat application example earlier. Yah,

523
00:26:53.519 --> 00:26:56.519
<v Speaker 2>how did optimization play out before even thinking about charting?

524
00:26:56.799 --> 00:26:59.680
<v Speaker 1>Yeah, the example started with simple user info and message

525
00:26:59.680 --> 00:27:05.400
<v Speaker 1>table and basic functions user log in, send message, et cetera. Initially,

526
00:27:05.599 --> 00:27:08.000
<v Speaker 1>fetching just seven new messages took something like seven or

527
00:27:08.000 --> 00:27:11.920
<v Speaker 1>and sixty milliseconds pretty slow. But then just by adding

528
00:27:11.960 --> 00:27:14.519
<v Speaker 1>the right indexes on foreign keys and message time stamps,

529
00:27:14.720 --> 00:27:17.200
<v Speaker 1>and crucially setting the fill factor on the message table

530
00:27:17.359 --> 00:27:18.599
<v Speaker 1>to allow for hot t.

531
00:27:18.759 --> 00:27:20.880
<v Speaker 2>Updates, stay shot TA updates, keep.

532
00:27:20.720 --> 00:27:23.519
<v Speaker 1>Only touple updates. It's a postgres will optimization where if

533
00:27:23.519 --> 00:27:26.680
<v Speaker 1>an update doesn't change any index columns, postcres can sometimes

534
00:27:26.759 --> 00:27:28.960
<v Speaker 1>update the road directly in place on a data page

535
00:27:29.200 --> 00:27:32.480
<v Speaker 1>without needing a new index entry. It massively reduces right

536
00:27:32.519 --> 00:27:35.359
<v Speaker 1>overhead for certain update patterns. Just those changes brought the

537
00:27:35.480 --> 00:27:37.440
<v Speaker 1>query time down from seven to sixty meters to under

538
00:27:37.440 --> 00:27:39.880
<v Speaker 1>three meters, and then with further tuning down to about

539
00:27:39.920 --> 00:27:43.279
<v Speaker 1>one point nine meters, a huge improvement without adding any servers.

540
00:27:43.799 --> 00:27:45.039
<v Speaker 1>Always optimize first.

541
00:27:45.200 --> 00:27:47.559
<v Speaker 2>Good point. But okay, say we do need to shard.

542
00:27:47.599 --> 00:27:48.720
<v Speaker 2>How do we split the data?

543
00:27:48.759 --> 00:27:52.440
<v Speaker 1>You need a partitioning key. If you have integer primary keys,

544
00:27:52.519 --> 00:27:56.039
<v Speaker 1>a simple modul operation like user in three bitwise and

545
00:27:56.119 --> 00:27:59.039
<v Speaker 1>d with three equivalent to modulo four for positive numbers

546
00:27:59.319 --> 00:28:02.880
<v Speaker 1>can distribute users across four partitions. If your key is

547
00:28:02.960 --> 00:28:06.839
<v Speaker 1>text like a username, you typically hash it first hashtext

548
00:28:06.960 --> 00:28:09.759
<v Speaker 1>username and then apply the modular to the resulting integer

549
00:28:09.799 --> 00:28:12.839
<v Speaker 1>hash for even distribution. And to manage the routing from

550
00:28:12.880 --> 00:28:16.279
<v Speaker 1>the proxy to these partitions, postgrescool has a specialized tool,

551
00:28:16.559 --> 00:28:17.319
<v Speaker 1>peel proxy.

552
00:28:17.640 --> 00:28:21.000
<v Speaker 2>Another procedural language YEP, but this one isn't for general logic.

553
00:28:21.119 --> 00:28:24.000
<v Speaker 1>It's specifically designed for database charding. You install like any

554
00:28:24.000 --> 00:28:26.279
<v Speaker 1>other language, create extension pal proxy.

555
00:28:26.400 --> 00:28:28.160
<v Speaker 2>How does it syntax work? How do you tell it

556
00:28:28.279 --> 00:28:29.440
<v Speaker 2>where to send in the queries?

557
00:28:29.640 --> 00:28:33.400
<v Speaker 1>It's quite declarative. Inside a peel proxy function, you use

558
00:28:33.480 --> 00:28:37.519
<v Speaker 1>connect to specify the connection string for a remote database partition.

559
00:28:38.079 --> 00:28:41.880
<v Speaker 1>You can group these connections into named clusters. Then the

560
00:28:41.920 --> 00:28:44.640
<v Speaker 1>core is the r on clause you tell it which

561
00:28:44.680 --> 00:28:47.160
<v Speaker 1>partitions to run the remote function on. This could be

562
00:28:47.160 --> 00:28:50.400
<v Speaker 1>based on a routing function partition, part funk user, or

563
00:28:50.440 --> 00:28:53.440
<v Speaker 1>you can specify any run on any single partition, maybe

564
00:28:53.440 --> 00:28:56.279
<v Speaker 1>for reads that could be anywhere or all, run the

565
00:28:56.319 --> 00:28:59.839
<v Speaker 1>function in parallel on all partitions. Perhaps for broadcasting a command.

566
00:29:00.200 --> 00:29:03.279
<v Speaker 1>You also specify the remote function call using select or target.

567
00:29:03.599 --> 00:29:06.880
<v Speaker 2>Okay, it seems logical. You mentioned parallel execution with run

568
00:29:06.920 --> 00:29:07.279
<v Speaker 2>on all.

569
00:29:07.599 --> 00:29:10.559
<v Speaker 1>Yeah. And there's also the spli statement, which is really

570
00:29:10.599 --> 00:29:15.400
<v Speaker 1>powerful for distributing bulk operations. Imagine a function create users

571
00:29:15.400 --> 00:29:18.720
<v Speaker 1>that takes a raise of usernames, passwords, and emails. You

572
00:29:18.720 --> 00:29:21.319
<v Speaker 1>can write a pl proxy function that uses SPLT to

573
00:29:21.359 --> 00:29:24.319
<v Speaker 1>take these arrays, figure out which partition each new user

574
00:29:24.359 --> 00:29:27.240
<v Speaker 1>belongs to based on their username hash, and then efficiently

575
00:29:27.279 --> 00:29:29.920
<v Speaker 1>send batches of users to the correct partitions in parallel.

576
00:29:30.240 --> 00:29:30.759
<v Speaker 1>Very slick.

577
00:29:30.920 --> 00:29:33.119
<v Speaker 2>How does pl proxy know about the cluster members?

578
00:29:33.279 --> 00:29:37.119
<v Speaker 1>Two main ways. The older way uses special configuration functions

579
00:29:37.160 --> 00:29:40.759
<v Speaker 1>like plproxy dot get cluster partitions that you define to

580
00:29:40.799 --> 00:29:44.839
<v Speaker 1>return the connection strengths. The more modern and generally recommended

581
00:29:44.880 --> 00:29:47.880
<v Speaker 1>way is to use sql MED, the standard SQL management

582
00:29:47.920 --> 00:29:51.720
<v Speaker 1>of external data features. You create foreign data wrapper pull proxy,

583
00:29:52.119 --> 00:29:55.680
<v Speaker 1>then create server for each partition and create user mapping

584
00:29:55.720 --> 00:29:59.799
<v Speaker 1>to store credentials securely pl proxy, then queries these standard

585
00:29:59.799 --> 00:30:03.079
<v Speaker 1>caps objects define the cluster configuration right.

586
00:30:02.920 --> 00:30:04.680
<v Speaker 2>And migrating existing data.

587
00:30:04.599 --> 00:30:07.480
<v Speaker 1>Usually involve setting up the schemas on the partitions, then

588
00:30:07.599 --> 00:30:10.519
<v Speaker 1>carefully copying data row by row or chunk by chunk,

589
00:30:10.880 --> 00:30:13.799
<v Speaker 1>using the partitioning key to determine the destination. It could

590
00:30:13.839 --> 00:30:16.400
<v Speaker 1>be complex for live systems. It's careful planning.

591
00:30:16.480 --> 00:30:18.480
<v Speaker 2>Okay, so we've staled out. Now let's talk about sharing

592
00:30:18.480 --> 00:30:22.000
<v Speaker 2>all this cool stuff. We might have built custom functions, types, operators.

593
00:30:22.079 --> 00:30:25.960
<v Speaker 2>Why package them as a postcrischool extension. What's the benefit

594
00:30:26.000 --> 00:30:27.519
<v Speaker 2>over just shipping seql scripts?

595
00:30:27.559 --> 00:30:30.079
<v Speaker 1>So extensions are a massive improvement over the bad old

596
00:30:30.160 --> 00:30:32.480
<v Speaker 1>days of just providing dishquare yell files like the old

597
00:30:32.480 --> 00:30:33.319
<v Speaker 1>contrip modules.

598
00:30:33.319 --> 00:30:34.160
<v Speaker 2>Why were those bad?

599
00:30:34.400 --> 00:30:38.480
<v Speaker 1>They had no concept of dependencies, no versioning, no clean installation,

600
00:30:38.599 --> 00:30:42.640
<v Speaker 1>and crucially no upgrade path. If you install version one

601
00:30:42.759 --> 00:30:44.440
<v Speaker 1>of a script and a version two came out that

602
00:30:44.519 --> 00:30:47.559
<v Speaker 1>changed a function signature or a table definition, you often

603
00:30:47.559 --> 00:30:50.519
<v Speaker 1>had to manually drop everything from version one, losing data,

604
00:30:50.599 --> 00:30:54.160
<v Speaker 1>and then load version two. Painful yikes. And extension solves

605
00:30:54.200 --> 00:30:59.000
<v Speaker 1>all this. It's a formal package containing all related sequl objects, functions, tables, views, types,

606
00:30:59.319 --> 00:31:02.240
<v Speaker 1>operators can figuration parameters. To install it with single command

607
00:31:02.680 --> 00:31:06.559
<v Speaker 1>create extension my extension. Postgrescool tracks the installed objects as

608
00:31:06.599 --> 00:31:10.920
<v Speaker 1>belonging to that extension and the killer feature upgrades. If

609
00:31:10.960 --> 00:31:13.359
<v Speaker 1>you release version two point oh you provide an extension

610
00:31:13.359 --> 00:31:15.920
<v Speaker 1>one point two point oh dot s well update script.

611
00:31:16.359 --> 00:31:19.839
<v Speaker 1>Users just run ulter extension my extension update and postcrescool

612
00:31:19.839 --> 00:31:23.839
<v Speaker 1>applies the necessary changes cleanly, preserving data. Extensions can also

613
00:31:23.839 --> 00:31:25.880
<v Speaker 1>declare dependencies on other extensions.

614
00:31:25.960 --> 00:31:29.119
<v Speaker 2>That sounds much much better. How do you build one again?

615
00:31:29.160 --> 00:31:32.079
<v Speaker 1>You typically use the PGXS build system with a simple makefile.

616
00:31:32.519 --> 00:31:35.279
<v Speaker 1>It handles compiling any c code and packaging the SQL

617
00:31:35.279 --> 00:31:38.519
<v Speaker 1>files correctly. Pseudo make install puts it where postgrescool can find.

618
00:31:38.400 --> 00:31:39.240
<v Speaker 2>It and sharing it.

619
00:31:39.440 --> 00:31:42.920
<v Speaker 1>You publish it to the Postgres School Extension Network PGXN

620
00:31:43.119 --> 00:31:46.680
<v Speaker 1>PGXN dot org. There are tools like pgx and utails

621
00:31:46.680 --> 00:31:49.680
<v Speaker 1>to help package your extension correctly. You need a metada

622
00:31:49.680 --> 00:31:54.079
<v Speaker 1>adjacent file describing your extension, its licensed dependencies, and crucially

623
00:31:54.119 --> 00:31:57.839
<v Speaker 1>it's version number. Using semantic versioning, this allows users to

624
00:31:57.920 --> 00:32:01.240
<v Speaker 1>easily search for, download and install all extensions using clients

625
00:32:01.279 --> 00:32:04.599
<v Speaker 1>like PGX and client. It really makes postgress will incredibly

626
00:32:04.640 --> 00:32:08.119
<v Speaker 1>extensible and inviting for the open source community. Compared to

627
00:32:08.160 --> 00:32:13.079
<v Speaker 1>other databases, this standardized, robust extension system is well pretty revolutionary.

628
00:32:13.359 --> 00:32:15.519
<v Speaker 1>It makes it easy for anyone to extend postgreds to

629
00:32:15.559 --> 00:32:18.559
<v Speaker 1>do almost anything they want. Hashtag taghtag outro eerro.

630
00:32:18.720 --> 00:32:21.400
<v Speaker 2>Wow, we've covered a ton today. We really have taken

631
00:32:21.440 --> 00:32:23.960
<v Speaker 2>a deep dive into POSTGRESSWIL server programming. It's clear it

632
00:32:23.960 --> 00:32:26.480
<v Speaker 2>transforms the database from just you know, storage into a

633
00:32:26.480 --> 00:32:30.519
<v Speaker 2>really dynamic application development framework. From the basics of peelpgseql,

634
00:32:30.519 --> 00:32:32.960
<v Speaker 2>through the power and risks of Peel, Python and see

635
00:32:33.119 --> 00:32:36.240
<v Speaker 2>and even scaling out horizontally with Peel proxy. The common

636
00:32:36.279 --> 00:32:38.759
<v Speaker 2>thread is pushing logic closer to the data, and that

637
00:32:38.839 --> 00:32:41.079
<v Speaker 2>leads to apps that are faster, definitely more secure, and

638
00:32:41.160 --> 00:32:42.039
<v Speaker 2>easier to maintain.

639
00:32:42.359 --> 00:32:44.559
<v Speaker 1>Absolutely, and we just sort of zoom out for a

640
00:32:44.599 --> 00:32:48.200
<v Speaker 1>final thought. In a world that's increasingly driven by data,

641
00:32:48.240 --> 00:32:51.559
<v Speaker 1>really understanding and using these service side capabilities isn't just

642
00:32:51.599 --> 00:32:54.359
<v Speaker 1>another technical skill. It's more like paradigm shift, you know,

643
00:32:54.839 --> 00:32:57.759
<v Speaker 1>a different way of thinking about where your critical logic

644
00:32:57.799 --> 00:33:01.319
<v Speaker 1>should live, which maybe raises a question for you listening

645
00:33:01.400 --> 00:33:05.039
<v Speaker 1>right now. What untapped potential, what hidden power are you

646
00:33:05.119 --> 00:33:08.279
<v Speaker 1>overlooking in your current database solutions by treating them as

647
00:33:08.359 --> 00:33:09.720
<v Speaker 1>just simple data buckets
