-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1493 lines (1114 loc) · 239 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 3.9.0">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta property="og:type" content="website">
<meta property="og:title" content="Win-Man's Blog">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Win-Man's Blog">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Win-Man's Blog">
<link rel="canonical" href="http://yoursite.com/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'default'
};
</script>
<title>Win-Man's Blog</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="Toggle navigation bar">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">Win-Man's Blog</h1>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-home fa-fw"></i>Home</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>About</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>Tags</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>Categories</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>Archives</a>
</li>
</ul>
</nav>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2022/02/14/061-tidb-binlog-mysql/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2022/02/14/061-tidb-binlog-mysql/" class="post-title-link" itemprop="url">使用 TiDB Binlog 同步数据到 MySQL 步骤</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2022-02-14 19:39:46 / Modified: 15:12:10" itemprop="dateCreated datePublished" datetime="2022-02-14T19:39:46+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/TiDB/" itemprop="url" rel="index"><span itemprop="name">TiDB</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="使用-TiDB-Binlog-同步数据到-MySQL-步骤"><a href="#使用-TiDB-Binlog-同步数据到-MySQL-步骤" class="headerlink" title="使用 TiDB Binlog 同步数据到 MySQL 步骤"></a>使用 TiDB Binlog 同步数据到 MySQL 步骤</h1><p>本文档适用于 TiDB 集群已经搭建完成并且已有部分数据在集群中,需要搭建 TiDB Binlog 将 TiDB 中数据同步到 MySQL 的情况 。</p>
<p><strong>说明:</strong></p>
<ul>
<li>$ 符号表示命令需要在服务器命令行中执行</li>
<li>${xxx} 表示需要替换成实际对应的内容</li>
</ul>
<h2 id="步骤一、扩容-pump-节点"><a href="#步骤一、扩容-pump-节点" class="headerlink" title="步骤一、扩容 pump 节点"></a>步骤一、扩容 pump 节点</h2><ol>
<li>准备扩容 pump 节点的拓扑文件</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> cat scale-pump.yaml</span><br><span class="line">pump_servers:</span><br><span class="line"> host: 10.55.12.31</span><br><span class="line"> host: 10.55.12.32</span><br><span class="line"> host: 10.55.12.33</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>执行扩容命令</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> tiup cluster scale-out ${cluster_name} ./scale-pump.yaml -uroot -p</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>修改 TiDB 集群,开启 Binlog </li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> tiup cluster edit-config ${cluster_name}</span><br><span class="line">在 server_config 部分添加对应的配置</span><br><span class="line">server_configs:</span><br><span class="line"> tidb:</span><br><span class="line"> binlog.enable: true</span><br><span class="line"> binlog.ignore-error: true</span><br></pre></td></tr></table></figure>
<ol start="4">
<li>滚动重启 tidb-server 节点开始 binlog</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> tiup cluster reload ${cluster_name} -R tidb</span><br></pre></td></tr></table></figure>
<ol start="5">
<li>查看集群状态</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> tiup cluster display ${cluster_name}</span><br></pre></td></tr></table></figure>
<h2 id="步骤二、全量数据迁移"><a href="#步骤二、全量数据迁移" class="headerlink" title="步骤二、全量数据迁移"></a>步骤二、全量数据迁移</h2><ol>
<li>使用 mydumper 全量初始化导出</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> wget http://download.pingcap.org/tidb-enterprise-tools-latest-linux-amd64.tar.gz</span><br><span class="line"><span class="meta">$</span> tar -xzf tidb-enterprise-tools-latest-linux-amd64.tar.gz && cd tidb-enterprise-tools-latest-linux-amd64</span><br><span class="line"><span class="meta">$</span> ./bin/mydumper -h ${source ip} -P 4000 -u root -t 16 -F 64 --skip-tz-utc -o ./tmp/all_backup</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>获取 initial_commit_ts</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> cat /tmp/all_backup/metadata</span><br><span class="line">Started dump at: 2019-12-13 15:16:44</span><br><span class="line">SHOW MASTER STATUS:</span><br><span class="line"> Log: tidb-binlog</span><br><span class="line"> Pos: 413196983854432259</span><br><span class="line"> GTID:</span><br><span class="line">Finished dump at: 2019-12-13 15:16:48</span><br></pre></td></tr></table></figure>
<p>Pos 413196983854432259 即为备份的 tso 时刻点值,后续 Binlog 增量同步时需要从这个时间点开始同步</p>
<ol start="3">
<li>备份数据复制到 MySQL 节点并使用 loader 将全备数据导入到目标库中</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> ./bin/loader -h ${target ip} -u root -P 3306 -t 32 -d /tmp/all_backup</span><br></pre></td></tr></table></figure>
<h2 id="步骤三、扩容-drainer-节点"><a href="#步骤三、扩容-drainer-节点" class="headerlink" title="步骤三、扩容 drainer 节点"></a>步骤三、扩容 drainer 节点</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">1. 准备扩容 drainer 节点的拓扑文件</span><br><span class="line"><span class="meta">$</span> cat scale-drainer.yaml</span><br><span class="line">drainer_servers:</span><br><span class="line"> - host: 10.0.1.12</span><br><span class="line"> # commit_ts 填写 mydumper 备份文件中 metadata 记录的 tso 号信息</span><br><span class="line"> commit_ts: 413196983854432259</span><br><span class="line"> config:</span><br><span class="line"> syncer.db-type: "mysql"</span><br><span class="line"> syncer.to.host: "10.0.1.12"</span><br><span class="line"> syncer.to.user: "root"</span><br><span class="line"> syncer.to.password: ""</span><br><span class="line"> syncer.to.port: 3306</span><br><span class="line"> syncer.relay.log-dir: "/dir/to/save/log"</span><br><span class="line"> syncer.relay.max-file-size: 10485760</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>通过 tiup 执行扩容命令</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> tiup cluster scale-out ${cluster_name} ./scale-drainer.yaml -uroot -p</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>查看集群节点状态</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> tiup cluster display ${cluster_name}</span><br></pre></td></tr></table></figure>
<h2 id="步骤四、检查增量数据同步情况"><a href="#步骤四、检查增量数据同步情况" class="headerlink" title="步骤四、检查增量数据同步情况"></a>步骤四、检查增量数据同步情况</h2><p>在上游 TiDB 执行一些变更操作,在下游 MySQL 中查询验证数据是否正常同步。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2022/02/08/060-github-action/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2022/02/08/060-github-action/" class="post-title-link" itemprop="url">使用 Github Actions 替换 Travis 自动发布 Hexo 博客</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2022-02-08 19:39:46" itemprop="dateCreated datePublished" datetime="2022-02-08T19:39:46+00:00">2022-02-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/杂/" itemprop="url" rel="index"><span itemprop="name">杂</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="使用-Github-Action-替换-Travis-自动发布-Hexo-博客"><a href="#使用-Github-Action-替换-Travis-自动发布-Hexo-博客" class="headerlink" title="使用 Github Action 替换 Travis 自动发布 Hexo 博客"></a>使用 Github Action 替换 Travis 自动发布 Hexo 博客</h1><p>之前通过 Hexo + Github Pages 的模式搭建了这个博客,并且通过 Travis 自动集成发布我更新的博客文章。但是从 2021 年 12 月份开始 Travis CI 不再免费为开源项目提供持续集成的托管服务了。也是因为我好久没写博客了,导致这个问题直到最近才被发现。寻找替代方案,找了一圈发现 Github 自己提供了 Github Actions 的持续集成服务。那就折腾折腾替换成 Github Actions 来发布博客。</p>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>我的博客项目地址是:<a href="https://github.com/Win-Man/win-man.github.io" target="_blank" rel="noopener">https://github.com/Win-Man/win-man.github.io</a> 。repo 中分了两个分支,一个 master 分支和一个 dev 分支。dev 分支负责存放原始的 hexo 文件,master 分支负责存放 hexo generate 生成的 html 文件,然后通过 <a href="https://win-man.github.io/" target="_blank" rel="noopener">https://win-man.github.io/</a> 这个网址访问到的就是 master 分支上的 html 文件了。</p>
<p>之前 Travis CI 持续集成服务中是会监测 dev 分支的更新,每当我有将提交 push 到 dev 分支上的时候,会触发 Travis CI 的持续集成服务,基于 dev 分支的内容自动更新 master 分支。所以想用 Github Actions 替换 Travis CI 也是类似的思路:</p>
<ol>
<li>监测到 dev 分支有更新</li>
<li>基于 dev 分支内容执行 hexo deploy </li>
</ol>
<h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><h3 id="生成秘钥"><a href="#生成秘钥" class="headerlink" title="生成秘钥"></a>生成秘钥</h3><p>这一步是为了让 Github Actions 有权限将更新 push 到 repo 。</p>
<ol>
<li>创建一个新的 SSH 秘钥</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -f hexo-deploy-key</span><br></pre></td></tr></table></figure>
<p>会生成 hexo-deploy-key 和 hexo-deploy-key.pub 两个文件</p>
<ol start="2">
<li>在 win-man.github.io 这个项目添加 Secrets</li>
</ol>
<p>添加 hexo-deploy-key 的内容作为 Secrets<br><img src="https://raw.githubusercontent.com/Win-Man/pic-storage/master/img/20220208200006.png" alt="add secrets"></p>
<ol start="3">
<li>在 win-man.github.io 这个项目添加 Deploy key</li>
</ol>
<p>添加 hexo-deploy-key.pub 的内容作为 Deploy key<br><img src="https://raw.githubusercontent.com/Win-Man/pic-storage/master/img/20220208200219.png" alt="add deploy key"></p>
<h3 id="配置-Github-Action"><a href="#配置-Github-Action" class="headerlink" title="配置 Github Action"></a>配置 Github Action</h3><ol>
<li>切换到 dev 分支下,然后创建 workflows 目录, 并配置 main.yaml 文件</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p .github/workflows/</span><br><span class="line">touch .github/workflows/main.yaml</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>填写配置文件</li>
</ol>
<p>配置文件内容如下:</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">hexo-deploy</span></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line"> <span class="attr">push:</span></span><br><span class="line"> <span class="attr">branches:</span> <span class="string">[</span> <span class="string">dev</span> <span class="string">]</span></span><br><span class="line"> <span class="attr">pull_request:</span></span><br><span class="line"> <span class="attr">branches:</span> <span class="string">[</span> <span class="string">dev</span> <span class="string">]</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">workflow_dispatch:</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># A workflow run is made up of one or more jobs that can run sequentially or in parallel</span></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line"> <span class="comment"># This workflow contains a single job called "build"</span></span><br><span class="line"> <span class="attr">build:</span></span><br><span class="line"> <span class="comment"># The type of runner that the job will run on</span></span><br><span class="line"> <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># Steps represent a sequence of tasks that will be executed as part of the job</span></span><br><span class="line"> <span class="attr">steps:</span></span><br><span class="line"> <span class="comment"># Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span> <span class="string">dev</span> <span class="string">branch</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">actions/checkout@v2</span></span><br><span class="line"> <span class="attr">with:</span></span><br><span class="line"> <span class="attr">ref:</span> <span class="string">dev</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Config</span> <span class="string">node.js</span></span><br><span class="line"> <span class="attr">uses:</span> <span class="string">actions/setup-node@v2</span></span><br><span class="line"> <span class="attr">with:</span></span><br><span class="line"> <span class="attr">node-version:</span> <span class="string">'13'</span></span><br><span class="line"> <span class="attr">cache:</span> <span class="string">'npm'</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">hexo</span></span><br><span class="line"> <span class="attr">env:</span></span><br><span class="line"> <span class="attr">ACTION_DEPLOY_KEY:</span> <span class="string">${{</span> <span class="string">secrets.HEXO_DEPLOY_SECRET</span> <span class="string">}}</span></span><br><span class="line"> <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"> <span class="string">mkdir</span> <span class="string">-p</span> <span class="string">~/.ssh/</span></span><br><span class="line"> <span class="string">echo</span> <span class="string">"$ACTION_DEPLOY_KEY"</span> <span class="string">></span> <span class="string">~/.ssh/id_rsa</span></span><br><span class="line"> <span class="string">chmod</span> <span class="number">600</span> <span class="string">~/.ssh/id_rsa</span></span><br><span class="line"> <span class="string">ssh-keyscan</span> <span class="string">github.com</span> <span class="string">>></span> <span class="string">~/.ssh/known_hosts</span></span><br><span class="line"> <span class="string">git</span> <span class="string">config</span> <span class="string">--global</span> <span class="string">user.name</span> <span class="string">"Win-Man"</span></span><br><span class="line"> <span class="string">git</span> <span class="string">config</span> <span class="string">--global</span> <span class="string">user.email</span> <span class="number">825895587</span><span class="string">@qq.com</span></span><br><span class="line"> <span class="string">npm</span> <span class="string">install</span> <span class="string">hexo-cli</span> <span class="string">-g</span></span><br><span class="line"> <span class="string">npm</span> <span class="string">install</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Generate</span> <span class="string">and</span> <span class="string">deploy</span></span><br><span class="line"> <span class="attr">run:</span> <span class="string">|</span></span><br><span class="line"> <span class="string">hexo</span> <span class="string">clean</span></span><br><span class="line"> <span class="string">hexo</span> <span class="string">deploy</span></span><br></pre></td></tr></table></figure>
<ol start="3">
<li><p>提交 Github Actions 配置</p>
</li>
<li><p>查看 Github Actions 并测试</p>
</li>
</ol>
<p>每当 dev 分支有代码更新的时候,就会触发 Github Actions 的工作流自动发布博客了。</p>
<p><img src="https://raw.githubusercontent.com/Win-Man/pic-storage/master/img/20220208200903.png" alt="actions"></p>
<h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><h3 id="node-version-错误导致生成-html-为空"><a href="#node-version-错误导致生成-html-为空" class="headerlink" title="node version 错误导致生成 html 为空"></a>node version 错误导致生成 html 为空</h3><h4 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h4><p>Github Actions job 正常触发也正常运行了,没有报错,但是访问 <a href="https://win-man.github.io/" target="_blank" rel="noopener">https://win-man.github.io/</a> 这个网址却是一篇空白。检查 master 分支上的 html 文件发现每篇文章的 html 文件是生成了,但是里面的内容是空的。</p>
<h4 id="问题原因"><a href="#问题原因" class="headerlink" title="问题原因"></a>问题原因</h4><p>nodejs 版本太高,hexo 不支持。</p>
<h4 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h4><p>将 main.yaml 中 node-version 的配置设置为 13 版本。</p>
<h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><ul>
<li><a href="https://docs.github.com/cn/actions" target="_blank" rel="noopener">Github Action docs</a></li>
<li><a href="https://makefile.so/2021/11/28/use-github-actions-to-deploy-hexo-blog/" target="_blank" rel="noopener">使用 Github Actions 部署 Hexo 博客</a></li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2022/01/29/059-2021-year-end-summary/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2022/01/29/059-2021-year-end-summary/" class="post-title-link" itemprop="url">2021 年终总结</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2022-01-29 19:39:46" itemprop="dateCreated datePublished" datetime="2022-01-29T19:39:46+00:00">2022-01-29</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/杂/" itemprop="url" rel="index"><span itemprop="name">杂</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="2021-年终总结"><a href="#2021-年终总结" class="headerlink" title="2021 年终总结"></a>2021 年终总结</h1><p>还有两天就要结束这一年,迈入新一年了,按照老习惯,趁着放假自己空闲一点,开始回顾自己这一年。想写年终总结时总是思绪万千,但是真的到了输出文字的时候总是写不出什么,可能还是表达能力太差了。</p>
<h2 id="工作"><a href="#工作" class="headerlink" title="工作"></a>工作</h2><p>去年一年,还是在 PingCAP 工作,有的变化只是我从社区部门转岗到商业部门做售前。这个决定经过了很久的思想斗争,但不能算是一个考虑周全的决定。做出决定的那一瞬间,其实也还是有很多凭感觉的地方在,考虑了很多也只是凭借感觉做了选择。之前在做 DBA 的时候有考虑过自己的职业规划,要不就是一直做技术,到可以做成技术专家的程度;另一个选择就是在技术有一定积累之后做售前或者架构师,将技术价值更大化。说实话,按照我心里的预期,我目前的工作经验以及积累还不足以让我觉得我可以做一个售前,但是当有机会时,又不想那么轻易的放弃,毕竟没有那么多等一切都准备好了才有的机会。</p>
<p>从 5 月份开始慢慢接触售前的工作,幸好之前已经对于 TiDB 有了解,所以在技术方面的压力没那么大,可以专心面对跟客户交流、跟人打交道方面的内容。售前的工作给了我更多与人接触的机会,之前我并不是那么喜欢跟人打交道,但是现在不得不去跟人打交道,目前已经习惯了。想想自己刚毕业的时候,连接到别人打的电话都会抵触,不愿意接,到现在主动跟人打电话也可以。感觉变化还是挺大的。</p>
<h2 id="读书"><a href="#读书" class="headerlink" title="读书"></a>读书</h2><p>从今年开始改变了一下统计我一年读书的方式,之前因为年终总结是按照农历年为跨度写的,所以之前读书也是按照一样的时间维度去统计,但是今年感觉这种方式统计可能会有不严谨的地方,所以就按照公历年份去看看自己读了多少书,这样也方便统计。</p>
<p>去年也是一样买了很多书,但是读完的不多。看了一下去年的统计,总共读完了 36 本书,大部分书还都是在微信读书上读完的,实体书没有读多少。读的技术方面的书越来越少了,啃不动了。也没有专门读某一领域方面的书。</p>
<p>给自己记录一本之后有机会还可以再读一遍的书 《鳗鱼的旅行》。</p>
<h2 id="生活"><a href="#生活" class="headerlink" title="生活"></a>生活</h2><p>21 年有一个字很火,那就是 ”卷“。但是我不是一个爱卷的人,所以我选择反卷。21 年选择了更多的时间去生活,或者只是说选择了更少的时间去工作。21 年完成了几件事,首先是去把摩托车驾照考了,也买了自己人生中的第一辆摩托车,在提车回来的第一天就骑着车开了 200 公里(主要的原因是因为杭州禁摩,只能选择绕路将车开回来)。考驾照时是骑的三轮摩托车,从来没有骑过二轮的。胆子很大的,提车那天自己一个人就去了,提车之前在网上看了很久的骑车教程。结果还好,找了一段路练习了一下换挡,然后就 200 公里一路骑回来了。还曾一度骑到了 90+ 码,现在想来还是有点后怕,第一天就这么猛,真的不要命。而且忘记了新车应该要磨合一下。</p>
<p>21 年还完成了连续骑自行车 100 公里的目标,花了一天时间从杭州出发,绕青山湖一圈再骑回杭州,刚好一圈。百公里消耗炒面一碗。也是因为骑自行车在 21 年摔了一跤,把右手手腕摔坏了,本来以为没有伤到骨头没有什么大事,没想到很长时间都不能正常运动,现在做俯卧撑手腕都感觉有点不一样(给自己减肥失败找个理由)。</p>
<p>21 年想学滑雪,没有完成。</p>
<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>最近学到一个新的词叫深度年(Year of Deepening)。主要意思是这一年让自己往已经了解的领域深挖,而不是不断扩展新的领域或者新的爱好,让自己走的更深,而不是更广。觉得 22 年可以往这个方向靠一靠。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2021/05/09/058-simple-database-4/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/05/09/058-simple-database-4/" class="post-title-link" itemprop="url">Let's Build a Simple Database - Our First Tests(and Bugs)</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-05-09 21:12:13" itemprop="dateCreated datePublished" datetime="2021-05-09T21:12:13+00:00">2021-05-09</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/翻译/" itemprop="url" rel="index"><span itemprop="name">翻译</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>原文地址:<a href="https://cstack.github.io/db_tutorial/parts/part4.html" target="_blank" rel="noopener">https://cstack.github.io/db_tutorial/parts/part4.html</a><br>原文作者: <a href="https://github.com/cstack" target="_blank" rel="noopener">cstack</a><br>译者:<a href="https://github.com/Win-Man" target="_blank" rel="noopener">Win-Man</a></p>
</blockquote>
<p>目前为止,我们的数据库具备了插入记录和输出所有记录行的能力。让我们花点时间测试一下目前数据库的能力。</p>
<p>我准备使用 <a href="http://rspec.info/" target="_blank" rel="noopener">rspec</a> 写测试,因为我对这个比较熟悉,并且语法比较易读。</p>
<p>我会定义一个简短的助手去发送一系列命令给我们的数据库程序,然后对返回的输出结果做断言。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">describe 'database' do</span><br><span class="line"> def run_script(commands)</span><br><span class="line"> raw_output = nil</span><br><span class="line"> IO.popen("./db", "r+") do |pipe|</span><br><span class="line"> commands.each do |command|</span><br><span class="line"> pipe.puts command</span><br><span class="line"> end</span><br><span class="line"></span><br><span class="line"> pipe.close_write</span><br><span class="line"></span><br><span class="line"> # Read entire output</span><br><span class="line"> raw_output = pipe.gets(nil)</span><br><span class="line"> end</span><br><span class="line"> raw_output.split("\n")</span><br><span class="line"> end</span><br><span class="line"></span><br><span class="line"> it 'inserts and retrieves a row' do</span><br><span class="line"> result = run_script([</span><br><span class="line"> "insert 1 user1 [email protected]",</span><br><span class="line"> "select",</span><br><span class="line"> ".exit",</span><br><span class="line"> ])</span><br><span class="line"> expect(result).to match_array([</span><br><span class="line"> "db > Executed.",</span><br><span class="line"> "db > (1, user1, [email protected])",</span><br><span class="line"> "Executed.",</span><br><span class="line"> "db > ",</span><br><span class="line"> ])</span><br><span class="line"> end</span><br><span class="line">end</span><br></pre></td></tr></table></figure>
<p>这个简单的测试确保了我们得到了和我们输入一样的输出结果,并且这个测试确实通过了:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">bundle exec rspec</span><br><span class="line">.</span><br><span class="line"></span><br><span class="line">Finished in 0.00871 seconds (files took 0.09506 seconds to load)</span><br><span class="line">1 example, 0 failures</span><br></pre></td></tr></table></figure>
<p>然后就可以测试往数据库中插入大量的记录了:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">it 'prints error message when table is full' do</span><br><span class="line"> script = (1..1401).map do |i|</span><br><span class="line"> "insert #{i} user#{i} person#{i}@example.com"</span><br><span class="line"> end</span><br><span class="line"> script << ".exit"</span><br><span class="line"> result = run_script(script)</span><br><span class="line"> expect(result[-2]).to eq('db > Error: Table full.')</span><br><span class="line">end</span><br></pre></td></tr></table></figure>
<p>再重新运行测试。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">bundle exec rspec</span><br><span class="line">..</span><br><span class="line"></span><br><span class="line">Finished in 0.01553 seconds (files took 0.08156 seconds to load)</span><br><span class="line">2 examples, 0 failures</span><br></pre></td></tr></table></figure>
<p>测试通过了,我们的数据库可以存储 1400 行数据目前,因为我们设置了最大的数据页大小是 100,并且每个数据页上可以存储 14 条记录。</p>
<p>重新翻看我们写的代码,我意识到我们可能没有正确的处理如何存储 text 字段。可以很容易的验证这个 case:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">it 'allows inserting strings that are the maximum length' do</span><br><span class="line"> long_username = "a"*32</span><br><span class="line"> long_email = "a"*255</span><br><span class="line"> script = [</span><br><span class="line"> "insert 1 #{long_username} #{long_email}",</span><br><span class="line"> "select",</span><br><span class="line"> ".exit",</span><br><span class="line"> ]</span><br><span class="line"> result = run_script(script)</span><br><span class="line"> expect(result).to match_array([</span><br><span class="line"> "db > Executed.",</span><br><span class="line"> "db > (1, #{long_username}, #{long_email})",</span><br><span class="line"> "Executed.",</span><br><span class="line"> "db > ",</span><br><span class="line"> ])</span><br><span class="line">end</span><br></pre></td></tr></table></figure>
<p>然后测试就跑不通了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Failures:</span><br><span class="line"></span><br><span class="line"> 1) database allows inserting strings that are the maximum length</span><br><span class="line"> Failure/Error: raw_output.split("\n")</span><br><span class="line"></span><br><span class="line"> ArgumentError:</span><br><span class="line"> invalid byte sequence in UTF-8</span><br><span class="line"> # ./spec/main_spec.rb:14:in `split'</span><br><span class="line"> # ./spec/main_spec.rb:14:in `run_script'</span><br><span class="line"> # ./spec/main_spec.rb:48:in `block (2 levels) in <top (required)>'</span><br></pre></td></tr></table></figure>
<p>如果我们手动测试这个例子,我们可以在输出的记录中看到一些奇怪的字符(我对字符串进行了缩写):</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">db > insert 1 aaaaa... aaaaa...</span><br><span class="line">Executed.</span><br><span class="line">db > select</span><br><span class="line">(1, aaaaa...aaa\�, aaaaa...aaa\�)</span><br><span class="line">Executed.</span><br><span class="line">db ></span><br></pre></td></tr></table></figure>
<p>这是怎么回事?我们再看一下我们对于 Row 的定义,我们给 username 字段分配了 32 字节的空间,email 字段分配了 255 字节的空间。但是 C 语言中 string 类型的字段应该是以 null 字符结尾的,我们并没有给这个结束符分配空间。所以这个问题的结局方案就是多分配一个字节的空间:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">const</span> <span class="keyword">uint32_t</span> COLUMN_EMAIL_SIZE = <span class="number">255</span>;</span><br><span class="line"> <span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> <span class="keyword">uint32_t</span> id;</span><br><span class="line">- <span class="keyword">char</span> username[COLUMN_USERNAME_SIZE];</span><br><span class="line">- <span class="keyword">char</span> email[COLUMN_EMAIL_SIZE];</span><br><span class="line">+ <span class="keyword">char</span> username[COLUMN_USERNAME_SIZE + <span class="number">1</span>];</span><br><span class="line">+ <span class="keyword">char</span> email[COLUMN_EMAIL_SIZE + <span class="number">1</span>];</span><br><span class="line"> } Row;</span><br></pre></td></tr></table></figure>
<p>再次运行测试,可以看到测试通过了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"> bundle exec rspec</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">Finished in 0.0188 seconds (files took 0.08516 seconds to load)</span><br><span class="line">3 examples, 0 failures</span><br></pre></td></tr></table></figure>
<p>我们不应该允许插入的 username 和 email 字段的值超过我们定义的 column size ,可以增加一个测试:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">it 'prints error message if strings are too long' do</span><br><span class="line"> long_username = "a"*33</span><br><span class="line"> long_email = "a"*256</span><br><span class="line"> script = [</span><br><span class="line"> "insert 1 #{long_username} #{long_email}",</span><br><span class="line"> "select",</span><br><span class="line"> ".exit",</span><br><span class="line"> ]</span><br><span class="line"> result = run_script(script)</span><br><span class="line"> expect(result).to match_array([</span><br><span class="line"> "db > String is too long.",</span><br><span class="line"> "db > Executed.",</span><br><span class="line"> "db > ",</span><br><span class="line"> ])</span><br><span class="line">end</span><br></pre></td></tr></table></figure>
<p>为了实现这个限制,我们需要更新一下我们的 parser 解析器。目前使用的是 scanf():</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line"> statement->type = STATEMENT_INSERT;</span><br><span class="line"> <span class="keyword">int</span> args_assigned = <span class="built_in">sscanf</span>(</span><br><span class="line"> input_buffer->buffer, <span class="string">"insert %d %s %s"</span>, &(statement->row_to_insert.id),</span><br><span class="line"> statement->row_to_insert.username, statement->row_to_insert.email);</span><br><span class="line"> <span class="keyword">if</span> (args_assigned < <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">return</span> PREPARE_SYNTAX_ERROR;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>但是 scanf() 有一些缺点。如果读取的字符串大小超过了分配的空间,会导致缓存溢出并且数据会被写入到未知的空间。我们需要在拷贝数据到 Row 结构体之前先检查一下字符串的长度。为了实现这个,我们需要将输入通过空格切割一下。</p>
<p>我准备试用 strtok() 来实现这个。这个比较容易理解:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">+<span class="function">PrepareResult <span class="title">prepare_insert</span><span class="params">(InputBuffer* input_buffer, Statement* statement)</span> </span>{</span><br><span class="line">+ statement->type = STATEMENT_INSERT;</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">char</span>* keyword = strtok(input_buffer->buffer, <span class="string">" "</span>);</span><br><span class="line">+ <span class="keyword">char</span>* id_string = strtok(<span class="literal">NULL</span>, <span class="string">" "</span>);</span><br><span class="line">+ <span class="keyword">char</span>* username = strtok(<span class="literal">NULL</span>, <span class="string">" "</span>);</span><br><span class="line">+ <span class="keyword">char</span>* email = strtok(<span class="literal">NULL</span>, <span class="string">" "</span>);</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">if</span> (id_string == <span class="literal">NULL</span> || username == <span class="literal">NULL</span> || email == <span class="literal">NULL</span>) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SYNTAX_ERROR;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">int</span> id = atoi(id_string);</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strlen</span>(username) > COLUMN_USERNAME_SIZE) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_STRING_TOO_LONG;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strlen</span>(email) > COLUMN_EMAIL_SIZE) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_STRING_TOO_LONG;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ statement->row_to_insert.id = id;</span><br><span class="line">+ <span class="built_in">strcpy</span>(statement->row_to_insert.username, username);</span><br><span class="line">+ <span class="built_in">strcpy</span>(statement->row_to_insert.email, email);</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line"> <span class="function">PrepareResult <span class="title">prepare_statement</span><span class="params">(InputBuffer* input_buffer,</span></span></span><br><span class="line"><span class="function"><span class="params"> Statement* statement)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line">+ <span class="keyword">return</span> prepare_insert(input_buffer, statement);</span><br><span class="line">- statement->type = STATEMENT_INSERT;</span><br><span class="line">- <span class="keyword">int</span> args_assigned = <span class="built_in">sscanf</span>(</span><br><span class="line">- input_buffer->buffer, <span class="string">"insert %d %s %s"</span>, &(statement->row_to_insert.id),</span><br><span class="line">- statement->row_to_insert.username, statement->row_to_insert.email);</span><br><span class="line">- <span class="keyword">if</span> (args_assigned < <span class="number">3</span>) {</span><br><span class="line">- <span class="keyword">return</span> PREPARE_SYNTAX_ERROR;</span><br><span class="line">- }</span><br><span class="line">- <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>在输入缓冲区上连续调用strtok,每当缓冲区到达分隔符(在本例中是空格)时,就插入一个空字符,从而将它分解为子字符串。它返回一个指向子字符串开头的指针。</p>
<p>可以调用 strlen() 函数确认每个 text 字段值是不是超过了长度限制。</p>
<p>我们可以像处理别的错误一样处理这个错误:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">enum</span> PrepareResult_t {</span><br><span class="line"> PREPARE_SUCCESS,</span><br><span class="line">+ PREPARE_STRING_TOO_LONG,</span><br><span class="line"> PREPARE_SYNTAX_ERROR,</span><br><span class="line"> PREPARE_UNRECOGNIZED_STATEMENT</span><br><span class="line"> };</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_STRING_TOO_LONG):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"String is too long.\n"</span>);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_SYNTAX_ERROR):</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Syntax error. Could not parse statement.\n"</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure>
<p>再次运行测试,测试通过</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">bundle exec rspec</span><br><span class="line">....</span><br><span class="line"></span><br><span class="line">Finished in 0.02284 seconds (files took 0.116 seconds to load)</span><br><span class="line">4 examples, 0 failures</span><br></pre></td></tr></table></figure>
<p>既然已经增加了一种错误情况,我们不妨再处理一个错误情况:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">it 'prints an error message if id is negative' do</span><br><span class="line"> script = [</span><br><span class="line"> "insert -1 cstack [email protected]",</span><br><span class="line"> "select",</span><br><span class="line"> ".exit",</span><br><span class="line"> ]</span><br><span class="line"> result = run_script(script)</span><br><span class="line"> expect(result).to match_array([</span><br><span class="line"> "db > ID must be positive.",</span><br><span class="line"> "db > Executed.",</span><br><span class="line"> "db > ",</span><br><span class="line"> ])</span><br><span class="line">end</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">enum</span> PrepareResult_t {</span><br><span class="line"> PREPARE_SUCCESS,</span><br><span class="line">+ PREPARE_NEGATIVE_ID,</span><br><span class="line"> PREPARE_STRING_TOO_LONG,</span><br><span class="line"> PREPARE_SYNTAX_ERROR,</span><br><span class="line"> PREPARE_UNRECOGNIZED_STATEMENT</span><br><span class="line">@@ <span class="number">-148</span>,<span class="number">9</span> +<span class="number">147</span>,<span class="number">6</span> @@ <span class="function">PrepareResult <span class="title">prepare_insert</span><span class="params">(InputBuffer* input_buffer, Statement* statement)</span> </span>{</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> id = atoi(id_string);</span><br><span class="line">+ <span class="keyword">if</span> (id < <span class="number">0</span>) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_NEGATIVE_ID;</span><br><span class="line">+ }</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strlen</span>(username) > COLUMN_USERNAME_SIZE) {</span><br><span class="line"> <span class="keyword">return</span> PREPARE_STRING_TOO_LONG;</span><br><span class="line"> }</span><br><span class="line">@@ <span class="number">-230</span>,<span class="number">9</span> +<span class="number">226</span>,<span class="number">6</span> @@ <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_NEGATIVE_ID):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"ID must be positive.\n"</span>);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_STRING_TOO_LONG):</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"String is too long.\n"</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure>
<p>目前这些测试已经足够了,下一个章节会是一个重要的特性:持久化。我们需要将数据存储到文件中,并且中文件中读取出来。</p>
<p>那会很棒的。</p>
<p>在这给出这个部分中与之前代码不同的部分:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line">@@ <span class="number">-22</span>,<span class="number">6</span> +<span class="number">22</span>,<span class="number">8</span> @@</span><br><span class="line"></span><br><span class="line"> <span class="keyword">enum</span> PrepareResult_t {</span><br><span class="line"> PREPARE_SUCCESS,</span><br><span class="line">+ PREPARE_NEGATIVE_ID,</span><br><span class="line">+ PREPARE_STRING_TOO_LONG,</span><br><span class="line"> PREPARE_SYNTAX_ERROR,</span><br><span class="line"> PREPARE_UNRECOGNIZED_STATEMENT</span><br><span class="line"> };</span><br><span class="line">@@ <span class="number">-34</span>,<span class="number">8</span> +<span class="number">36</span>,<span class="number">8</span> @@</span><br><span class="line"> <span class="meta">#<span class="meta-keyword">define</span> COLUMN_EMAIL_SIZE 255</span></span><br><span class="line"> <span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> <span class="keyword">uint32_t</span> id;</span><br><span class="line">- <span class="keyword">char</span> username[COLUMN_USERNAME_SIZE];</span><br><span class="line">- <span class="keyword">char</span> email[COLUMN_EMAIL_SIZE];</span><br><span class="line">+ <span class="keyword">char</span> username[COLUMN_USERNAME_SIZE + <span class="number">1</span>];</span><br><span class="line">+ <span class="keyword">char</span> email[COLUMN_EMAIL_SIZE + <span class="number">1</span>];</span><br><span class="line"> } Row;</span><br><span class="line"></span><br><span class="line">@@ <span class="number">-150</span>,<span class="number">18</span> +<span class="number">152</span>,<span class="number">40</span> @@ <span class="function">MetaCommandResult <span class="title">do_meta_command</span><span class="params">(InputBuffer* input_buffer, Table *table)</span> </span>{</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">-<span class="function">PrepareResult <span class="title">prepare_statement</span><span class="params">(InputBuffer* input_buffer,</span></span></span><br><span class="line"><span class="function"><span class="params">- Statement* statement)</span> </span>{</span><br><span class="line">- <span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line">+<span class="function">PrepareResult <span class="title">prepare_insert</span><span class="params">(InputBuffer* input_buffer, Statement* statement)</span> </span>{</span><br><span class="line"> statement->type = STATEMENT_INSERT;</span><br><span class="line">- <span class="keyword">int</span> args_assigned = <span class="built_in">sscanf</span>(</span><br><span class="line">- input_buffer->buffer, <span class="string">"insert %d %s %s"</span>, &(statement->row_to_insert.id),</span><br><span class="line">- statement->row_to_insert.username, statement->row_to_insert.email</span><br><span class="line">- );</span><br><span class="line">- <span class="keyword">if</span> (args_assigned < <span class="number">3</span>) {</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">char</span>* keyword = strtok(input_buffer->buffer, <span class="string">" "</span>);</span><br><span class="line">+ <span class="keyword">char</span>* id_string = strtok(<span class="literal">NULL</span>, <span class="string">" "</span>);</span><br><span class="line">+ <span class="keyword">char</span>* username = strtok(<span class="literal">NULL</span>, <span class="string">" "</span>);</span><br><span class="line">+ <span class="keyword">char</span>* email = strtok(<span class="literal">NULL</span>, <span class="string">" "</span>);</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">if</span> (id_string == <span class="literal">NULL</span> || username == <span class="literal">NULL</span> || email == <span class="literal">NULL</span>) {</span><br><span class="line"> <span class="keyword">return</span> PREPARE_SYNTAX_ERROR;</span><br><span class="line"> }</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">int</span> id = atoi(id_string);</span><br><span class="line">+ <span class="keyword">if</span> (id < <span class="number">0</span>) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_NEGATIVE_ID;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strlen</span>(username) > COLUMN_USERNAME_SIZE) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_STRING_TOO_LONG;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strlen</span>(email) > COLUMN_EMAIL_SIZE) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_STRING_TOO_LONG;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ statement->row_to_insert.id = id;</span><br><span class="line">+ <span class="built_in">strcpy</span>(statement->row_to_insert.username, username);</span><br><span class="line">+ <span class="built_in">strcpy</span>(statement->row_to_insert.email, email);</span><br><span class="line">+</span><br><span class="line"> <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">+</span><br><span class="line">+}</span><br><span class="line">+<span class="function">PrepareResult <span class="title">prepare_statement</span><span class="params">(InputBuffer* input_buffer,</span></span></span><br><span class="line"><span class="function"><span class="params">+ Statement* statement)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line">+ <span class="keyword">return</span> prepare_insert(input_buffer, statement);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">"select"</span>) == <span class="number">0</span>) {</span><br><span class="line"> statement->type = STATEMENT_SELECT;</span><br><span class="line">@@ <span class="number">-223</span>,<span class="number">6</span> +<span class="number">247</span>,<span class="number">12</span> @@ <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_NEGATIVE_ID):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"ID must be positive.\n"</span>);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_STRING_TOO_LONG):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"String is too long.\n"</span>);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_SYNTAX_ERROR):</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Syntax error. Could not parse statement.\n"</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br></pre></td></tr></table></figure>
<p>以及我们增加的测试:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line">+describe 'database' do</span><br><span class="line">+ def run_script(commands)</span><br><span class="line">+ raw_output = nil</span><br><span class="line">+ IO.popen("./db", "r+") do |pipe|</span><br><span class="line">+ commands.each do |command|</span><br><span class="line">+ pipe.puts command</span><br><span class="line">+ end</span><br><span class="line">+</span><br><span class="line">+ pipe.close_write</span><br><span class="line">+</span><br><span class="line">+ # Read entire output</span><br><span class="line">+ raw_output = pipe.gets(nil)</span><br><span class="line">+ end</span><br><span class="line">+ raw_output.split("\n")</span><br><span class="line">+ end</span><br><span class="line">+</span><br><span class="line">+ it 'inserts and retrieves a row' do</span><br><span class="line">+ result = run_script([</span><br><span class="line">+ "insert 1 user1 [email protected]",</span><br><span class="line">+ "select",</span><br><span class="line">+ ".exit",</span><br><span class="line">+ ])</span><br><span class="line">+ expect(result).to match_array([</span><br><span class="line">+ "db > Executed.",</span><br><span class="line">+ "db > (1, user1, [email protected])",</span><br><span class="line">+ "Executed.",</span><br><span class="line">+ "db > ",</span><br><span class="line">+ ])</span><br><span class="line">+ end</span><br><span class="line">+</span><br><span class="line">+ it 'prints error message when table is full' do</span><br><span class="line">+ script = (1..1401).map do |i|</span><br><span class="line">+ "insert #{i} user#{i} person#{i}@example.com"</span><br><span class="line">+ end</span><br><span class="line">+ script << ".exit"</span><br><span class="line">+ result = run_script(script)</span><br><span class="line">+ expect(result[-2]).to eq('db > Error: Table full.')</span><br><span class="line">+ end</span><br><span class="line">+</span><br><span class="line">+ it 'allows inserting strings that are the maximum length' do</span><br><span class="line">+ long_username = "a"*32</span><br><span class="line">+ long_email = "a"*255</span><br><span class="line">+ script = [</span><br><span class="line">+ "insert 1 #{long_username} #{long_email}",</span><br><span class="line">+ "select",</span><br><span class="line">+ ".exit",</span><br><span class="line">+ ]</span><br><span class="line">+ result = run_script(script)</span><br><span class="line">+ expect(result).to match_array([</span><br><span class="line">+ "db > Executed.",</span><br><span class="line">+ "db > (1, #{long_username}, #{long_email})",</span><br><span class="line">+ "Executed.",</span><br><span class="line">+ "db > ",</span><br><span class="line">+ ])</span><br><span class="line">+ end</span><br><span class="line">+</span><br><span class="line">+ it 'prints error message if strings are too long' do</span><br><span class="line">+ long_username = "a"*33</span><br><span class="line">+ long_email = "a"*256</span><br><span class="line">+ script = [</span><br><span class="line">+ "insert 1 #{long_username} #{long_email}",</span><br><span class="line">+ "select",</span><br><span class="line">+ ".exit",</span><br><span class="line">+ ]</span><br><span class="line">+ result = run_script(script)</span><br><span class="line">+ expect(result).to match_array([</span><br><span class="line">+ "db > String is too long.",</span><br><span class="line">+ "db > Executed.",</span><br><span class="line">+ "db > ",</span><br><span class="line">+ ])</span><br><span class="line">+ end</span><br><span class="line">+</span><br><span class="line">+ it 'prints an error message if id is negative' do</span><br><span class="line">+ script = [</span><br><span class="line">+ "insert -1 cstack [email protected]",</span><br><span class="line">+ "select",</span><br><span class="line">+ ".exit",</span><br><span class="line">+ ]</span><br><span class="line">+ result = run_script(script)</span><br><span class="line">+ expect(result).to match_array([</span><br><span class="line">+ "db > ID must be positive.",</span><br><span class="line">+ "db > Executed.",</span><br><span class="line">+ "db > ",</span><br><span class="line">+ ])</span><br><span class="line">+ end</span><br><span class="line">+end</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2021/05/06/057-simple-database-3/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/05/06/057-simple-database-3/" class="post-title-link" itemprop="url">Let's Build a Simple Database - An In-Memory,Append-Only,Sinle-Table Database</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-05-06 21:12:13" itemprop="dateCreated datePublished" datetime="2021-05-06T21:12:13+00:00">2021-05-06</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/翻译/" itemprop="url" rel="index"><span itemprop="name">翻译</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>原文地址:<a href="https://cstack.github.io/db_tutorial/parts/part3.html" target="_blank" rel="noopener">https://cstack.github.io/db_tutorial/parts/part3.html</a><br>原文作者: <a href="https://github.com/cstack" target="_blank" rel="noopener">cstack</a><br>译者:<a href="https://github.com/Win-Man" target="_blank" rel="noopener">Win-Man</a></p>
</blockquote>
<p>我们通过在我们数据库上设置很多限制,从一个小的数据库开始。这一节,我们会做这些内容:</p>
<ul>
<li>支持两种操作:插入一行数据和查询出所有数据行</li>
<li>将数据存储在内存中(不持久化到硬盘上)</li>
<li>支持单个、表结构固定的表</li>
</ul>
<p>我们表结构固定的表示用于存储用户的,结构是:</p>
<table>
<thead>
<tr>
<th>column</th>
<th>type</th>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>integer</td>
</tr>
<tr>
<td>username</td>
<td>varchar(32)</td>
</tr>
<tr>
<td>email</td>
<td>varchar(255)</td>
</tr>
</tbody>
</table>
<p>这是一个简单的表结构,但是可以让我们去支持多种数据格式和多种数据大小。</p>
<p>insert 语句会类似于这个形式</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">insert</span> <span class="number">1</span> cstack [email protected]</span><br></pre></td></tr></table></figure>
<p>这意味着我们需要更新我们的 <code>prepare_statement</code> 函数去解析参数</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line"> statement->type = STATEMENT_INSERT;</span><br><span class="line">+ <span class="keyword">int</span> args_assigned = <span class="built_in">sscanf</span>(</span><br><span class="line">+ input_buffer->buffer, <span class="string">"insert %d %s %s"</span>, &(statement->row_to_insert.id),</span><br><span class="line">+ statement->row_to_insert.username, statement->row_to_insert.email);</span><br><span class="line">+ <span class="keyword">if</span> (args_assigned < <span class="number">3</span>) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SYNTAX_ERROR;</span><br><span class="line">+ }</span><br><span class="line"> <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">"select"</span>) == <span class="number">0</span>) {</span><br></pre></td></tr></table></figure>
<p>我们将这些解析出来的参数存储到一个 Row 数据结构中:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> COLUMN_USERNAME_SIZE 32</span></span><br><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> COLUMN_EMAIL_SIZE 255</span></span><br><span class="line">+<span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line">+ <span class="keyword">uint32_t</span> id;</span><br><span class="line">+ <span class="keyword">char</span> username[COLUMN_USERNAME_SIZE];</span><br><span class="line">+ <span class="keyword">char</span> email[COLUMN_EMAIL_SIZE];</span><br><span class="line">+} Row;</span><br><span class="line">+</span><br><span class="line"> <span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> StatementType type;</span><br><span class="line">+ Row row_to_insert; <span class="comment">// only used by insert statement</span></span><br><span class="line"> } Statement;</span><br></pre></td></tr></table></figure>
<p>现在我们需要将 Row 结构体中的数据拷贝到能代表 table 数据的结构中。 SQLite 使用了 B-tree 数据结构,可以提升查询、插入、删除的速度。我们开始的会更简单一点。类似于 B-tree, 会将行记录分组到 page 页上,但是是将这些数据页以数据形式存储,而不是以树的形式存储。</p>
<p>我们的计划是:</p>
<ul>
<li>将数据行存储在内存页中</li>
<li>每个数据页可以存储尽可能多的行</li>
<li>记录行被序列化成一种压缩的形式</li>
<li>数据页只有在需要的时候才会分配</li>
<li>保持一个固定大小的数据页指针数组</li>
</ul>
<p>首先,我们需要定义一下一行记录的压缩格式是怎么样的:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> size_of_attribute(Struct, Attribute) sizeof(((Struct*)0)->Attribute)</span></span><br><span class="line">+</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ID_SIZE = size_of_attribute(Row, id);</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> USERNAME_SIZE = size_of_attribute(Row, username);</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> EMAIL_SIZE = size_of_attribute(Row, email);</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ID_OFFSET = <span class="number">0</span>;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> USERNAME_OFFSET = ID_OFFSET + ID_SIZE;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> EMAIL_OFFSET = USERNAME_OFFSET + USERNAME_SIZE;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ROW_SIZE = ID_SIZE + USERNAME_SIZE + EMAIL_SIZE;</span><br></pre></td></tr></table></figure>
<p>这意味着被序列化之后的一行数据会是这样的:</p>
<table>
<thead>
<tr>
<th>column</th>
<th>size(bytes)</th>
<th>offset</th>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>4</td>
<td>0</td>
</tr>
<tr>
<td>username</td>
<td>32</td>
<td>4</td>
</tr>
<tr>
<td>email</td>
<td>255</td>
<td>36</td>
</tr>
<tr>
<td>total</td>
<td>291</td>
</tr>
</tbody>
</table>
<p>我们也需要将编写转换、反转换的代码.</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">serialize_row</span><span class="params">(Row* source, <span class="keyword">void</span>* destination)</span> </span>{</span><br><span class="line">+ <span class="built_in">memcpy</span>(destination + ID_OFFSET, &(source->id), ID_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(destination + USERNAME_OFFSET, &(source->username), USERNAME_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(destination + EMAIL_OFFSET, &(source->email), EMAIL_SIZE);</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">deserialize_row</span><span class="params">(<span class="keyword">void</span>* source, Row* destination)</span> </span>{</span><br><span class="line">+ <span class="built_in">memcpy</span>(&(destination->id), source + ID_OFFSET, ID_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(&(destination->username), source + USERNAME_OFFSET, USERNAME_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(&(destination->email), source + EMAIL_OFFSET, EMAIL_SIZE);</span><br><span class="line">+}</span><br></pre></td></tr></table></figure>
<p>接着,Table 的数据结构就是一个数据页指针和记录这个表内有多少数据:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> PAGE_SIZE = <span class="number">4096</span>;</span><br><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> TABLE_MAX_PAGES 100</span></span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ROWS_PER_PAGE = PAGE_SIZE / ROW_SIZE;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line">+ <span class="keyword">uint32_t</span> num_rows;</span><br><span class="line">+ <span class="keyword">void</span>* pages[TABLE_MAX_PAGES];</span><br><span class="line">+} Table;</span><br></pre></td></tr></table></figure>
<p>我将我们数据页的大小设置为了 4KB ,因为这也是大多数操作系统使用的数据页大小。意味着我们的使用的数据页大小和操作系统数据页大小是一致的。操作系统会将数据页作为一个整体进行交换,而不是将他们拆散成多个数据页。</p>
<p>我随意设置了一个限制,限制我们只能分配 100 个数据页。当我们转成树结构的时候,我们数据库的最大大小受限于单个文件的大小(尽管我们还是有设置我们在内存中保留多少数据页)</p>
<p>一行数据不能跨数据页存储。因为相邻的数据页可能不会同时存在在内存中,这个限制可以让读写行数据更加容易。</p>
<p>说到这个,看一下我们是如果在内存中定位到需要读取的某一行数据的:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">+<span class="function"><span class="keyword">void</span>* <span class="title">row_slot</span><span class="params">(Table* table, <span class="keyword">uint32_t</span> row_num)</span> </span>{</span><br><span class="line">+ <span class="keyword">uint32_t</span> page_num = row_num / ROWS_PER_PAGE;</span><br><span class="line">+ <span class="keyword">void</span>* page = table->pages[page_num];</span><br><span class="line">+ <span class="keyword">if</span> (page == <span class="literal">NULL</span>) {</span><br><span class="line">+ <span class="comment">// Allocate memory only when we try to access page</span></span><br><span class="line">+ page = table->pages[page_num] = <span class="built_in">malloc</span>(PAGE_SIZE);</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">uint32_t</span> row_offset = row_num % ROWS_PER_PAGE;</span><br><span class="line">+ <span class="keyword">uint32_t</span> byte_offset = row_offset * ROW_SIZE;</span><br><span class="line">+ <span class="keyword">return</span> page + byte_offset;</span><br><span class="line">+}</span><br></pre></td></tr></table></figure>
<p>现在我们可以让 execute_statement 从表结构体中读取或者写入数据了:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line">-<span class="function"><span class="keyword">void</span> <span class="title">execute_statement</span><span class="params">(Statement* statement)</span> </span>{</span><br><span class="line">+<span class="function">ExecuteResult <span class="title">execute_insert</span><span class="params">(Statement* statement, Table* table)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (table->num_rows >= TABLE_MAX_ROWS) {</span><br><span class="line">+ <span class="keyword">return</span> EXECUTE_TABLE_FULL;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ Row* row_to_insert = &(statement->row_to_insert);</span><br><span class="line">+</span><br><span class="line">+ serialize_row(row_to_insert, row_slot(table, table->num_rows));</span><br><span class="line">+ table->num_rows += <span class="number">1</span>;</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">return</span> EXECUTE_SUCCESS;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">ExecuteResult <span class="title">execute_select</span><span class="params">(Statement* statement, Table* table)</span> </span>{</span><br><span class="line">+ Row row;</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">uint32_t</span> i = <span class="number">0</span>; i < table->num_rows; i++) {</span><br><span class="line">+ deserialize_row(row_slot(table, i), &row);</span><br><span class="line">+ print_row(&row);</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">return</span> EXECUTE_SUCCESS;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">ExecuteResult <span class="title">execute_statement</span><span class="params">(Statement* statement, Table* table)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (statement->type) {</span><br><span class="line"> <span class="keyword">case</span> (STATEMENT_INSERT):</span><br><span class="line">- <span class="built_in">printf</span>(<span class="string">"This is where we would do an insert.\n"</span>);</span><br><span class="line">- <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">return</span> execute_insert(statement, table);</span><br><span class="line"> <span class="keyword">case</span> (STATEMENT_SELECT):</span><br><span class="line">- <span class="built_in">printf</span>(<span class="string">"This is where we would do a select.\n"</span>);</span><br><span class="line">- <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">return</span> execute_select(statement, table);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>最后,我们需要初始化一下表,创建释放各自内存的函数以及处理更多错误的情况:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">+ <span class="function">Table* <span class="title">new_table</span><span class="params">()</span> </span>{</span><br><span class="line">+ Table* table = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Table));</span><br><span class="line">+ table->num_rows = <span class="number">0</span>;</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">uint32_t</span> i = <span class="number">0</span>; i < TABLE_MAX_PAGES; i++) {</span><br><span class="line">+ table->pages[i] = <span class="literal">NULL</span>;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">return</span> table;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">free_table</span><span class="params">(Table* table)</span> </span>{</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; table->pages[i]; i++) {</span><br><span class="line">+ <span class="built_in">free</span>(table->pages[i]);</span><br><span class="line">+ }</span><br><span class="line">+ <span class="built_in">free</span>(table);</span><br><span class="line">+}</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line">+ Table* table = new_table();</span><br><span class="line"> InputBuffer* input_buffer = new_input_buffer();</span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> print_prompt();</span><br><span class="line">@@ <span class="number">-105</span>,<span class="number">13</span> +<span class="number">203</span>,<span class="number">22</span> @@ <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_SYNTAX_ERROR):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Syntax error. Could not parse statement.\n"</span>);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">case</span> (PREPARE_UNRECOGNIZED_STATEMENT):</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Unrecognized keyword at start of '%s'.\n"</span>,</span><br><span class="line"> input_buffer->buffer);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">- execute_statement(&statement);</span><br><span class="line">- <span class="built_in">printf</span>(<span class="string">"Executed.\n"</span>);</span><br><span class="line">+ <span class="keyword">switch</span> (execute_statement(&statement, table)) {</span><br><span class="line">+ <span class="keyword">case</span> (EXECUTE_SUCCESS):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Executed.\n"</span>);</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (EXECUTE_TABLE_FULL):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Error: Table full.\n"</span>);</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ }</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>经过这些修改,我们就可以真正的在我们的数据库中存储数据了</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">~ ./db</span><br><span class="line">db > insert 1 cstack [email protected]</span><br><span class="line">Executed.</span><br><span class="line">db > insert 2 bob [email protected]</span><br><span class="line">Executed.</span><br><span class="line">db > select</span><br><span class="line">(1, cstack, [email protected])</span><br><span class="line">(2, bob, [email protected])</span><br><span class="line">Executed.</span><br><span class="line">db > insert foo bar 1</span><br><span class="line">Syntax error. Could not parse statement.</span><br><span class="line">db > .exit</span><br></pre></td></tr></table></figure>
<p>这时候是写一些测试的好时候了,因为以下几个原因:</p>
<ul>
<li>我们计划修改数据结构去存储我们的表,有测试的话可以方便捕捉到回归错误</li>
<li>有一些边缘 case 我们没有手动测试(比如填满一张表)</li>
</ul>
<p>我们会在下一个部分做这些内容。现在先看下这个部分完整修改的内容:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br></pre></td><td class="code"><pre><span class="line">@@ <span class="number">-2</span>,<span class="number">6</span> +<span class="number">2</span>,<span class="number">7</span> @@</span><br><span class="line"> <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdio.h></span></span></span><br><span class="line"> <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdlib.h></span></span></span><br><span class="line"> <span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string.h></span></span></span><br><span class="line">+<span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><stdint.h></span></span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> <span class="keyword">char</span>* buffer;</span><br><span class="line">@@ <span class="number">-10</span>,<span class="number">6</span> +<span class="number">11</span>,<span class="number">105</span> @@ <span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> } InputBuffer;</span><br><span class="line"></span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> { EXECUTE_SUCCESS, EXECUTE_TABLE_FULL } ExecuteResult;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> {</span><br><span class="line">+ META_COMMAND_SUCCESS,</span><br><span class="line">+ META_COMMAND_UNRECOGNIZED_COMMAND</span><br><span class="line">+} MetaCommandResult;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> {</span><br><span class="line">+ PREPARE_SUCCESS,</span><br><span class="line">+ PREPARE_SYNTAX_ERROR,</span><br><span class="line">+ PREPARE_UNRECOGNIZED_STATEMENT</span><br><span class="line">+ } PrepareResult;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> { STATEMENT_INSERT, STATEMENT_SELECT } StatementType;</span><br><span class="line">+</span><br><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> COLUMN_USERNAME_SIZE 32</span></span><br><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> COLUMN_EMAIL_SIZE 255</span></span><br><span class="line">+<span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line">+ <span class="keyword">uint32_t</span> id;</span><br><span class="line">+ <span class="keyword">char</span> username[COLUMN_USERNAME_SIZE];</span><br><span class="line">+ <span class="keyword">char</span> email[COLUMN_EMAIL_SIZE];</span><br><span class="line">+} Row;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line">+ StatementType type;</span><br><span class="line">+ Row row_to_insert; <span class="comment">//only used by insert statement</span></span><br><span class="line">+} Statement;</span><br><span class="line">+</span><br><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> size_of_attribute(Struct, Attribute) sizeof(((Struct*)0)->Attribute)</span></span><br><span class="line">+</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ID_SIZE = size_of_attribute(Row, id);</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> USERNAME_SIZE = size_of_attribute(Row, username);</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> EMAIL_SIZE = size_of_attribute(Row, email);</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ID_OFFSET = <span class="number">0</span>;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> USERNAME_OFFSET = ID_OFFSET + ID_SIZE;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> EMAIL_OFFSET = USERNAME_OFFSET + USERNAME_SIZE;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ROW_SIZE = ID_SIZE + USERNAME_SIZE + EMAIL_SIZE;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> PAGE_SIZE = <span class="number">4096</span>;</span><br><span class="line">+<span class="meta">#<span class="meta-keyword">define</span> TABLE_MAX_PAGES 100</span></span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> ROWS_PER_PAGE = PAGE_SIZE / ROW_SIZE;</span><br><span class="line">+<span class="keyword">const</span> <span class="keyword">uint32_t</span> TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line">+ <span class="keyword">uint32_t</span> num_rows;</span><br><span class="line">+ <span class="keyword">void</span>* pages[TABLE_MAX_PAGES];</span><br><span class="line">+} Table;</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">print_row</span><span class="params">(Row* row)</span> </span>{</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"(%d, %s, %s)\n"</span>, row->id, row->username, row->email);</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">serialize_row</span><span class="params">(Row* source, <span class="keyword">void</span>* destination)</span> </span>{</span><br><span class="line">+ <span class="built_in">memcpy</span>(destination + ID_OFFSET, &(source->id), ID_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(destination + USERNAME_OFFSET, &(source->username), USERNAME_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(destination + EMAIL_OFFSET, &(source->email), EMAIL_SIZE);</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">deserialize_row</span><span class="params">(<span class="keyword">void</span> *source, Row* destination)</span> </span>{</span><br><span class="line">+ <span class="built_in">memcpy</span>(&(destination->id), source + ID_OFFSET, ID_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(&(destination->username), source + USERNAME_OFFSET, USERNAME_SIZE);</span><br><span class="line">+ <span class="built_in">memcpy</span>(&(destination->email), source + EMAIL_OFFSET, EMAIL_SIZE);</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span>* <span class="title">row_slot</span><span class="params">(Table* table, <span class="keyword">uint32_t</span> row_num)</span> </span>{</span><br><span class="line">+ <span class="keyword">uint32_t</span> page_num = row_num / ROWS_PER_PAGE;</span><br><span class="line">+ <span class="keyword">void</span> *page = table->pages[page_num];</span><br><span class="line">+ <span class="keyword">if</span> (page == <span class="literal">NULL</span>) {</span><br><span class="line">+ <span class="comment">// Allocate memory only when we try to access page</span></span><br><span class="line">+ page = table->pages[page_num] = <span class="built_in">malloc</span>(PAGE_SIZE);</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">uint32_t</span> row_offset = row_num % ROWS_PER_PAGE;</span><br><span class="line">+ <span class="keyword">uint32_t</span> byte_offset = row_offset * ROW_SIZE;</span><br><span class="line">+ <span class="keyword">return</span> page + byte_offset;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">Table* <span class="title">new_table</span><span class="params">()</span> </span>{</span><br><span class="line">+ Table* table = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Table));</span><br><span class="line">+ table->num_rows = <span class="number">0</span>;</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">uint32_t</span> i = <span class="number">0</span>; i < TABLE_MAX_PAGES; i++) {</span><br><span class="line">+ table->pages[i] = <span class="literal">NULL</span>;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">return</span> table;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">free_table</span><span class="params">(Table* table)</span> </span>{</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; table->pages[i]; i++) {</span><br><span class="line">+ <span class="built_in">free</span>(table->pages[i]);</span><br><span class="line">+ }</span><br><span class="line">+ <span class="built_in">free</span>(table);</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line"> <span class="function">InputBuffer* <span class="title">new_input_buffer</span><span class="params">()</span> </span>{</span><br><span class="line"> InputBuffer* input_buffer = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(InputBuffer));</span><br><span class="line"> input_buffer->buffer = <span class="literal">NULL</span>;</span><br><span class="line">@@ <span class="number">-40</span>,<span class="number">17</span> +<span class="number">140</span>,<span class="number">105</span> @@ <span class="function"><span class="keyword">void</span> <span class="title">close_input_buffer</span><span class="params">(InputBuffer* input_buffer)</span> </span>{</span><br><span class="line"> <span class="built_in">free</span>(input_buffer);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">+<span class="function">MetaCommandResult <span class="title">do_meta_command</span><span class="params">(InputBuffer* input_buffer, Table *table)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">".exit"</span>) == <span class="number">0</span>) {</span><br><span class="line">+ close_input_buffer(input_buffer);</span><br><span class="line">+ free_table(table);</span><br><span class="line">+ <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">+ } <span class="keyword">else</span> {</span><br><span class="line">+ <span class="keyword">return</span> META_COMMAND_UNRECOGNIZED_COMMAND;</span><br><span class="line">+ }</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">PrepareResult <span class="title">prepare_statement</span><span class="params">(InputBuffer* input_buffer,</span></span></span><br><span class="line"><span class="function"><span class="params">+ Statement* statement)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line">+ statement->type = STATEMENT_INSERT;</span><br><span class="line">+ <span class="keyword">int</span> args_assigned = <span class="built_in">sscanf</span>(</span><br><span class="line">+ input_buffer->buffer, <span class="string">"insert %d %s %s"</span>, &(statement->row_to_insert.id),</span><br><span class="line">+ statement->row_to_insert.username, statement->row_to_insert.email</span><br><span class="line">+ );</span><br><span class="line">+ <span class="keyword">if</span> (args_assigned < <span class="number">3</span>) {</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SYNTAX_ERROR;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">"select"</span>) == <span class="number">0</span>) {</span><br><span class="line">+ statement->type = STATEMENT_SELECT;</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_UNRECOGNIZED_STATEMENT;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">ExecuteResult <span class="title">execute_insert</span><span class="params">(Statement* statement, Table* table)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (table->num_rows >= TABLE_MAX_ROWS) {</span><br><span class="line">+ <span class="keyword">return</span> EXECUTE_TABLE_FULL;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ Row* row_to_insert = &(statement->row_to_insert);</span><br><span class="line">+</span><br><span class="line">+ serialize_row(row_to_insert, row_slot(table, table->num_rows));</span><br><span class="line">+ table->num_rows += <span class="number">1</span>;</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">return</span> EXECUTE_SUCCESS;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">ExecuteResult <span class="title">execute_select</span><span class="params">(Statement* statement, Table* table)</span> </span>{</span><br><span class="line">+ Row row;</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">uint32_t</span> i = <span class="number">0</span>; i < table->num_rows; i++) {</span><br><span class="line">+ deserialize_row(row_slot(table, i), &row);</span><br><span class="line">+ print_row(&row);</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">return</span> EXECUTE_SUCCESS;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">ExecuteResult <span class="title">execute_statement</span><span class="params">(Statement* statement, Table *table)</span> </span>{</span><br><span class="line">+ <span class="keyword">switch</span> (statement->type) {</span><br><span class="line">+ <span class="keyword">case</span> (STATEMENT_INSERT):</span><br><span class="line">+ <span class="keyword">return</span> execute_insert(statement, table);</span><br><span class="line">+ <span class="keyword">case</span> (STATEMENT_SELECT):</span><br><span class="line">+ <span class="keyword">return</span> execute_select(statement, table);</span><br><span class="line">+ }</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line">+ Table* table = new_table();</span><br><span class="line"> InputBuffer* input_buffer = new_input_buffer();</span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> print_prompt();</span><br><span class="line"> read_input(input_buffer);</span><br><span class="line"></span><br><span class="line">- <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">".exit"</span>) == <span class="number">0</span>) {</span><br><span class="line">- close_input_buffer(input_buffer);</span><br><span class="line">- <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">- } <span class="keyword">else</span> {</span><br><span class="line">- <span class="built_in">printf</span>(<span class="string">"Unrecognized command '%s'.\n"</span>, input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">if</span> (input_buffer->buffer[<span class="number">0</span>] == <span class="string">'.'</span>) {</span><br><span class="line">+ <span class="keyword">switch</span> (do_meta_command(input_buffer, table)) {</span><br><span class="line">+ <span class="keyword">case</span> (META_COMMAND_SUCCESS):</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ <span class="keyword">case</span> (META_COMMAND_UNRECOGNIZED_COMMAND):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Unrecognized command '%s'\n"</span>, input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ }</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ Statement statement;</span><br><span class="line">+ <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_SYNTAX_ERROR):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Syntax error. Could not parse statement.\n"</span>);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_UNRECOGNIZED_STATEMENT):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Unrecognized keyword at start of '%s'.\n"</span>,</span><br><span class="line">+ input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">switch</span> (execute_statement(&statement, table)) {</span><br><span class="line">+ <span class="keyword">case</span> (EXECUTE_SUCCESS):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Executed.\n"</span>);</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (EXECUTE_TABLE_FULL):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Error: Table full.\n"</span>);</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2021/04/12/057-travis-troubleshooting/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/04/12/057-travis-troubleshooting/" class="post-title-link" itemprop="url">hexo next 主题通过 travis ci 执行 hexo generate 报错</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-04-12 21:12:13" itemprop="dateCreated datePublished" datetime="2021-04-12T21:12:13+00:00">2021-04-12</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/troubleshooting/" itemprop="url" rel="index"><span itemprop="name">troubleshooting</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="问题记录"><a href="#问题记录" class="headerlink" title="问题记录"></a>问题记录</h2><p>前段时间给 github page 换了一个 hexo 主题,换成了 <a href="https://theme-next.iissnan.com/" target="_blank" rel="noopener">next</a> 主题,但是在更换主题之后,发现 travis 上的 CI 无法跑过了,查看日志内容如下:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> node --version</span><br><span class="line">v6.9.4</span><br><span class="line"><span class="meta">$</span> npm --version</span><br><span class="line">3.10.10</span><br><span class="line"><span class="meta">$</span> nvm --version</span><br><span class="line">0.37.2</span><br><span class="line">before_install.1</span><br><span class="line">0.01s$ export TZ='Asia/Shanghai'</span><br><span class="line">before_install.2</span><br><span class="line">6.90s$ npm install -g hexo</span><br><span class="line">before_install.3</span><br><span class="line">2.81s$ npm install -g hexo-cli</span><br><span class="line">install</span><br><span class="line">1.54s$ npm install</span><br><span class="line">before_script.1</span><br><span class="line">0.01s$ git config --global user.name "Win-Man"</span><br><span class="line">before_script.2</span><br><span class="line">0.00s$ git config --global user.email [email protected]</span><br><span class="line">before_script.3</span><br><span class="line">0.00s$ sed -i'' "[email protected]:Win-Man/win-man.github.io.git~https://${CI_TOKEN}@github.com/Win-Man/win-man.github.io.git~" _config.yml</span><br><span class="line">0.67s$ hexo clean</span><br><span class="line">The command "hexo clean" exited with 0.</span><br><span class="line">1.17s$ hexo generate</span><br><span class="line">INFO Start processing</span><br><span class="line">FATAL Something's wrong. Maybe you can find the solution here: https://hexo.io/docs/troubleshooting.html</span><br><span class="line">TypeError: Object.values is not a function</span><br><span class="line"> at points.views.forEach.type (/home/travis/build/Win-Man/win-man.github.io/themes/next/scripts/events/lib/injects.js:82:46)</span><br><span class="line"> at Array.forEach (native)</span><br><span class="line"> at module.exports.hexo (/home/travis/build/Win-Man/win-man.github.io/themes/next/scripts/events/lib/injects.js:67:16)</span><br><span class="line"> at Hexo.hexo.on (/home/travis/build/Win-Man/win-man.github.io/themes/next/scripts/events/index.js:9:27)</span><br><span class="line"> at emitNone (events.js:86:13)</span><br><span class="line"> at Hexo.emit (events.js:185:7)</span><br><span class="line"> at Hexo._generate (/home/travis/build/Win-Man/win-man.github.io/node_modules/hexo/lib/hexo/index.js:399:8)</span><br><span class="line"> at loadDatabase.then.then (/home/travis/build/Win-Man/win-man.github.io/node_modules/hexo/lib/hexo/index.js:249:22)</span><br><span class="line"> at tryCatcher (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/util.js:16:23)</span><br><span class="line"> at Promise._settlePromiseFromHandler (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise.js:547:31)</span><br><span class="line"> at Promise._settlePromise (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise.js:604:18)</span><br><span class="line"> at Promise._settlePromise0 (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise.js:649:10)</span><br><span class="line"> at Promise._settlePromises (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise.js:729:18)</span><br><span class="line"> at Promise._fulfill (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise.js:673:18)</span><br><span class="line"> at PromiseArray._resolve (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise_array.js:127:19)</span><br><span class="line"> at PromiseArray._promiseFulfilled (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise_array.js:145:14)</span><br><span class="line"> at Promise._settlePromise (/home/travis/build/Win-Man/win-man.github.io/node_modules/bluebird/js/release/promise.js:609:26)</span><br><span class="line">The command "hexo generate" exited with 2.</span><br><span class="line">cache.2</span><br><span class="line">store build cache</span><br></pre></td></tr></table></figure>
<p>但是在我本地电脑执行 <code>hexo g</code>,<code>hexo s</code>,<code>hexo d</code> 都没啥问题,怀疑到了 node js 版本的问题,因为这个 repo 中的 <code>.travis.yml</code> 配置文件还是好几年前的了,于是尝试修改 node js 版本与本地版本一致。修改版本并提交之后还是有报错,但是报错信息变了。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> node --version</span><br><span class="line">v13.11.0</span><br><span class="line"><span class="meta">$</span> npm --version</span><br><span class="line">6.13.7</span><br><span class="line"><span class="meta">$</span> nvm --version</span><br><span class="line">0.37.2</span><br><span class="line">before_install.1</span><br><span class="line">0.01s$ export TZ='Asia/Shanghai'</span><br><span class="line">before_install.2</span><br><span class="line">5.40s$ npm install -g hexo</span><br><span class="line">3.16s$ npm install -g hexo-cli</span><br><span class="line">npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.3.1 (node_modules/hexo-cli/node_modules/chokidar/node_modules/fsevents):</span><br><span class="line">npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})</span><br><span class="line">npm ERR! code EEXIST</span><br><span class="line">npm ERR! syscall symlink</span><br><span class="line">npm ERR! path ../lib/node_modules/hexo-cli/bin/hexo</span><br><span class="line">npm ERR! dest /home/travis/.nvm/versions/node/v13.11.0/bin/hexo</span><br><span class="line">npm ERR! errno -17</span><br><span class="line">npm ERR! EEXIST: file already exists, symlink '../lib/node_modules/hexo-cli/bin/hexo' -> '/home/travis/.nvm/versions/node/v13.11.0/bin/hexo'</span><br><span class="line">npm ERR! File exists: /home/travis/.nvm/versions/node/v13.11.0/bin/hexo</span><br><span class="line">npm ERR! Remove the existing file and try again, or run npm</span><br><span class="line">npm ERR! with --force to overwrite files recklessly.</span><br><span class="line">npm ERR! A complete log of this run can be found in:</span><br><span class="line">npm ERR! /home/travis/.npm/_logs/2021-04-12T06_49_56_362Z-debug.log</span><br><span class="line">The command "npm install -g hexo-cli" failed and exited with 239 during .</span><br></pre></td></tr></table></figure>
<p>根据错误信息查找到一个链接<br><a href="https://segmentfault.com/a/1190000018759308" target="_blank" rel="noopener">https://segmentfault.com/a/1190000018759308</a></p>
<h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>参考链接中的结果方案在 <code>npm install</code> 中加上 <code>-f</code> 选项。<br>附上完整的 <code>.travis.yml</code> 内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">language: node_js</span><br><span class="line">node_js:</span><br><span class="line">- '13.11.0'</span><br><span class="line">branches:</span><br><span class="line"> only:</span><br><span class="line"> - dev</span><br><span class="line">cache:</span><br><span class="line"> directories:</span><br><span class="line"> - node_modules</span><br><span class="line">before_install:</span><br><span class="line">- export TZ='Asia/Shanghai'</span><br><span class="line">- npm install -g hexo</span><br><span class="line">- npm install -g hexo-cli -f</span><br><span class="line">before_script:</span><br><span class="line">- git config --global user.name "Win-Man"</span><br><span class="line">- git config --global user.email [email protected]</span><br><span class="line">- sed -i'' "[email protected]:Win-Man/win-man.github.io.git~https://${CI_TOKEN}@github.com/Win-Man/win-man.github.io.git~" _config.yml</span><br><span class="line">install:</span><br><span class="line">- npm install -f</span><br><span class="line">script:</span><br><span class="line">- hexo clean</span><br><span class="line">- hexo generate</span><br><span class="line">after_success:</span><br><span class="line">- hexo deploy</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2021/04/11/056-simple-database-2/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/04/11/056-simple-database-2/" class="post-title-link" itemprop="url">Let's Build a Simple Database - World's Simplest SQL Compiler and Virtual Machine</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-04-11 21:12:13" itemprop="dateCreated datePublished" datetime="2021-04-11T21:12:13+00:00">2021-04-11</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/翻译/" itemprop="url" rel="index"><span itemprop="name">翻译</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>原文地址:<a href="https://cstack.github.io/db_tutorial/parts/part2.html" target="_blank" rel="noopener">https://cstack.github.io/db_tutorial/parts/part2.html</a><br>原文作者: <a href="https://github.com/cstack" target="_blank" rel="noopener">cstack</a><br>译者:<a href="https://github.com/Win-Man" target="_blank" rel="noopener">Win-Man</a></p>
</blockquote>
<h2 id="世界上最简单的-SQL-编译器和字节码虚拟机"><a href="#世界上最简单的-SQL-编译器和字节码虚拟机" class="headerlink" title="世界上最简单的 SQL 编译器和字节码虚拟机"></a>世界上最简单的 SQL 编译器和字节码虚拟机</h2><p>我们正在写一个 sqlite 的克隆版本。sqlite 中 SQL 编译器是一个解析一个 string 输入并输出一个内部可识别的字节码。然后 sqlite 编译之后的字节码传给字节码虚拟机,在虚拟机中执行 SQL。</p>
<p><img src="https://cstack.github.io/db_tutorial/assets/images/arch2.gif" alt></p>
<p><center>SQLite Architecture (<a href="https://www.sqlite.org/arch.html" target="_blank" rel="noopener">https://www.sqlite.org/arch.html</a>)<center></center></center></p>
<p>将这些内容拆成两个步骤有一些优势点:</p>
<ul>
<li>减少每个部门的复杂度(例如:字节码虚拟机不用考虑语法错误)</li>
<li>对于相同的查询可以只需要编译一次,将编译后的字节码缓存住,以此可以提高性能</li>
</ul>
<p>基于这个想法,我们重构一下我们的主函数来支持两个新的关键字:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line"> InputBuffer* input_buffer = new_input_buffer();</span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> print_prompt();</span><br><span class="line"> read_input(input_buffer);</span><br><span class="line"></span><br><span class="line">- <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">".exit"</span>) == <span class="number">0</span>) {</span><br><span class="line">- <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">- } <span class="keyword">else</span> {</span><br><span class="line">- <span class="built_in">printf</span>(<span class="string">"Unrecognized command '%s'.\n"</span>, input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">if</span> (input_buffer->buffer[<span class="number">0</span>] == <span class="string">'.'</span>) {</span><br><span class="line">+ <span class="keyword">switch</span> (do_meta_command(input_buffer)) {</span><br><span class="line">+ <span class="keyword">case</span> (META_COMMAND_SUCCESS):</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ <span class="keyword">case</span> (META_COMMAND_UNRECOGNIZED_COMMAND):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Unrecognized command '%s'\n"</span>, input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ }</span><br><span class="line"> }</span><br><span class="line">+</span><br><span class="line">+ Statement statement;</span><br><span class="line">+ <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_UNRECOGNIZED_STATEMENT):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Unrecognized keyword at start of '%s'.\n"</span>,</span><br><span class="line">+ input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ execute_statement(&statement);</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Executed.\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>像 <code>.exit</code> 这种非 SQL 的语句被称为“元指令”。这些元指令都是以 <code>.</code> 开始的,所以我们可以将这类语句单独在一个独立的函数中处理。</p>
<p>接下来,我们增加一个步骤用于将输入的行转换为数据库内部表达式。这就是我们对于 sqlite 前端的 hack 版本。</p>
<p>最后,我们将预处理好的语句传递给 <code>execute_statement</code>。这个函数后续会演进成我们的字节码虚拟机。</p>
<p>需要需要的是我们新增加了两个函数,并且这两个函数返回 enum 来表示成功或者失败。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> {</span><br><span class="line"> META_COMMAND_SUCCESS,</span><br><span class="line"> META_COMMAND_UNRECOGNIZED_COMMAND</span><br><span class="line">} MetaCommandResult;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> { PREPARE_SUCCESS, PREPARE_UNRECOGNIZED_STATEMENT } PrepareResult;</span><br></pre></td></tr></table></figure>
<p>“Unrecognized statement” 语句表示这个语句像是一个表达式,但是表达式有错误,所以我们在任何情况都使用 enum 枚举结果代码。C 语言的编译器会报错如果我的 switch 语句不能处理枚举的项,所以我们可以确保处理了这个函数的每个结果。在将来可能会添加更多的结果代码。</p>
<p><code>do_meta_command</code> 只是现有函数的一个封装,为了之后可处理更多的命令:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">MetaCommandResult <span class="title">do_meta_command</span><span class="params">(InputBuffer* input_buffer)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">".exit"</span>) == <span class="number">0</span>) {</span><br><span class="line"> <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> META_COMMAND_UNRECOGNIZED_COMMAND;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>目前我们的预处理语句只是包含了有两个可能结果的枚举数组。这可能会包含更多的数据如果我们允许在语句中传入参数:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">enum</span> { STATEMENT_INSERT, STATEMENT_SELECT } StatementType;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line"> StatementType type;</span><br><span class="line">} Statement;</span><br></pre></td></tr></table></figure>
<p><code>prepare_statement</code>(我们的 SQL 编译器)目前还无法识别 SQL 语句。事实上这个函数只能识别两个单词:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">PrepareResult prepare_statement(InputBuffer* input_buffer,</span><br><span class="line"> Statement* statement) {</span><br><span class="line"> if (strncmp(input_buffer->buffer, "insert", 6) == 0) {</span><br><span class="line"> statement->type = STATEMENT_INSERT;</span><br><span class="line"> return PREPARE_SUCCESS;</span><br><span class="line"> }</span><br><span class="line"> if (strcmp(input_buffer->buffer, "select") == 0) {</span><br><span class="line"> statement->type = STATEMENT_SELECT;</span><br><span class="line"> return PREPARE_SUCCESS;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return PREPARE_UNRECOGNIZED_STATEMENT;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>需要注意的是我们使用 strncmp 对比 insert 因为 insert 关键字之后会跟着插入的数据。(例如:insert 1 cstack <a href="mailto:[email protected]" target="_blank" rel="noopener">[email protected]</a>)</p>
<p>最后,<code>execute_statement</code> 包含了一些分支:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">execute_statement</span><span class="params">(Statement* statement)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (statement->type) {</span><br><span class="line"> <span class="keyword">case</span> (STATEMENT_INSERT):</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"This is where we would do an insert.\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> (STATEMENT_SELECT):</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"This is where we would do a select.\n"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这边并没有返回任何错误码因为目前为止这边还不会发生任何错误。</p>
<p>经过这些重构之后,我们现在的 sqlite 可以认识两个新的关键字。</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">~ ./db</span><br><span class="line">db > insert foo bar</span><br><span class="line">This is where we would <span class="keyword">do</span> an insert.</span><br><span class="line">Executed.</span><br><span class="line">db > <span class="keyword">delete</span> foo</span><br><span class="line">Unrecognized keyword <span class="keyword">at</span> <span class="keyword">start</span> <span class="keyword">of</span> <span class="string">'delete foo'</span>.</span><br><span class="line">db > <span class="keyword">select</span></span><br><span class="line">This <span class="keyword">is</span> <span class="keyword">where</span> we would <span class="keyword">do</span> a select.</span><br><span class="line">Executed.</span><br><span class="line">db > .tables</span><br><span class="line">Unrecognized command <span class="string">'.tables'</span></span><br><span class="line">db > .exit</span><br><span class="line">~</span><br></pre></td></tr></table></figure>
<p>我们数据库的基本框架基本已经形成了,如果不用存储数据的话,这是不是很好?在下一个部分,我们会去实现 insert 和 select 语句,创造世界上最差劲的存储引擎。同时在这给出这一差异部分的内容:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line">@@ <span class="number">-10</span>,<span class="number">6</span> +<span class="number">10</span>,<span class="number">23</span> @@ <span class="class"><span class="keyword">struct</span> <span class="title">InputBuffer_t</span> {</span></span><br><span class="line"> } InputBuffer;</span><br><span class="line"> </span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> {</span><br><span class="line">+ META_COMMAND_SUCCESS,</span><br><span class="line">+ META_COMMAND_UNRECOGNIZED_COMMAND</span><br><span class="line">+} MetaCommandResult;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> { PREPARE_SUCCESS, PREPARE_UNRECOGNIZED_STATEMENT } PrepareResult;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="keyword">enum</span> { STATEMENT_INSERT, STATEMENT_SELECT } StatementType;</span><br><span class="line">+</span><br><span class="line">+<span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></span><br><span class="line">+ StatementType type;</span><br><span class="line">+} Statement;</span><br><span class="line">+</span><br><span class="line"> <span class="function">InputBuffer* <span class="title">new_input_buffer</span><span class="params">()</span> </span>{</span><br><span class="line"> InputBuffer* input_buffer = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(InputBuffer));</span><br><span class="line"> input_buffer->buffer = <span class="literal">NULL</span>;</span><br><span class="line">@@ <span class="number">-40</span>,<span class="number">17</span> +<span class="number">57</span>,<span class="number">67</span> @@ <span class="function"><span class="keyword">void</span> <span class="title">close_input_buffer</span><span class="params">(InputBuffer* input_buffer)</span> </span>{</span><br><span class="line"> <span class="built_in">free</span>(input_buffer);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">+<span class="function">MetaCommandResult <span class="title">do_meta_command</span><span class="params">(InputBuffer* input_buffer)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">".exit"</span>) == <span class="number">0</span>) {</span><br><span class="line">+ close_input_buffer(input_buffer);</span><br><span class="line">+ <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">+ } <span class="keyword">else</span> {</span><br><span class="line">+ <span class="keyword">return</span> META_COMMAND_UNRECOGNIZED_COMMAND;</span><br><span class="line">+ }</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function">PrepareResult <span class="title">prepare_statement</span><span class="params">(InputBuffer* input_buffer,</span></span></span><br><span class="line"><span class="function"><span class="params">+ Statement* statement)</span> </span>{</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strncmp</span>(input_buffer->buffer, <span class="string">"insert"</span>, <span class="number">6</span>) == <span class="number">0</span>) {</span><br><span class="line">+ statement->type = STATEMENT_INSERT;</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">+ }</span><br><span class="line">+ <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">"select"</span>) == <span class="number">0</span>) {</span><br><span class="line">+ statement->type = STATEMENT_SELECT;</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_SUCCESS;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ <span class="keyword">return</span> PREPARE_UNRECOGNIZED_STATEMENT;</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line">+<span class="function"><span class="keyword">void</span> <span class="title">execute_statement</span><span class="params">(Statement* statement)</span> </span>{</span><br><span class="line">+ <span class="keyword">switch</span> (statement->type) {</span><br><span class="line">+ <span class="keyword">case</span> (STATEMENT_INSERT):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"This is where we would do an insert.\n"</span>);</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (STATEMENT_SELECT):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"This is where we would do a select.\n"</span>);</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ }</span><br><span class="line">+}</span><br><span class="line">+</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span>* argv[])</span> </span>{</span><br><span class="line"> InputBuffer* input_buffer = new_input_buffer();</span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> print_prompt();</span><br><span class="line"> read_input(input_buffer);</span><br><span class="line"> </span><br><span class="line">- <span class="keyword">if</span> (<span class="built_in">strcmp</span>(input_buffer->buffer, <span class="string">".exit"</span>) == <span class="number">0</span>) {</span><br><span class="line">- close_input_buffer(input_buffer);</span><br><span class="line">- <span class="built_in">exit</span>(EXIT_SUCCESS);</span><br><span class="line">- } <span class="keyword">else</span> {</span><br><span class="line">- <span class="built_in">printf</span>(<span class="string">"Unrecognized command '%s'.\n"</span>, input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">if</span> (input_buffer->buffer[<span class="number">0</span>] == <span class="string">'.'</span>) {</span><br><span class="line">+ <span class="keyword">switch</span> (do_meta_command(input_buffer)) {</span><br><span class="line">+ <span class="keyword">case</span> (META_COMMAND_SUCCESS):</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ <span class="keyword">case</span> (META_COMMAND_UNRECOGNIZED_COMMAND):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Unrecognized command '%s'\n"</span>, input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ }</span><br><span class="line"> }</span><br><span class="line">+</span><br><span class="line">+ Statement statement;</span><br><span class="line">+ <span class="keyword">switch</span> (prepare_statement(input_buffer, &statement)) {</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_SUCCESS):</span><br><span class="line">+ <span class="keyword">break</span>;</span><br><span class="line">+ <span class="keyword">case</span> (PREPARE_UNRECOGNIZED_STATEMENT):</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Unrecognized keyword at start of '%s'.\n"</span>,</span><br><span class="line">+ input_buffer->buffer);</span><br><span class="line">+ <span class="keyword">continue</span>;</span><br><span class="line">+ }</span><br><span class="line">+</span><br><span class="line">+ execute_statement(&statement);</span><br><span class="line">+ <span class="built_in">printf</span>(<span class="string">"Executed.\n"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="default">
<link itemprop="mainEntityOfPage" href="http://yoursite.com/2021/03/27/055-simple-database-1/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/pig.jpeg">
<meta itemprop="name" content="Win-Man">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Win-Man's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2021/03/27/055-simple-database-1/" class="post-title-link" itemprop="url">Let's Build a Simple Database - Introduction and Setting up the REPL</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2021-03-27 21:12:13" itemprop="dateCreated datePublished" datetime="2021-03-27T21:12:13+00:00">2021-03-27</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2022-02-14 15:12:10" itemprop="dateModified" datetime="2022-02-14T15:12:10+00:00">2022-02-14</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/翻译/" itemprop="url" rel="index"><span itemprop="name">翻译</span></a>
</span>