aboutsummaryrefslogtreecommitdiff
path: root/src/content_mapblock.cpp
blob: b84b69629d99082ad6cc490d3da65449e20be7a7 (plain)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "content_mapblock.h"
#include "util/numeric.h"
#include "util/directiontables.h"
#include "main.h" // For g_settings
#include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
#include "settings.h"
#include "nodedef.h"
#include "tile.h"
#include "mesh.h"
#include <IMeshManipulator.h>
#include "gamedef.h"
#include "log.h"


// Create a cuboid.
//  collector - the MeshCollector for the resulting polygons
//  box       - the position and size of the box
//  tiles     - the tiles (materials) to use (for all 6 faces)
//  tilecount - number of entries in tiles, 1<=tilecount<=6
//  c         - vertex colour - used for all
//  txc       - texture coordinates - this is a list of texture coordinates
//              for the opposite corners of each face - therefore, there
//              should be (2+2)*6=24 values in the list. Alternatively, pass
//              NULL to use the entire texture for each face. The order of
//              the faces in the list is up-down-right-left-back-front
//              (compatible with ContentFeatures). If you specified 0,0,1,1
//              for each face, that would be the same as passing NULL.
void makeCuboid(MeshCollector *collector, const aabb3f &box,
	TileSpec *tiles, int tilecount,
	video::SColor &c, const f32* txc)
{
	assert(tilecount >= 1 && tilecount <= 6);

	v3f min = box.MinEdge;
	v3f max = box.MaxEdge;
 
 
 
	if(txc == NULL)
	{
		static const f32 txc_default[24] = {
			0,0,1,1,
			0,0,1,1,
			0,0,1,1,
			0,0,1,1,
			0,0,1,1,
			0,0,1,1
		};
		txc = txc_default;
	}

	video::S3DVertex vertices[24] =
	{
		// up
		video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
		video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
		video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
		video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
		// down
		video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
		video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
		video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
		video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
		// right
		video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
		video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
		video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
		video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
		// left
		video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
		video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
		video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
		video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
		// back
		video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
		video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
		video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
		video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
		// front
		video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
		video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
		video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
		video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
	};

	for(int i = 0; i < 6; i++)
				{
				switch (tiles[MYMIN(i, tilecount-1)].rotation)
				{
				case 0:
					break;
				case 1: //R90
					for (int x = 0; x < 4; x++)
						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
					break;
				case 2: //R180
					for (int x = 0; x < 4; x++)
						vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
					break;
				case 3: //R270
					for (int x = 0; x < 4; x++)
						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
					break;
				case 4: //FXR90
					for (int x = 0; x < 4; x++){
						vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
					}
					break;
				case 5: //FXR270
					for (int x = 0; x < 4; x++){
						vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
					}
					break;
				case 6: //FYR90
					for (int x = 0; x < 4; x++){
						vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
					}
					break;
				case 7: //FYR270
					for (int x = 0; x < 4; x++){
						vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
					}
					break;
				case 8: //FX
					for (int x = 0; x < 4; x++){
						vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
					}
					break;
				case 9: //FY
					for (int x = 0; x < 4; x++){
						vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
					}
					break;
				default:
					break;
				}
			}
	u16 indices[] = {0,1,2,2,3,0};
	// Add to mesh collector
	for(s32 j=0; j<24; j+=4)
	{
		int tileindex = MYMIN(j/4, tilecount-1);
		collector->append(tiles[tileindex],
				vertices+j, 4, indices, 6);
	}
}

void mapblock_mesh_generate_special(MeshMakeData *data,
		MeshCollector &collector)
{
	INodeDefManager *nodedef = data->m_gamedef->ndef();
	ITextureSource *tsrc = data->m_gamedef->tsrc();
	scene::ISceneManager* smgr = data->m_gamedef->getSceneManager();
	scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();

	// 0ms
	//TimeTaker timer("mapblock_mesh_generate_special()");

	/*
		Some settings
	*/
	bool enable_mesh_cache	= g_settings->getBool("enable_mesh_cache");
	bool new_style_water = g_settings->getBool("new_style_water");

	float node_liquid_level = 1.0;
	if (new_style_water)
		node_liquid_level = 0.85;

	v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;

	// Create selection mesh
	v3s16 p = data->m_highlighted_pos_relative;
	if (data->m_show_hud &&
			(p.X >= 0) && (p.X < MAP_BLOCKSIZE) &&
			(p.Y >= 0) && (p.Y < MAP_BLOCKSIZE) &&
			(p.Z >= 0) && (p.Z < MAP_BLOCKSIZE)) {

		MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
		if(n.getContent() != CONTENT_AIR) {
			// Get selection mesh light level
			static const v3s16 dirs[7] = {
					v3s16( 0, 0, 0),
					v3s16( 0, 1, 0),
					v3s16( 0,-1, 0),
					v3s16( 1, 0, 0),
					v3s16(-1, 0, 0),
					v3s16( 0, 0, 1),
					v3s16( 0, 0,-1)
			};

			u16 l = 0;
			u16 l1 = 0;
			for (u8 i = 0; i < 7; i++) {
				MapNode n1 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dirs[i]);	
				l1 = getInteriorLight(n1, -4, nodedef);
				if (l1 > l) 
					l = l1;
			}
			video::SColor c = MapBlock_LightColor(255, l, 0);
			data->m_highlight_mesh_color = c;
			std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
			TileSpec h_tile;			
			h_tile.material_flags |= MATERIAL_FLAG_HIGHLIGHTED;
			h_tile.texture = tsrc->getTexture("halo.png",&h_tile.texture_id);
			v3f pos = intToFloat(p, BS);
			f32 d = 0.05 * BS;
			for(std::vector<aabb3f>::iterator
					i = boxes.begin();
					i != boxes.end(); i++)
			{
				aabb3f box = *i;
				box.MinEdge += v3f(-d, -d, -d) + pos;
				box.MaxEdge += v3f(d, d, d) + pos;
				makeCuboid(&collector, box, &h_tile, 1, c, NULL);
			}
		}
	}

	for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
	for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
	for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
	{
		v3s16 p(x,y,z);

		MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
		const ContentFeatures &f = nodedef->get(n);

		// Only solidness=0 stuff is drawn here
		if(f.solidness != 0)
			continue;

		switch(f.drawtype){
		default:
			infostream<<"Got "<<f.drawtype<<std::endl;
			assert(0);
			break;
		case NDT_AIRLIKE:
			break;
		case NDT_LIQUID:
		{
			/*
				Add water sources to mesh if using new style
			*/
			TileSpec tile_liquid = f.special_tiles[0];
			TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);

			bool top_is_same_liquid = false;
			MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
			content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
			content_t c_source = nodedef->getId(f.liquid_alternative_source);
			if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
				top_is_same_liquid = true;

			u16 l = getInteriorLight(n, 0, nodedef);
			video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);

			/*
				Generate sides
			 */
			v3s16 side_dirs[4] = {
				v3s16(1,0,0),
				v3s16(-1,0,0),
				v3s16(0,0,1),
				v3s16(0,0,-1),
			};
			for(u32 i=0; i<4; i++)
			{
				v3s16 dir = side_dirs[i];

				MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
				content_t neighbor_content = neighbor.getContent();
				const ContentFeatures &n_feat = nodedef->get(neighbor_content);
				MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
				content_t n_top_c = n_top.getContent();

				if(neighbor_content == CONTENT_IGNORE)
					continue;

				/*
					If our topside is liquid and neighbor's topside
					is liquid, don't draw side face
				*/
				if(top_is_same_liquid && (n_top_c == c_flowing ||
						n_top_c == c_source || n_top_c == CONTENT_IGNORE))
					continue;

				// Don't draw face if neighbor is blocking the view
				if(n_feat.solidness == 2)
					continue;

				bool neighbor_is_same_liquid = (neighbor_content == c_source
						|| neighbor_content == c_flowing);

				// Don't draw any faces if neighbor same is liquid and top is
				// same liquid
				if(neighbor_is_same_liquid && !top_is_same_liquid)
					continue;

				// Use backface culled material if neighbor doesn't have a
				// solidness of 0
				const TileSpec *current_tile = &tile_liquid;
				if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
					current_tile = &tile_liquid_bfculled;

				video::S3DVertex vertices[4] =
				{
					video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1),
					video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1),
					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
				};

				/*
					If our topside is liquid, set upper border of face
					at upper border of node
				*/
				if(top_is_same_liquid)
				{
					vertices[2].Pos.Y = 0.5*BS;
					vertices[3].Pos.Y = 0.5*BS;
				}
				/*
					Otherwise upper position of face is liquid level
				*/
				else
				{
					vertices[2].Pos.Y = (node_liquid_level-0.5)*BS;
					vertices[3].Pos.Y = (node_liquid_level-0.5)*BS;
				}
				/*
					If neighbor is liquid, lower border of face is liquid level
				*/
				if(neighbor_is_same_liquid)
				{
					vertices[0].Pos.Y = (node_liquid_level-0.5)*BS;
					vertices[1].Pos.Y = (node_liquid_level-0.5)*BS;
				}
				/*
					If neighbor is not liquid, lower border of face is
					lower border of node
				*/
				else
				{
					vertices[0].Pos.Y = -0.5*BS;
					vertices[1].Pos.Y = -0.5*BS;
				}

				for(s32 j=0; j<4; j++)
				{
					if(dir == v3s16(0,0,1))
						vertices[j].Pos.rotateXZBy(0);
					if(dir == v3s16(0,0,-1))
						vertices[j].Pos.rotateXZBy(180);
					if(dir == v3s16(-1,0,0))
						vertices[j].Pos.rotateXZBy(90);
					if(dir == v3s16(1,0,-0))
						vertices[j].Pos.rotateXZBy(-90);

					// Do this to not cause glitches when two liquids are
					// side-by-side
					/*if(neighbor_is_same_liquid == false){
						vertices[j].Pos.X *= 0.98;
						vertices[j].Pos.Z *= 0.98;
					}*/

					vertices[j].Pos += intToFloat(p, BS);
				}

				u16 indices[] = {0,1,2,2,3,0};
				// Add to mesh collector
				collector.append(*current_tile, vertices, 4, indices, 6);
			}

			/*
				Generate top
			 */
			if(top_is_same_liquid)
				continue;
			
			video::S3DVertex vertices[4] =
			{
				video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
				video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
				video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
				video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
			};

			v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS);
			for(s32 i=0; i<4; i++)
			{
				vertices[i].Pos += offset;
			}

			u16 indices[] = {0,1,2,2,3,0};
			// Add to mesh collector
			collector.append(tile_liquid, vertices, 4, indices, 6);
		break;}
		case NDT_FLOWINGLIQUID:
		{
			/*
				Add flowing liquid to mesh
			*/
			TileSpec tile_liquid = f.special_tiles[0];
			TileSpec tile_liquid_bfculled = f.special_tiles[1];

			bool top_is_same_liquid = false;
			MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
			content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
			content_t c_source = nodedef->getId(f.liquid_alternative_source);
			if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
				top_is_same_liquid = true;
			
			u16 l = 0;
			// If this liquid emits light and doesn't contain light, draw
			// it at what it emits, for an increased effect
			u8 light_source = nodedef->get(n).light_source;
			if(light_source != 0){
				l = decode_light(light_source);
				l = l | (l<<8);
			}
			// Use the light of the node on top if possible
			else if(nodedef->get(ntop).param_type == CPT_LIGHT)
				l = getInteriorLight(ntop, 0, nodedef);
			// Otherwise use the light of this node (the liquid)
			else
				l = getInteriorLight(n, 0, nodedef);
			video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
			
			u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);

			// Neighbor liquid levels (key = relative position)
			// Includes current node
			std::map<v3s16, f32> neighbor_levels;
			std::map<v3s16, content_t> neighbor_contents;
			std::map<v3s16, u8> neighbor_flags;
			const u8 neighborflag_top_is_same_liquid = 0x01;
			v3s16 neighbor_dirs[9] = {
				v3s16(0,0,0),
				v3s16(0,0,1),
				v3s16(0,0,-1),
				v3s16(1,0,0),
				v3s16(-1,0,0),
				v3s16(1,0,1),
				v3s16(-1,0,-1),
				v3s16(1,0,-1),
				v3s16(-1,0,1),
			};
			for(u32 i=0; i<9; i++)
			{
				content_t content = CONTENT_AIR;
				float level = -0.5 * BS;
				u8 flags = 0;
				// Check neighbor
				v3s16 p2 = p + neighbor_dirs[i];
				MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
				if(n2.getContent() != CONTENT_IGNORE)
				{
					content = n2.getContent();

					if(n2.getContent() == c_source)
						level = (-0.5+node_liquid_level) * BS;
					else if(n2.getContent() == c_flowing){
						u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK);
						if (liquid_level <= LIQUID_LEVEL_MAX+1-range)
							liquid_level = 0;
						else
							liquid_level -= (LIQUID_LEVEL_MAX+1-range);
						level = (-0.5 + ((float)liquid_level+ 0.5) / (float)range * node_liquid_level) * BS;
					}

					// Check node above neighbor.
					// NOTE: This doesn't get executed if neighbor
					//       doesn't exist
					p2.Y += 1;
					n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
					if(n2.getContent() == c_source ||
							n2.getContent() == c_flowing)
						flags |= neighborflag_top_is_same_liquid;
				}
				
				neighbor_levels[neighbor_dirs[i]] = level;
				neighbor_contents[neighbor_dirs[i]] = content;
				neighbor_flags[neighbor_dirs[i]] = flags;
			}

			// Corner heights (average between four liquids)
			f32 corner_levels[4];
			
			v3s16 halfdirs[4] = {
				v3s16(0,0,0),
				v3s16(1,0,0),
				v3s16(1,0,1),
				v3s16(0,0,1),
			};
			for(u32 i=0; i<4; i++)
			{
				v3s16 cornerdir = halfdirs[i];
				float cornerlevel = 0;
				u32 valid_count = 0;
				u32 air_count = 0;
				for(u32 j=0; j<4; j++)
				{
					v3s16 neighbordir = cornerdir - halfdirs[j];
					content_t content = neighbor_contents[neighbordir];
					// If top is liquid, draw starting from top of node
					if(neighbor_flags[neighbordir] &
							neighborflag_top_is_same_liquid)
					{
						cornerlevel = 0.5*BS;
						valid_count = 1;
						break;
					}
					// Source is always the same height
					else if(content == c_source)
					{
						cornerlevel = (-0.5+node_liquid_level)*BS;
						valid_count = 1;
						break;
					}
					// Flowing liquid has level information
					else if(content == c_flowing)
					{
						cornerlevel += neighbor_levels[neighbordir];
						valid_count++;
					}
					else if(content == CONTENT_AIR)
					{
						air_count++;
					}
				}
				if(air_count >= 2)
					cornerlevel = -0.5*BS+0.2;
				else if(valid_count > 0)
					cornerlevel /= valid_count;
				corner_levels[i] = cornerlevel;
			}

			/*
				Generate sides
			*/

			v3s16 side_dirs[4] = {
				v3s16(1,0,0),
				v3s16(-1,0,0),
				v3s16(0,0,1),
				v3s16(0,0,-1),
			};
			s16 side_corners[4][2] = {
				{1, 2},
				{3, 0},
				{2, 3},
				{0, 1},
			};
			for(u32 i=0; i<4; i++)
			{
				v3s16 dir = side_dirs[i];

				/*
					If our topside is liquid and neighbor's topside
					is liquid, don't draw side face
				*/
				if(top_is_same_liquid &&
						neighbor_flags[dir] & neighborflag_top_is_same_liquid)
					continue;

				content_t neighbor_content = neighbor_contents[dir];
				const ContentFeatures &n_feat = nodedef->get(neighbor_content);
				
				// Don't draw face if neighbor is blocking the view
				if(n_feat.solidness == 2)
					continue;
				
				bool neighbor_is_same_liquid = (neighbor_content == c_source
						|| neighbor_content == c_flowing);
				
				// Don't draw any faces if neighbor same is liquid and top is
				// same liquid
				if(neighbor_is_same_liquid == true
						&& top_is_same_liquid == false)
					continue;

				// Use backface culled material if neighbor doesn't have a
				// solidness of 0
				const TileSpec *current_tile = &tile_liquid;
				if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
					current_tile = &tile_liquid_bfculled;
				
				video::S3DVertex vertices[4] =
				{
					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
				};
				
				/*
					If our topside is liquid, set upper border of face
					at upper border of node
				*/
				if(top_is_same_liquid)
				{
					vertices[2].Pos.Y = 0.5*BS;
					vertices[3].Pos.Y = 0.5*BS;
				}
				/*
					Otherwise upper position of face is corner levels
				*/
				else
				{
					vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
					vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
				}
				
				/*
					If neighbor is liquid, lower border of face is corner
					liquid levels
				*/
				if(neighbor_is_same_liquid)
				{
					vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
					vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
				}
				/*
					If neighbor is not liquid, lower border of face is
					lower border of node
				*/
				else
				{
					vertices[0].Pos.Y = -0.5*BS;
					vertices[1].Pos.Y = -0.5*BS;
				}
				
				for(s32 j=0; j<4; j++)
				{
					if(dir == v3s16(0,0,1))
						vertices[j].Pos.rotateXZBy(0);
					if(dir == v3s16(0,0,-1))
						vertices[j].Pos.rotateXZBy(180);
					if(dir == v3s16(-1,0,0))
						vertices[j].Pos.rotateXZBy(90);
					if(dir == v3s16(1,0,-0))
						vertices[j].Pos.rotateXZBy(-90);
						
					// Do this to not cause glitches when two liquids are
					// side-by-side
					/*if(neighbor_is_same_liquid == false){
						vertices[j].Pos.X *= 0.98;
						vertices[j].Pos.Z *= 0.98;
					}*/

					vertices[j].Pos += intToFloat(p, BS);
				}

				u16 indices[] = {0,1,2,2,3,0};
				// Add to mesh collector
				collector.append(*current_tile, vertices, 4, indices, 6);
			}
			
			/*
				Generate top side, if appropriate
			*/
			
			if(top_is_same_liquid == false)
			{
				video::S3DVertex vertices[4] =
				{
					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
					video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
					video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
				};
				
				// To get backface culling right, the vertices need to go
				// clockwise around the front of the face. And we happened to
				// calculate corner levels in exact reverse order.
				s32 corner_resolve[4] = {3,2,1,0};

				for(s32 i=0; i<4; i++)
				{
					//vertices[i].Pos.Y += liquid_level;
					//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
					s32 j = corner_resolve[i];
					vertices[i].Pos.Y += corner_levels[j];
					vertices[i].Pos += intToFloat(p, BS);
				}
				
				// Default downwards-flowing texture animation goes from 
				// -Z towards +Z, thus the direction is +Z.
				// Rotate texture to make animation go in flow direction
				// Positive if liquid moves towards +Z
				f32 dz = (corner_levels[side_corners[3][0]] +
						corner_levels[side_corners[3][1]]) -
						(corner_levels[side_corners[2][0]] +
						corner_levels[side_corners[2][1]]);
				// Positive if liquid moves towards +X
				f32 dx = (corner_levels[side_corners[1][0]] +
						corner_levels[side_corners[1][1]]) -
						(corner_levels[side_corners[0][0]] +
						corner_levels[side_corners[0][1]]);
				f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ;
				v2f tcoord_center(0.5, 0.5);
				v2f tcoord_translate(
						blockpos_nodes.Z + z,
						blockpos_nodes.X + x);
				tcoord_translate.rotateBy(tcoord_angle);
				tcoord_translate.X -= floor(tcoord_translate.X);
				tcoord_translate.Y -= floor(tcoord_translate.Y);

				for(s32 i=0; i<4; i++)
				{
					vertices[i].TCoords.rotateBy(
							tcoord_angle,
							tcoord_center);
					vertices[i].TCoords += tcoord_translate;
				}

				v2f t = vertices[0].TCoords;
				vertices[0].TCoords = vertices[2].TCoords;
				vertices[2].TCoords = t;

				u16 indices[] = {0,1,2,2,3,0};
				// Add to mesh collector
				collector.append(tile_liquid, vertices, 4, indices, 6);
			}
		break;}
		case NDT_GLASSLIKE:
		{
			TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);

			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			for(u32 j=0; j<6; j++)
			{
				// Check this neighbor
				v3s16 n2p = blockpos_nodes + p + g_6dirs[j];
				MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
				// Don't make face if neighbor is of same type
				if(n2.getContent() == n.getContent())
					continue;

				// The face at Z+
				video::S3DVertex vertices[4] = {
					video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
					video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
					video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 0,0),
					video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
				};
				
				// Rotations in the g_6dirs format
				if(j == 0) // Z+
					for(u16 i=0; i<4; i++)
						vertices[i].Pos.rotateXZBy(0);
				else if(j == 1) // Y+
					for(u16 i=0; i<4; i++)
						vertices[i].Pos.rotateYZBy(-90);
				else if(j == 2) // X+
					for(u16 i=0; i<4; i++)
						vertices[i].Pos.rotateXZBy(-90);
				else if(j == 3) // Z-
					for(u16 i=0; i<4; i++)
						vertices[i].Pos.rotateXZBy(180);
				else if(j == 4) // Y-
					for(u16 i=0; i<4; i++)
						vertices[i].Pos.rotateYZBy(90);
				else if(j == 5) // X-
					for(u16 i=0; i<4; i++)
						vertices[i].Pos.rotateXZBy(90);

				for(u16 i=0; i<4; i++){
					vertices[i].Pos += intToFloat(p, BS);
				}

				u16 indices[] = {0,1,2,2,3,0};
				// Add to mesh collector
				collector.append(tile, vertices, 4, indices, 6);
			}
		break;}
		case NDT_GLASSLIKE_FRAMED_OPTIONAL:
			// This is always pre-converted to something else
			assert(0);
			break;
		case NDT_GLASSLIKE_FRAMED:
		{
			static const v3s16 dirs[6] = {
				v3s16( 0, 1, 0),
				v3s16( 0,-1, 0),
				v3s16( 1, 0, 0),
				v3s16(-1, 0, 0),
				v3s16( 0, 0, 1),
				v3s16( 0, 0,-1)
			};

			u8 i;
			TileSpec tiles[6];
			for (i = 0; i < 6; i++)
				tiles[i] = getNodeTile(n, p, dirs[i], data);
			
			TileSpec glass_tiles[6];
			if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
				glass_tiles[0] = tiles[2];
				glass_tiles[1] = tiles[3];
				glass_tiles[2] = tiles[1];
				glass_tiles[3] = tiles[1];
				glass_tiles[4] = tiles[1];
				glass_tiles[5] = tiles[1];
			} else {
				for (i = 0; i < 6; i++)
					glass_tiles[i] = tiles[1];	
			}
			
			u8 param2 = n.getParam2();
			bool H_merge = ! bool(param2 & 128);
			bool V_merge = ! bool(param2 & 64);
			param2  = param2 & 63;
			
			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
			v3f pos = intToFloat(p, BS);
			static const float a = BS / 2;
			static const float g = a - 0.003;
			static const float b = .876 * ( BS / 2 );
			
			static const aabb3f frame_edges[12] = {
				aabb3f( b, b,-a, a, a, a), // y+
				aabb3f(-a, b,-a,-b, a, a), // y+
				aabb3f( b,-a,-a, a,-b, a), // y-
				aabb3f(-a,-a,-a,-b,-b, a), // y-
				aabb3f( b,-a, b, a, a, a), // x+
				aabb3f( b,-a,-a, a, a,-b), // x+
				aabb3f(-a,-a, b,-b, a, a), // x-
				aabb3f(-a,-a,-a,-b, a,-b), // x-
				aabb3f(-a, b, b, a, a, a), // z+
				aabb3f(-a,-a, b, a,-b, a), // z+
				aabb3f(-a,-a,-a, a,-b,-b), // z-
				aabb3f(-a, b,-a, a, a,-b)  // z-
			};
			static const aabb3f glass_faces[6] = {
				aabb3f(-g, g,-g, g, g, g), // y+
				aabb3f(-g,-g,-g, g,-g, g), // y-
				aabb3f( g,-g,-g, g, g, g), // x+
				aabb3f(-g,-g,-g,-g, g, g), // x-
				aabb3f(-g,-g, g, g, g, g), // z+
				aabb3f(-g,-g,-g, g, g,-g)  // z-
			};
			
			// table of node visible faces, 0 = invisible
			int visible_faces[6] = {0,0,0,0,0,0};
			
			// table of neighbours, 1 = same type, checked with g_26dirs
			int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
			
			// g_26dirs to check when only horizontal merge is allowed
			int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
			
			content_t current = n.getContent();
			content_t n2c;
			MapNode n2;
			v3s16 n2p;

			// neighbours checks for frames visibility

			if (!H_merge && V_merge) {
				n2p = blockpos_nodes + p + g_26dirs[1];
				n2 = data->m_vmanip.getNodeNoEx(n2p);
				n2c = n2.getContent();
				if (n2c == current || n2c == CONTENT_IGNORE)
					nb[1] = 1;
				n2p = blockpos_nodes + p + g_26dirs[4];
				n2 = data->m_vmanip.getNodeNoEx(n2p);
				n2c = n2.getContent();
				if (n2c == current || n2c == CONTENT_IGNORE)
					nb[4] = 1;	
			} else if (H_merge && !V_merge) {
				for(i = 0; i < 8; i++) {
					n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
					n2 = data->m_vmanip.getNodeNoEx(n2p);
					n2c = n2.getContent();
					if (n2c == current || n2c == CONTENT_IGNORE)
						nb[nb_H_dirs[i]] = 1;		
				}
			} else if (H_merge && V_merge) {
				for(i = 0; i < 18; i++)	{
					n2p = blockpos_nodes + p + g_26dirs[i];
					n2 = data->m_vmanip.getNodeNoEx(n2p);
					n2c = n2.getContent();
					if (n2c == current || n2c == CONTENT_IGNORE)
						nb[i] = 1;
				}
			}

			// faces visibility checks

			if (!V_merge) {
				visible_faces[0] = 1;
				visible_faces[1] = 1;
			} else {
				for(i = 0; i < 2; i++) {
					n2p = blockpos_nodes + p + dirs[i];
					n2 = data->m_vmanip.getNodeNoEx(n2p);
					n2c = n2.getContent();
					if (n2c != current)
						visible_faces[i] = 1;
				}
			}
				
			if (!H_merge) {
				visible_faces[2] = 1;
				visible_faces[3] = 1;
				visible_faces[4] = 1;
				visible_faces[5] = 1;
			} else {
				for(i = 2; i < 6; i++) {
					n2p = blockpos_nodes + p + dirs[i];
					n2 = data->m_vmanip.getNodeNoEx(n2p);
					n2c = n2.getContent();
					if (n2c != current)
						visible_faces[i] = 1;
				}
			}
	
			static const u8 nb_triplet[12*3] = {
				1,2, 7,  1,5, 6,  4,2,15,  4,5,14,
				2,0,11,  2,3,13,  5,0,10,  5,3,12,
				0,1, 8,  0,4,16,  3,4,17,  3,1, 9
			};

			f32 tx1, ty1, tz1, tx2, ty2, tz2;
			aabb3f box;

			for(i = 0; i < 12; i++)
			{
				int edge_invisible;
				if (nb[nb_triplet[i*3+2]])
					edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
				else
					edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
				if (edge_invisible)
					continue;
				box = frame_edges[i];
				box.MinEdge += pos;
				box.MaxEdge += pos;
				tx1 = (box.MinEdge.X / BS) + 0.5;
				ty1 = (box.MinEdge.Y / BS) + 0.5;
				tz1 = (box.MinEdge.Z / BS) + 0.5;
				tx2 = (box.MaxEdge.X / BS) + 0.5;
				ty2 = (box.MaxEdge.Y / BS) + 0.5;
				tz2 = (box.MaxEdge.Z / BS) + 0.5;
				f32 txc1[24] = {
					tx1,   1-tz2,   tx2, 1-tz1,
					tx1,     tz1,   tx2,   tz2,
					tz1,   1-ty2,   tz2, 1-ty1,
					1-tz2, 1-ty2, 1-tz1, 1-ty1,
					1-tx2, 1-ty2, 1-tx1, 1-ty1,
					tx1,   1-ty2,   tx2, 1-ty1,
				};
				makeCuboid(&collector, box, &tiles[0], 1, c, txc1);
			}

			for(i = 0; i < 6; i++)
			{
				if (!visible_faces[i])
					continue;
				box = glass_faces[i];
				box.MinEdge += pos;
				box.MaxEdge += pos;
				tx1 = (box.MinEdge.X / BS) + 0.5;
				ty1 = (box.MinEdge.Y / BS) + 0.5;
				tz1 = (box.MinEdge.Z / BS) + 0.5;
				tx2 = (box.MaxEdge.X / BS) + 0.5;
				ty2 = (box.MaxEdge.Y / BS) + 0.5;
				tz2 = (box.MaxEdge.Z / BS) + 0.5;
				f32 txc2[24] = {
					tx1,   1-tz2,   tx2, 1-tz1,
					tx1,     tz1,   tx2,   tz2,
					tz1,   1-ty2,   tz2, 1-ty1,
					1-tz2, 1-ty2, 1-tz1, 1-ty1,
					1-tx2, 1-ty2, 1-tx1, 1-ty1,
					tx1,   1-ty2,   tx2, 1-ty1,
				};
				makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2);
			}

			if (param2 > 0 && f.special_tiles[0].texture) {
				// Interior volume level is in range 0 .. 63,
				// convert it to -0.5 .. 0.5
				float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
				TileSpec interior_tiles[6];
				for (i = 0; i < 6; i++)
					interior_tiles[i] = f.special_tiles[0];
				float offset = 0.003;
				box = aabb3f(visible_faces[3] ? -b : -a + offset,
							 visible_faces[1] ? -b : -a + offset,
							 visible_faces[5] ? -b : -a + offset,
							 visible_faces[2] ? b : a - offset,
							 visible_faces[0] ? b * vlev : a * vlev - offset,
							 visible_faces[4] ? b : a - offset);
				box.MinEdge += pos;
				box.MaxEdge += pos;
				tx1 = (box.MinEdge.X / BS) + 0.5;
				ty1 = (box.MinEdge.Y / BS) + 0.5;
				tz1 = (box.MinEdge.Z / BS) + 0.5;
				tx2 = (box.MaxEdge.X / BS) + 0.5;
				ty2 = (box.MaxEdge.Y / BS) + 0.5;
				tz2 = (box.MaxEdge.Z / BS) + 0.5;
				f32 txc3[24] = {
					tx1,   1-tz2,   tx2, 1-tz1,
					tx1,     tz1,   tx2,   tz2,
					tz1,   1-ty2,   tz2, 1-ty1,
					1-tz2, 1-ty2, 1-tz1, 1-ty1,
					1-tx2, 1-ty2, 1-tx1, 1-ty1,
					tx1,   1-ty2,   tx2, 1-ty1,
				};
				makeCuboid(&collector, box, interior_tiles, 6, c,  txc3);
			}
		break;}
		case NDT_ALLFACES:
		{
			TileSpec tile_leaves = getNodeTile(n, p,
					v3s16(0,0,0), data);

			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			v3f pos = intToFloat(p, BS);
			aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
			box.MinEdge += pos;
			box.MaxEdge += pos;
			makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
		break;}
		case NDT_ALLFACES_OPTIONAL:
			// This is always pre-converted to something else
			assert(0);
			break;
		case NDT_TORCHLIKE:
		{
			v3s16 dir = n.getWallMountedDir(nodedef);
			
			u8 tileindex = 0;
			if(dir == v3s16(0,-1,0)){
				tileindex = 0; // floor
			} else if(dir == v3s16(0,1,0)){
				tileindex = 1; // ceiling
			// For backwards compatibility
			} else if(dir == v3s16(0,0,0)){
				tileindex = 0; // floor
			} else {
				tileindex = 2; // side
			}

			TileSpec tile = getNodeTileN(n, p, tileindex, data);
			tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			float s = BS/2*f.visual_scale;
			// Wall at X+ of node
			video::S3DVertex vertices[4] =
			{
				video::S3DVertex(-s,-s,0, 0,0,0, c, 0,1),
				video::S3DVertex( s,-s,0, 0,0,0, c, 1,1),
				video::S3DVertex( s, s,0, 0,0,0, c, 1,0),
				video::S3DVertex(-s, s,0, 0,0,0, c, 0,0),
			};

			for(s32 i=0; i<4; i++)
			{
				if(dir == v3s16(1,0,0))
					vertices[i].Pos.rotateXZBy(0);
				if(dir == v3s16(-1,0,0))
					vertices[i].Pos.rotateXZBy(180);
				if(dir == v3s16(0,0,1))
					vertices[i].Pos.rotateXZBy(90);
				if(dir == v3s16(0,0,-1))
					vertices[i].Pos.rotateXZBy(-90);
				if(dir == v3s16(0,-1,0))
					vertices[i].Pos.rotateXZBy(45);
				if(dir == v3s16(0,1,0))
					vertices[i].Pos.rotateXZBy(-45);

				vertices[i].Pos += intToFloat(p, BS);
			}

			u16 indices[] = {0,1,2,2,3,0};
			// Add to mesh collector
			collector.append(tile, vertices, 4, indices, 6);
		break;}
		case NDT_SIGNLIKE:
		{
			TileSpec tile = getNodeTileN(n, p, 0, data);
			tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

			u16 l = getInteriorLight(n, 0, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
				
			float d = (float)BS/16;
			float s = BS/2*f.visual_scale;
			// Wall at X+ of node
			video::S3DVertex vertices[4] =
			{
				video::S3DVertex(BS/2-d,  s,  s, 0,0,0, c, 0,0),
				video::S3DVertex(BS/2-d,  s, -s, 0,0,0, c, 1,0),
				video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1),
				video::S3DVertex(BS/2-d, -s,  s, 0,0,0, c, 0,1),
			};

			v3s16 dir = n.getWallMountedDir(nodedef);

			for(s32 i=0; i<4; i++)
			{
				if(dir == v3s16(1,0,0))
					vertices[i].Pos.rotateXZBy(0);
				if(dir == v3s16(-1,0,0))
					vertices[i].Pos.rotateXZBy(180);
				if(dir == v3s16(0,0,1))
					vertices[i].Pos.rotateXZBy(90);
				if(dir == v3s16(0,0,-1))
					vertices[i].Pos.rotateXZBy(-90);
				if(dir == v3s16(0,-1,0))
					vertices[i].Pos.rotateXYBy(-90);
				if(dir == v3s16(0,1,0))
					vertices[i].Pos.rotateXYBy(90);

				vertices[i].Pos += intToFloat(p, BS);
			}

			u16 indices[] = {0,1,2,2,3,0};
			// Add to mesh collector
			collector.append(tile, vertices, 4, indices, 6);
		break;}
		case NDT_PLANTLIKE:
		{
			TileSpec tile = getNodeTileN(n, p, 0, data);
			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
			
			float s = BS / 2 * f.visual_scale;

			for (int j = 0; j < 2; j++)
			{
				video::S3DVertex vertices[4] =
				{
					video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1),
					video::S3DVertex( s,-BS/2, 0, 0,0,0, c, 1,1),
					video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
					video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
				};

				if(j == 0)
				{
					for(u16 i = 0; i < 4; i++)
						vertices[i].Pos.rotateXZBy(46 + n.param2 * 2);
				}
				else if(j == 1)
				{
					for(u16 i = 0; i < 4; i++)
						vertices[i].Pos.rotateXZBy(-44 + n.param2 * 2);
				}

				for (int i = 0; i < 4; i++)
				{
					vertices[i].Pos *= f.visual_scale;
					vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1);
					vertices[i].Pos += intToFloat(p, BS);
				}

				u16 indices[] = {0, 1, 2, 2, 3, 0};
				// Add to mesh collector
				collector.append(tile, vertices, 4, indices, 6);
			}
		break;}
		case NDT_FIRELIKE:
		{
			TileSpec tile = getNodeTileN(n, p, 0, data);
			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			float s = BS/2*f.visual_scale;

			content_t current = n.getContent();
			content_t n2c;
			MapNode n2;
			v3s16 n2p;

			static const v3s16 dirs[6] = {
				v3s16( 0, 1, 0),
				v3s16( 0,-1, 0),
				v3s16( 1, 0, 0),
				v3s16(-1, 0, 0),
				v3s16( 0, 0, 1),
				v3s16( 0, 0,-1)
			};

			int doDraw[6] = {0,0,0,0,0,0};

			bool drawAllFaces = true;

			bool drawBottomFacesOnly = false; // Currently unused

			// Check for adjacent nodes
			for(int i = 0; i < 6; i++)
			{
				n2p = blockpos_nodes + p + dirs[i];
				n2 = data->m_vmanip.getNodeNoEx(n2p);
				n2c = n2.getContent();
				if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
					doDraw[i] = 1;
					if(drawAllFaces)
						drawAllFaces = false;

				}
			}

			for(int j = 0; j < 6; j++)
			{
				int vOffset = 0; // Vertical offset of faces after rotation
				int hOffset = 4; // Horizontal offset of faces to reach the edge

				video::S3DVertex vertices[4] =
				{
					video::S3DVertex(-s,-BS/2,      0, 0,0,0, c, 0,1),
					video::S3DVertex( s,-BS/2,      0, 0,0,0, c, 1,1),
					video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
					video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
				};

				// Calculate which faces should be drawn, (top or sides)
				if(j == 0 && (drawAllFaces || (doDraw[3] == 1 || doDraw[1] == 1)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateXZBy(90 + n.param2 * 2);
						vertices[i].Pos.rotateXYBy(-10);
						vertices[i].Pos.Y -= vOffset;
						vertices[i].Pos.X -= hOffset;
					}
				}
				else if(j == 1 && (drawAllFaces || (doDraw[5] == 1 || doDraw[1] == 1)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateXZBy(180 + n.param2 * 2);
						vertices[i].Pos.rotateYZBy(10);
						vertices[i].Pos.Y -= vOffset;
						vertices[i].Pos.Z -= hOffset;
					}
				}
				else if(j == 2 && (drawAllFaces || (doDraw[2] == 1 || doDraw[1] == 1)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateXZBy(270 + n.param2 * 2);
						vertices[i].Pos.rotateXYBy(10);
						vertices[i].Pos.Y -= vOffset;
						vertices[i].Pos.X += hOffset;
					}
				}
				else if(j == 3 && (drawAllFaces || (doDraw[4] == 1 || doDraw[1] == 1)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateYZBy(-10);
						vertices[i].Pos.Y -= vOffset;
						vertices[i].Pos.Z += hOffset;
					}
				}

				// Center cross-flames
				else if(j == 4 && (drawAllFaces || doDraw[1] == 1))
				{
					for(int i=0; i<4; i++) {
						vertices[i].Pos.rotateXZBy(45 + n.param2 * 2);
						vertices[i].Pos.Y -= vOffset;
					}
				}
				else if(j == 5 && (drawAllFaces || doDraw[1] == 1))
				{
					for(int i=0; i<4; i++) {
						vertices[i].Pos.rotateXZBy(-45 + n.param2 * 2);
						vertices[i].Pos.Y -= vOffset;
					}
				}

				// Render flames on bottom
				else if(j == 0 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateYZBy(70);
						vertices[i].Pos.rotateXZBy(90 + n.param2 * 2);
						vertices[i].Pos.Y += 4.84;
						vertices[i].Pos.X -= hOffset+0.7;
					}
				}
				else if(j == 1 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateYZBy(70);
						vertices[i].Pos.rotateXZBy(180 + n.param2 * 2);
						vertices[i].Pos.Y += 4.84;
						vertices[i].Pos.Z -= hOffset+0.7;
					}
				}
				else if(j == 2 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateYZBy(70);
						vertices[i].Pos.rotateXZBy(270 + n.param2 * 2);
						vertices[i].Pos.Y += 4.84;
						vertices[i].Pos.X += hOffset+0.7;
					}
				}
				else if(j == 3 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
				{
					for(int i = 0; i < 4; i++) {
						vertices[i].Pos.rotateYZBy(70);
						vertices[i].Pos.Y += 4.84;
						vertices[i].Pos.Z += hOffset+0.7;
					}
				}
				else {
					// Skip faces that aren't adjacent to a node
					continue;
				}

				for(int i=0; i<4; i++)
				{
					vertices[i].Pos *= f.visual_scale;
					vertices[i].Pos += intToFloat(p, BS);
				}

				u16 indices[] = {0,1,2,2,3,0};
				// Add to mesh collector
				collector.append(tile, vertices, 4, indices, 6);
			}
		break;}
		case NDT_FENCELIKE:
		{
			TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
			TileSpec tile_nocrack = tile;
			tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;

			// Put wood the right way around in the posts
			TileSpec tile_rot = tile;
			tile_rot.rotation = 1;

			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			const f32 post_rad=(f32)BS/8;
			const f32 bar_rad=(f32)BS/16;
			const f32 bar_len=(f32)(BS/2)-post_rad;

			v3f pos = intToFloat(p, BS);

			// The post - always present
			aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
			post.MinEdge += pos;
			post.MaxEdge += pos;
			f32 postuv[24]={
					6/16.,6/16.,10/16.,10/16.,
					6/16.,6/16.,10/16.,10/16.,
					0/16.,0,4/16.,1,
					4/16.,0,8/16.,1,
					8/16.,0,12/16.,1,
					12/16.,0,16/16.,1};
			makeCuboid(&collector, post, &tile_rot, 1, c, postuv);

			// Now a section of fence, +X, if there's a post there
			v3s16 p2 = p;
			p2.X++;
			MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
			const ContentFeatures *f2 = &nodedef->get(n2);
			if(f2->drawtype == NDT_FENCELIKE)
			{
				aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
						bar_len+BS/2,bar_rad+BS/4,bar_rad);
				bar.MinEdge += pos;
				bar.MaxEdge += pos;
				f32 xrailuv[24]={
					0/16.,2/16.,16/16.,4/16.,
					0/16.,4/16.,16/16.,6/16.,
					6/16.,6/16.,8/16.,8/16.,
					10/16.,10/16.,12/16.,12/16.,
					0/16.,8/16.,16/16.,10/16.,
					0/16.,14/16.,16/16.,16/16.};
				makeCuboid(&collector, bar, &tile_nocrack, 1,
						c, xrailuv);
				bar.MinEdge.Y -= BS/2;
				bar.MaxEdge.Y -= BS/2;
				makeCuboid(&collector, bar, &tile_nocrack, 1,
						c, xrailuv);
			}

			// Now a section of fence, +Z, if there's a post there
			p2 = p;
			p2.Z++;
			n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
			f2 = &nodedef->get(n2);
			if(f2->drawtype == NDT_FENCELIKE)
			{
				aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
						bar_rad,bar_rad+BS/4,bar_len+BS/2);
				bar.MinEdge += pos;
				bar.MaxEdge += pos;
				f32 zrailuv[24]={
					3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
					4/16.,1/16.,6/16.,5/16., // for wood texture instead
					0/16.,9/16.,16/16.,11/16.,
					0/16.,6/16.,16/16.,8/16.,
					6/16.,6/16.,8/16.,8/16.,
					10/16.,10/16.,12/16.,12/16.};
				makeCuboid(&collector, bar, &tile_nocrack, 1,
						c, zrailuv);
				bar.MinEdge.Y -= BS/2;
				bar.MaxEdge.Y -= BS/2;
				makeCuboid(&collector, bar, &tile_nocrack, 1,
						c, zrailuv);
			}
		break;}
		case NDT_RAILLIKE:
		{
			bool is_rail_x [] = { false, false };  /* x-1, x+1 */
			bool is_rail_z [] = { false, false };  /* z-1, z+1 */

			bool is_rail_z_minus_y [] = { false, false };  /* z-1, z+1; y-1 */
			bool is_rail_x_minus_y [] = { false, false };  /* x-1, z+1; y-1 */
			bool is_rail_z_plus_y [] = { false, false };  /* z-1, z+1; y+1 */
			bool is_rail_x_plus_y [] = { false, false };  /* x-1, x+1; y+1 */

			MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
			MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
			MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
			MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
			MapNode n_plus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y+1, z));
			MapNode n_plus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y-1, z));
			MapNode n_minus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y+1, z));
			MapNode n_minus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y-1, z));
			MapNode n_plus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z+1));
			MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1));
			MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1));
			MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1));

			content_t thiscontent = n.getContent();
			std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
			bool self_connect_to_raillike = ((ItemGroupList) nodedef->get(n).groups)[groupname] != 0;

			if ((nodedef->get(n_minus_x).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_minus_x).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_minus_x.getContent() == thiscontent)
				is_rail_x[0] = true;

			if ((nodedef->get(n_minus_x_minus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_minus_x_minus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_minus_x_minus_y.getContent() == thiscontent)
				is_rail_x_minus_y[0] = true;

			if ((nodedef->get(n_minus_x_plus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_minus_x_plus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_minus_x_plus_y.getContent() == thiscontent)
				is_rail_x_plus_y[0] = true;

			if ((nodedef->get(n_plus_x).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_plus_x).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_plus_x.getContent() == thiscontent)
				is_rail_x[1] = true;

			if ((nodedef->get(n_plus_x_minus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_plus_x_minus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_plus_x_minus_y.getContent() == thiscontent)
				is_rail_x_minus_y[1] = true;

			if ((nodedef->get(n_plus_x_plus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_plus_x_plus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_plus_x_plus_y.getContent() == thiscontent)
				is_rail_x_plus_y[1] = true;

			if ((nodedef->get(n_minus_z).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_minus_z).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_minus_z.getContent() == thiscontent)
				is_rail_z[0] = true;

			if ((nodedef->get(n_minus_z_minus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_minus_z_minus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_minus_z_minus_y.getContent() == thiscontent)
				is_rail_z_minus_y[0] = true;

			if ((nodedef->get(n_minus_z_plus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_minus_z_plus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_minus_z_plus_y.getContent() == thiscontent)
				is_rail_z_plus_y[0] = true;

			if ((nodedef->get(n_plus_z).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_plus_z).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_plus_z.getContent() == thiscontent)
				is_rail_z[1] = true;

			if ((nodedef->get(n_plus_z_minus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_plus_z_minus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_plus_z_minus_y.getContent() == thiscontent)
				is_rail_z_minus_y[1] = true;

			if ((nodedef->get(n_plus_z_plus_y).drawtype == NDT_RAILLIKE
					&& ((ItemGroupList) nodedef->get(n_plus_z_plus_y).groups)[groupname] != 0
					&& self_connect_to_raillike)
					|| n_plus_z_plus_y.getContent() == thiscontent)
				is_rail_z_plus_y[1] = true;

			bool is_rail_x_all[] = {false, false};
			bool is_rail_z_all[] = {false, false};
			is_rail_x_all[0]=is_rail_x[0] || is_rail_x_minus_y[0] || is_rail_x_plus_y[0];
			is_rail_x_all[1]=is_rail_x[1] || is_rail_x_minus_y[1] || is_rail_x_plus_y[1];
			is_rail_z_all[0]=is_rail_z[0] || is_rail_z_minus_y[0] || is_rail_z_plus_y[0];
			is_rail_z_all[1]=is_rail_z[1] || is_rail_z_minus_y[1] || is_rail_z_plus_y[1];

			// reasonable default, flat straight unrotated rail
			bool is_straight = true;
			int adjacencies = 0;
			int angle = 0;
			u8 tileindex = 0;

			// check for sloped rail
			if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
			{
				adjacencies = 5; //5 means sloped
				is_straight = true; // sloped is always straight
			}
			else
			{
				// is really straight, rails on both sides
				is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
				adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
			}

			switch (adjacencies) {
			case 1:
				if(is_rail_x_all[0] || is_rail_x_all[1])
					angle = 90;
				break;
			case 2:
				if(!is_straight)
					tileindex = 1; // curved
				if(is_rail_x_all[0] && is_rail_x_all[1])
					angle = 90;
				if(is_rail_z_all[0] && is_rail_z_all[1]){
					if (is_rail_z_plus_y[0])
						angle = 180;
				}
				else if(is_rail_x_all[0] && is_rail_z_all[0])
					angle = 270;
				else if(is_rail_x_all[0] && is_rail_z_all[1])
					angle = 180;
				else if(is_rail_x_all[1] && is_rail_z_all[1])
					angle = 90;
				break;
			case 3:
				// here is where the potential to 'switch' a junction is, but not implemented at present
				tileindex = 2; // t-junction
				if(!is_rail_x_all[1])
					angle=180;
				if(!is_rail_z_all[0])
					angle=90;
				if(!is_rail_z_all[1])
					angle=270;
				break;
			case 4:
				tileindex = 3; // crossing
				break;
			case 5: //sloped
				if(is_rail_z_plus_y[0])
					angle = 180;
				if(is_rail_x_plus_y[0])
					angle = 90;
				if(is_rail_x_plus_y[1])
					angle = -90;
				break;
			default:
				break;
			}

			TileSpec tile = getNodeTileN(n, p, tileindex, data);
			tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;

			u16 l = getInteriorLight(n, 0, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			float d = (float)BS/64;
			float s = BS/2;

			short g = -1;
			if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
				g = 1; //Object is at a slope

			video::S3DVertex vertices[4] =
			{
					video::S3DVertex(-s,  -s+d,-s,  0,0,0,  c,0,1),
					video::S3DVertex( s,  -s+d,-s,  0,0,0,  c,1,1),
					video::S3DVertex( s, g*s+d, s,  0,0,0,  c,1,0),
					video::S3DVertex(-s, g*s+d, s,  0,0,0,  c,0,0),
			};

			for(s32 i=0; i<4; i++)
			{
				if(angle != 0)
					vertices[i].Pos.rotateXZBy(angle);
				vertices[i].Pos += intToFloat(p, BS);
			}

			u16 indices[] = {0,1,2,2,3,0};
			collector.append(tile, vertices, 4, indices, 6);
		break;}
		case NDT_NODEBOX:
		{
			static const v3s16 tile_dirs[6] = {
				v3s16(0, 1, 0),
				v3s16(0, -1, 0),
				v3s16(1, 0, 0),
				v3s16(-1, 0, 0),
				v3s16(0, 0, 1),
				v3s16(0, 0, -1)
			};
			TileSpec tiles[6];
			
			u16 l = getInteriorLight(n, 1, nodedef);
			video::SColor c = MapBlock_LightColor(255, l, f.light_source);

			v3f pos = intToFloat(p, BS);

			std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
			for(std::vector<aabb3f>::iterator
					i = boxes.begin();
					i != boxes.end(); i++)
			{
			for(int j = 0; j < 6; j++)
				{
				// Handles facedir rotation for textures
				tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
				}
				aabb3f box = *i;
				box.MinEdge += pos;
				box.MaxEdge += pos;
				
				f32 temp;
				if (box.MinEdge.X > box.MaxEdge.X)
				{
					temp=box.MinEdge.X;
					box.MinEdge.X=box.MaxEdge.X;
					box.MaxEdge.X=temp;
				}
				if (box.MinEdge.Y > box.MaxEdge.Y)
				{
					temp=box.MinEdge.Y;
					box.MinEdge.Y=box.MaxEdge.Y;
					box.MaxEdge.Y=temp;
				}
				if (box.MinEdge.Z > box.MaxEdge.Z)
				{
					temp=box.MinEdge.Z;
					box.MinEdge.Z=box.MaxEdge.Z;
					box.MaxEdge.Z=temp;
				}

				//
				// Compute texture coords
				f32 tx1 = (box.MinEdge.X/BS)+0.5;
				f32 ty1 = (box.MinEdge.Y/BS)+0.5;
				f32 tz1 = (box.MinEdge.Z/BS)+0.5;
				f32 tx2 = (box.MaxEdge.X/BS)+0.5;
				f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
				f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
				f32 txc[24] = {
					// up
					tx1, 1-tz2, tx2, 1-tz1,
					// down
					tx1, tz1, tx2, tz2,
					// right
					tz1, 1-ty2, tz2, 1-ty1,
					// left
					1-tz2, 1-ty2, 1-tz1, 1-ty1,
					// back
					1-tx2, 1-ty2, 1-tx1, 1-ty1,
					// front
					tx1, 1-ty2, tx2, 1-ty1,
				};
				makeCuboid(&collector, box, tiles, 6, c, txc);
			}
		break;}
		case NDT_MESH:
		{
			v3f pos = intToFloat(p, BS);
			video::SColor c = MapBlock_LightColor(255, getInteriorLight(n, 1, nodedef), f.light_source);

			u8 facedir = 0;
			if (f.param_type_2 == CPT2_FACEDIR) {
				facedir = n.getFaceDir(nodedef);
			} else if (f.param_type_2 == CPT2_WALLMOUNTED) {
				//convert wallmounted to 6dfacedir.
				//when cache enabled, it is already converted
				facedir = n.getWallMounted(nodedef);
				if (!enable_mesh_cache) {
					static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
					facedir = wm_to_6d[facedir];
				}
			}

			if (f.mesh_ptr[facedir]) {
				// use cached meshes
				for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
					scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
					collector.append(getNodeTileN(n, p, j, data),
						(video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
						buf->getIndices(), buf->getIndexCount(), pos, c);
				}
			} else if (f.mesh_ptr[0]) {
				// no cache, clone and rotate mesh
				scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]);
				rotateMeshBy6dFacedir(mesh, facedir);
				recalculateBoundingBox(mesh);
				meshmanip->recalculateNormals(mesh, true, false);
				for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
					scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
					collector.append(getNodeTileN(n, p, j, data),
						(video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
						buf->getIndices(), buf->getIndexCount(), pos, c);
				}
				mesh->drop();
			}
		break;}
		}
	}
}

/* Value returned by sqlite3_total_changes() */
- sqlite3_mutex *mutex; /* Connection mutex */
int aLimit[SQLITE_N_LIMIT]; /* Limits */
+ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
struct sqlite3InitInfo { /* Information used during initialization */
- int iDb; /* When back is being initialized */
int newTnum; /* Rootpage of table being initialized */
+ u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
} init;
+ int nVdbeActive; /* Number of VDBEs currently running */
+ int nVdbeRead; /* Number of active VDBEs that read or write */
+ int nVdbeWrite; /* Number of active VDBEs that read and write */
+ int nVdbeExec; /* Number of nested calls to VdbeExec() */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
- struct Vdbe *pVdbe; /* List of active virtual machines */
- int activeVdbeCnt; /* Number of VDBEs currently executing */
- int writeVdbeCnt; /* Number of active VDBEs that are writing */
- int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
void (*xTrace)(void*,const char*); /* Trace function */
void *pTraceArg; /* Argument to the trace function */
void (*xProfile)(void*,const char*,u64); /* Profiling function */
@@ -9456,42 +10627,38 @@ struct sqlite3 {
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
void *pCollNeededArg;
sqlite3_value *pErr; /* Most recent error message */
- char *zErrMsg; /* Most recent error message (UTF-8 encoded) */
- char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */
union {
volatile int isInterrupted; /* True if sqlite3_interrupt has been called */
double notUsed1; /* Spacer */
} u1;
Lookaside lookaside; /* Lookaside malloc configuration */
#ifndef SQLITE_OMIT_AUTHORIZATION
- int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
- /* Access authorization function */
+ sqlite3_xauth xAuth; /* Access authorization function */
void *pAuthArg; /* 1st argument to the access auth function */
#endif
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int (*xProgress)(void *); /* The progress callback */
void *pProgressArg; /* Argument to the progress callback */
- int nProgressOps; /* Number of opcodes for progress callback */
+ unsigned nProgressOps; /* Number of opcodes for progress callback */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
+ int nVTrans; /* Allocated size of aVTrans */
Hash aModule; /* populated by sqlite3_create_module() */
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
VTable **aVTrans; /* Virtual tables with open transactions */
- int nVTrans; /* Allocated size of aVTrans */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
#endif
FuncDefHash aFunc; /* Hash table of connection functions */
Hash aCollSeq; /* All collating sequences */
BusyHandler busyHandler; /* Busy callback */
- int busyTimeout; /* Busy handler timeout, in msec */
Db aDbStatic[2]; /* Static space for the 2 default backends */
Savepoint *pSavepoint; /* List of active savepoints */
+ int busyTimeout; /* Busy handler timeout, in msec */
int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
- u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
i64 nDeferredCons; /* Net deferred constraints this transaction. */
+ i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
-
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
/* The following variables are all protected by the STATIC_MASTER
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
@@ -9509,6 +10676,9 @@ struct sqlite3 {
void (*xUnlockNotify)(void **, int); /* Unlock notify callback */
sqlite3 *pNextBlocked; /* Next in list of all blocked connections */
#endif
+#ifdef SQLITE_USER_AUTHENTICATION
+ sqlite3_userauth auth; /* User authentication information */
+#endif
};
/*
@@ -9519,48 +10689,73 @@ struct sqlite3 {
/*
** Possible values for the sqlite3.flags.
*/
-#define SQLITE_VdbeTrace 0x00000100 /* True to trace VDBE execution */
-#define SQLITE_InternChanges 0x00000200 /* Uncommitted Hash table changes */
-#define SQLITE_FullColNames 0x00000400 /* Show full column names on SELECT */
-#define SQLITE_ShortColNames 0x00000800 /* Show short columns names */
-#define SQLITE_CountRows 0x00001000 /* Count rows changed by INSERT, */
+#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
+#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */
+#define SQLITE_FullFSync 0x00000004 /* Use full fsync on the backend */
+#define SQLITE_CkptFullFSync 0x00000008 /* Use full fsync for checkpoint */
+#define SQLITE_CacheSpill 0x00000010 /* OK to spill pager cache */
+#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
+#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
-#define SQLITE_NullCallback 0x00002000 /* Invoke the callback once if the */
+#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
-#define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */
-#define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */
-#define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when
- ** accessing read-only databases */
-#define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */
-#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
-#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */
-#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */
-#define SQLITE_CkptFullFSync 0x00400000 /* Use full fsync for checkpoint */
-#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */
-#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */
-#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */
-#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */
-#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */
-#define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */
-#define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */
-#define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */
-
-/*
-** Bits of the sqlite3.flags field that are used by the
-** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface.
-** These must be the low-order bits of the flags field.
-*/
-#define SQLITE_QueryFlattener 0x01 /* Disable query flattening */
-#define SQLITE_ColumnCache 0x02 /* Disable the column cache */
-#define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */
-#define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */
-#define SQLITE_IndexCover 0x10 /* Disable index covering table */
-#define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */
-#define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */
-#define SQLITE_IdxRealAsInt 0x80 /* Store REAL as INT in indices */
-#define SQLITE_OptMask 0xff /* Mask of all disablable opts */
+#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */
+#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */
+#define SQLITE_VdbeAddopTrace 0x00001000 /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */
+#define SQLITE_ReadUncommitted 0x0004000 /* For shared-cache mode */
+#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */
+#define SQLITE_RecoveryMode 0x00010000 /* Ignore schema errors */
+#define SQLITE_ReverseOrder 0x00020000 /* Reverse unordered SELECTs */
+#define SQLITE_RecTriggers 0x00040000 /* Enable recursive triggers */
+#define SQLITE_ForeignKeys 0x00080000 /* Enforce foreign key constraints */
+#define SQLITE_AutoIndex 0x00100000 /* Enable automatic indexes */
+#define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */
+#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
+#define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */
+#define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */
+#define SQLITE_QueryOnly 0x02000000 /* Disable database changes */
+#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */
+
+
+/*
+** Bits of the sqlite3.dbOptFlags field that are used by the
+** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
+** selectively disable various optimizations.
+*/
+#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
+#define SQLITE_ColumnCache 0x0002 /* Column cache */
+#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
+#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
+/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */
+#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
+#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
+#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
+#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
+#define SQLITE_Transitive 0x0200 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
+#define SQLITE_Stat3 0x0800 /* Use the SQLITE_STAT3 table */
+#define SQLITE_AllOpts 0xffff /* All optimizations */
+
+/*
+** Macros for testing whether or not optimizations are enabled or disabled.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+#define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0)
+#define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0)
+#else
+#define OptimizationDisabled(db, mask) 0
+#define OptimizationEnabled(db, mask) 1
+#endif
+
+/*
+** Return true if it OK to factor constant expressions into the initialization
+** code. The argument is a Parse object for the code generator.
+*/
+#define ConstFactorOk(P) ((P)->okConstFactor)
/*
** Possible values for the sqlite.magic field.
@@ -9572,6 +10767,7 @@ struct sqlite3 {
#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
+#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */
/*
** Each SQL function is defined by an instance of the following
@@ -9581,8 +10777,7 @@ struct sqlite3 {
*/
struct FuncDef {
i16 nArg; /* Number of arguments. -1 means unlimited */
- u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */
- u8 flags; /* Some combination of SQLITE_FUNC_* */
+ u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
@@ -9614,15 +10809,22 @@ struct FuncDestructor {
};
/*
-** Possible values for FuncDef.flags
+** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF
+** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There
+** are assert() statements in the code to verify this.
*/
-#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
-#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
-#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
-#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
-#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */
-#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */
-#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */
+#define SQLITE_FUNC_ENCMASK 0x003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
+#define SQLITE_FUNC_LIKE 0x004 /* Candidate for the LIKE optimization */
+#define SQLITE_FUNC_CASE 0x008 /* Case-sensitive LIKE-type function */
+#define SQLITE_FUNC_EPHEM 0x010 /* Ephemeral. Delete with VDBE */
+#define SQLITE_FUNC_NEEDCOLL 0x020 /* sqlite3GetFuncCollSeq() might be called */
+#define SQLITE_FUNC_LENGTH 0x040 /* Built-in length() function */
+#define SQLITE_FUNC_TYPEOF 0x080 /* Built-in typeof() function */
+#define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */
+#define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */
+#define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */
+#define SQLITE_FUNC_CONSTANT 0x800 /* Constant inputs give a constant output */
+#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -9635,6 +10837,9 @@ struct FuncDestructor {
** as the user-data (sqlite3_user_data()) for the function. If
** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set.
**
+** VFUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag.
+**
** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
** Used to create an aggregate function definition implemented by
** the C functions xStep and xFinal. The first four parameters
@@ -9650,15 +10855,25 @@ struct FuncDestructor {
** parameter.
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
+ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
pArg, 0, xFunc, 0, 0, #zName, 0, 0}
#define LIKEFUNC(zName, nArg, arg, flags) \
- {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
+ (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
- {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
+#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
/*
@@ -9670,6 +10885,7 @@ struct FuncDestructor {
struct Savepoint {
char *zName; /* Savepoint name (nul-terminated) */
i64 nDeferredCons; /* Number of deferred fk violations */
+ i64 nDeferredImmCons; /* Number of deferred imm fk. */
Savepoint *pNext; /* Parent savepoint (if any) */
};
@@ -9704,53 +10920,35 @@ struct Column {
char *zDflt; /* Original text of the default value */
char *zType; /* Data type for this column */
char *zColl; /* Collating sequence. If NULL, use the default */
- u8 notNull; /* True if there is a NOT NULL constraint */
- u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */
+ u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
char affinity; /* One of the SQLITE_AFF_... values */
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- u8 isHidden; /* True if this column is 'hidden' */
-#endif
+ u8 szEst; /* Estimated size of this column. INT==1 */
+ u8 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
+/* Allowed values for Column.colFlags:
+*/
+#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
+#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
+
/*
** A "Collating Sequence" is defined by an instance of the following
** structure. Conceptually, a collating sequence consists of a name and
** a comparison routine that defines the order of that sequence.
**
-** There may two separate implementations of the collation function, one
-** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that
-** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine
-** native byte order. When a collation sequence is invoked, SQLite selects
-** the version that will require the least expensive encoding
-** translations, if any.
-**
-** The CollSeq.pUser member variable is an extra parameter that passed in
-** as the first argument to the UTF-8 comparison function, xCmp.
-** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function,
-** xCmp16.
-**
-** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the
+** If CollSeq.xCmp is NULL, it means that the
** collating sequence is undefined. Indices built on an undefined
** collating sequence may not be read or written.
*/
struct CollSeq {
char *zName; /* Name of the collating sequence, UTF-8 encoded */
u8 enc; /* Text encoding handled by xCmp() */
- u8 type; /* One of the SQLITE_COLL_... values below */
void *pUser; /* First argument to xCmp() */
int (*xCmp)(void*,int, const void*, int, const void*);
void (*xDel)(void*); /* Destructor for pUser */
};
/*
-** Allowed values of CollSeq.type:
-*/
-#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */
-#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */
-#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */
-#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */
-
-/*
** A sort order can be either ASC or DESC.
*/
#define SQLITE_SO_ASC 0 /* Sort in ascending order */
@@ -9763,18 +10961,18 @@ struct CollSeq {
** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve
** the speed a little by numbering the values consecutively.
**
-** But rather than start with 0 or 1, we begin with 'a'. That way,
+** But rather than start with 0 or 1, we begin with 'A'. That way,
** when multiple affinity types are concatenated into a string and
** used as the P4 operand, they will be more readable.
**
** Note also that the numeric types are grouped together so that testing
-** for a numeric type is a single comparison.
+** for a numeric type is a single comparison. And the NONE type is first.
*/
-#define SQLITE_AFF_TEXT 'a'
-#define SQLITE_AFF_NONE 'b'
-#define SQLITE_AFF_NUMERIC 'c'
-#define SQLITE_AFF_INTEGER 'd'
-#define SQLITE_AFF_REAL 'e'
+#define SQLITE_AFF_NONE 'A'
+#define SQLITE_AFF_TEXT 'B'
+#define SQLITE_AFF_NUMERIC 'C'
+#define SQLITE_AFF_INTEGER 'D'
+#define SQLITE_AFF_REAL 'E'
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
@@ -9782,15 +10980,21 @@ struct CollSeq {
** The SQLITE_AFF_MASK values masks off the significant bits of an
** affinity value.
*/
-#define SQLITE_AFF_MASK 0x67
+#define SQLITE_AFF_MASK 0x47
/*
** Additional bit values that can be ORed with an affinity without
** changing the affinity.
+**
+** The SQLITE_NOTNULL flag is a combination of NULLEQ and JUMPIFNULL.
+** It causes an assert() to fire if either operand to a comparison
+** operator is NULL. It is added to certain comparison operators to
+** prove that the operands are always NOT NULL.
*/
-#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */
-#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */
+#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
+#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
+#define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */
/*
** An object of this type is created for each virtual table present in
@@ -9876,28 +11080,32 @@ struct VTable {
*/
struct Table {
char *zName; /* Name of the table or view */
- int iPKey; /* If not negative, use aCol[iPKey] as the primary key */
- int nCol; /* Number of columns in this table */
Column *aCol; /* Information about each column */
Index *pIndex; /* List of SQL indexes on this table. */
- int tnum; /* Root BTree node for this table (see note above) */
- unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
- u16 nRef; /* Number of pointers to this Table */
- u8 tabFlags; /* Mask of TF_* values */
- u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
FKey *pFKey; /* Linked list of all foreign keys in this table */
char *zColAff; /* String defining the affinity of each column */
#ifndef SQLITE_OMIT_CHECK
- Expr *pCheck; /* The AND of all CHECK constraints */
+ ExprList *pCheck; /* All CHECK constraints */
+#endif
+ LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */
+ int tnum; /* Root BTree node for this table (see note above) */
+ i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */
+ i16 nCol; /* Number of columns in this table */
+ u16 nRef; /* Number of pointers to this Table */
+ LogEst szTabRow; /* Estimated size of each table row in bytes */
+#ifdef SQLITE_ENABLE_COSTMULT
+ LogEst costMult; /* Cost multiplier for using this table */
#endif
+ u8 tabFlags; /* Mask of TF_* values */
+ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
#ifndef SQLITE_OMIT_ALTERTABLE
int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
- VTable *pVTable; /* List of VTable objects. */
int nModuleArg; /* Number of arguments to the module */
char **azModuleArg; /* Text of all module args. [0] is module name */
+ VTable *pVTable; /* List of VTable objects. */
#endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
Schema *pSchema; /* Schema that contains this table */
@@ -9905,15 +11113,14 @@ struct Table {
};
/*
-** Allowed values for Tabe.tabFlags.
+** Allowed values for Table.tabFlags.
*/
#define TF_Readonly 0x01 /* Read-only system table */
#define TF_Ephemeral 0x02 /* An ephemeral table */
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
#define TF_Virtual 0x10 /* Is a virtual table */
-#define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */
-
+#define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */
/*
@@ -9923,12 +11130,15 @@ struct Table {
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0)
-# define IsHiddenColumn(X) ((X)->isHidden)
+# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
#else
# define IsVirtual(X) 0
# define IsHiddenColumn(X) 0
#endif
+/* Does the table have a rowid */
+#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
+
/*
** Each foreign key constraint is an instance of the following structure.
**
@@ -9943,26 +11153,35 @@ struct Table {
** );
**
** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2".
+** Equivalent names:
+**
+** from-table == child-table
+** to-table == parent-table
**
** Each REFERENCES clause generates an instance of the following structure
** which is attached to the from-table. The to-table need not exist when
** the from-table is created. The existence of the to-table is not checked.
+**
+** The list of all parents for child Table X is held at X.pFKey.
+**
+** A list of all children for a table named Z (which might not even exist)
+** is held in Schema.fkeyHash with a hash key of Z.
*/
struct FKey {
Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */
- FKey *pNextFrom; /* Next foreign key in pFrom */
+ FKey *pNextFrom; /* Next FKey with the same in pFrom. Next parent of pFrom */
char *zTo; /* Name of table that the key points to (aka: Parent) */
- FKey *pNextTo; /* Next foreign key on table named zTo */
- FKey *pPrevTo; /* Previous foreign key on table named zTo */
+ FKey *pNextTo; /* Next with the same zTo. Next child of zTo. */
+ FKey *pPrevTo; /* Previous with the same zTo */
int nCol; /* Number of columns in this key */
/* EV: R-30323-21917 */
- u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
- u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
- Trigger *apTrigger[2]; /* Triggers for aAction[] actions */
- struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
- int iFrom; /* Index of column in pFrom */
- char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
- } aCol[1]; /* One entry for each of nCol column s */
+ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
+ u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
+ Trigger *apTrigger[2];/* Triggers for aAction[] actions */
+ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
+ int iFrom; /* Index of column in pFrom */
+ char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
+ } aCol[1]; /* One entry for each of nCol columns */
};
/*
@@ -10002,19 +11221,25 @@ struct FKey {
#define OE_SetDflt 8 /* Set the foreign key value to its default */
#define OE_Cascade 9 /* Cascade the changes */
-#define OE_Default 99 /* Do whatever the default action is */
+#define OE_Default 10 /* Do whatever the default action is */
/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
+**
+** Note that aSortOrder[] and aColl[] have nField+1 slots. There
+** are nField slots for the columns of an index then one extra slot
+** for the rowid at the end.
*/
struct KeyInfo {
- sqlite3 *db; /* The database connection */
+ u32 nRef; /* Number of references to this KeyInfo object */
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
- u16 nField; /* Number of entries in aColl[] */
- u8 *aSortOrder; /* Sort order for each column. May be NULL */
+ u16 nField; /* Number of key columns in the index */
+ u16 nXField; /* Number of columns beyond the key columns */
+ sqlite3 *db; /* The database connection */
+ u8 *aSortOrder; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
@@ -10031,24 +11256,20 @@ struct KeyInfo {
**
** This structure holds a record that has already been disassembled
** into its constituent fields.
+**
+** The r1 and r2 member variables are only used by the optimized comparison
+** functions vdbeRecordCompareInt() and vdbeRecordCompareString().
*/
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
u16 nField; /* Number of entries in apMem[] */
- u16 flags; /* Boolean settings. UNPACKED_... below */
- i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
+ i8 default_rc; /* Comparison result if keys are equal */
+ u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
Mem *aMem; /* Values */
+ int r1; /* Value to return if (lhs > rhs) */
+ int r2; /* Value to return if (rhs < lhs) */
};
-/*
-** Allowed values of UnpackedRecord.flags
-*/
-#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */
-#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */
-#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
-#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
-#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
-#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
/*
** Each SQL index is represented in memory by an
@@ -10077,34 +11298,60 @@ struct UnpackedRecord {
** element.
*/
struct Index {
- char *zName; /* Name of this index */
- int nColumn; /* Number of columns in the table used by this index */
- int *aiColumn; /* Which columns are used by this index. 1st is 0 */
- unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
- Table *pTable; /* The SQL table being indexed */
- int tnum; /* Page containing root of this index in database file */
- u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
- u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
- u8 bUnordered; /* Use this index for == or IN queries only */
- char *zColAff; /* String defining the affinity of each column */
- Index *pNext; /* The next index associated with the same table */
- Schema *pSchema; /* Schema containing this index */
- u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
- char **azColl; /* Array of collation sequence names for index */
- IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */
+ char *zName; /* Name of this index */
+ i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */
+ LogEst *aiRowLogEst; /* From ANALYZE: Est. rows selected by each column */
+ Table *pTable; /* The SQL table being indexed */
+ char *zColAff; /* String defining the affinity of each column */
+ Index *pNext; /* The next index associated with the same table */
+ Schema *pSchema; /* Schema containing this index */
+ u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
+ char **azColl; /* Array of collation sequence names for index */
+ Expr *pPartIdxWhere; /* WHERE clause for partial indices */
+ KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */
+ int tnum; /* DB Page containing root of this index */
+ LogEst szIdxRow; /* Estimated average row size in bytes */
+ u16 nKeyCol; /* Number of columns forming the key */
+ u16 nColumn; /* Number of columns stored in the index */
+ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
+ unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
+ unsigned bUnordered:1; /* Use this index for == or IN queries only */
+ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
+ unsigned isResized:1; /* True if resizeIndexObject() has been called */
+ unsigned isCovering:1; /* True if this is a covering index */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int nSample; /* Number of elements in aSample[] */
+ int nSampleCol; /* Size of IndexSample.anEq[] and so on */
+ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
+ IndexSample *aSample; /* Samples of the left-most key */
+ tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this table */
+#endif
};
/*
-** Each sample stored in the sqlite_stat2 table is represented in memory
-** using a structure of this type.
+** Allowed values for Index.idxType
+*/
+#define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */
+#define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */
+#define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */
+
+/* Return true if index X is a PRIMARY KEY index */
+#define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY)
+
+/* Return true if index X is a UNIQUE index */
+#define IsUniqueIndex(X) ((X)->onError!=OE_None)
+
+/*
+** Each sample stored in the sqlite_stat3 table is represented in memory
+** using a structure of this type. See documentation at the top of the
+** analyze.c source file for additional information.
*/
struct IndexSample {
- union {
- char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
- double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */
- } u;
- u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
- u8 nByte; /* Size in byte of text or blob. */
+ void *p; /* Pointer to sampled record */
+ int n; /* Size of record in bytes */
+ tRowcnt *anEq; /* Est. number of rows where the key equals this sample */
+ tRowcnt *anLt; /* Est. number of rows where key is less than this sample */
+ tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */
};
/*
@@ -10139,8 +11386,10 @@ struct AggInfo {
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
** than the source table */
int sortingIdx; /* Cursor number of the sorting index */
- ExprList *pGroupBy; /* The group by clause */
+ int sortingIdxPTab; /* Cursor number of pseudo-table */
int nSortingColumn; /* Number of columns in the sorting index */
+ int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
+ ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */
int iTable; /* Cursor number of the source table */
@@ -10150,7 +11399,6 @@ struct AggInfo {
Expr *pExpr; /* The original expression */
} *aCol;
int nColumn; /* Number of used entries in aCol[] */
- int nColumnAlloc; /* Number of slots allocated for aCol[] */
int nAccumulator; /* Number of columns that show through to the output.
** Additional columns are used only as parameters to
** aggregate functions */
@@ -10161,7 +11409,6 @@ struct AggInfo {
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
- int nFuncAlloc; /* Number of slots allocated for aFunc[] */
};
/*
@@ -10246,7 +11493,7 @@ typedef int ynVar;
struct Expr {
u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */
- u16 flags; /* Various flags. EP_* See below */
+ u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
int iValue; /* Non-negative integer value if EP_IntValue */
@@ -10260,80 +11507,78 @@ struct Expr {
Expr *pLeft; /* Left subnode */
Expr *pRight; /* Right subnode */
union {
- ExprList *pList; /* Function arguments or in "<expr> IN (<expr-list)" */
- Select *pSelect; /* Used for sub-selects and "<expr> IN (<select>)" */
+ ExprList *pList; /* op = IN, EXISTS, SELECT, CASE, FUNCTION, BETWEEN */
+ Select *pSelect; /* EP_xIsSelect and op = IN, EXISTS, SELECT */
} x;
- CollSeq *pColl; /* The collation type of the column or 0 */
/* If the EP_Reduced flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
** access them will result in a segfault or malfunction.
*********************************************************************/
+#if SQLITE_MAX_EXPR_DEPTH>0
+ int nHeight; /* Height of the tree headed by this node */
+#endif
int iTable; /* TK_COLUMN: cursor number of table holding column
** TK_REGISTER: register number
- ** TK_TRIGGER: 1 -> new, 0 -> old */
+ ** TK_TRIGGER: 1 -> new, 0 -> old
+ ** EP_Unlikely: 1000 times likelihood */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1). */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
- u8 flags2; /* Second set of flags. EP2_... */
- u8 op2; /* If a TK_REGISTER, the original value of Expr.op */
+ u8 op2; /* TK_REGISTER: original value of Expr.op
+ ** TK_COLUMN: the value of p5 for OP_Column
+ ** TK_AGG_FUNCTION: nesting depth */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
Table *pTab; /* Table for TK_COLUMN expressions. */
-#if SQLITE_MAX_EXPR_DEPTH>0
- int nHeight; /* Height of the tree headed by this node */
-#endif
};
/*
** The following are the meanings of bits in the Expr.flags field.
*/
-#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */
-#define EP_Agg 0x0002 /* Contains one or more aggregate functions */
-#define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */
-#define EP_Error 0x0008 /* Expression contains one or more errors */
-#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */
-#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */
-#define EP_DblQuoted 0x0040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */
-#define EP_FixedDest 0x0200 /* Result needed in a specific register */
-#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
-
-#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
+#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
+#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
+#define EP_Resolved 0x000004 /* IDs have been resolved to COLUMNs */
+#define EP_Error 0x000008 /* Expression contains one or more errors */
+#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
+#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
+#define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */
+#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */
+#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
+#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
+#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
+#define EP_Constant 0x080000 /* Node is a constant */
+#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
/*
-** The following are the meanings of bits in the Expr.flags2 field.
+** These macros can be used to test, set, or clear bits in the
+** Expr.flags field.
*/
-#define EP2_MallocedToken 0x0001 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP2_Irreducible 0x0002 /* Cannot EXPRDUP_REDUCE this Expr */
+#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
+#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
+#define ExprSetProperty(E,P) (E)->flags|=(P)
+#define ExprClearProperty(E,P) (E)->flags&=~(P)
-/*
-** The pseudo-routine sqlite3ExprSetIrreducible sets the EP2_Irreducible
-** flag on an expression structure. This flag is used for VV&A only. The
-** routine is implemented as a macro that only works when in debugging mode,
-** so as not to burden production code.
+/* The ExprSetVVAProperty() macro is used for Verification, Validation,
+** and Accreditation only. It works like ExprSetProperty() during VVA
+** processes but is a no-op for delivery.
*/
#ifdef SQLITE_DEBUG
-# define ExprSetIrreducible(X) (X)->flags2 |= EP2_Irreducible
+# define ExprSetVVAProperty(E,P) (E)->flags|=(P)
#else
-# define ExprSetIrreducible(X)
+# define ExprSetVVAProperty(E,P)
#endif
/*
-** These macros can be used to test, set, or clear bits in the
-** Expr.flags field.
-*/
-#define ExprHasProperty(E,P) (((E)->flags&(P))==(P))
-#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0)
-#define ExprSetProperty(E,P) (E)->flags|=(P)
-#define ExprClearProperty(E,P) (E)->flags&=~(P)
-
-/*
** Macros to determine the number of bytes required by a normal Expr
** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
** and an Expr struct with the EP_TokenOnly flag set.
@@ -10355,20 +11600,33 @@ struct Expr {
** list of "ID = expr" items in an UPDATE. A list of expressions can
** also be used as the argument to a function, in which case the a.zName
** field is not used.
+**
+** By default the Expr.zSpan field holds a human-readable description of
+** the expression that is used in the generation of error messages and
+** column labels. In this case, Expr.zSpan is typically the text of a
+** column expression as it exists in a SELECT statement. However, if
+** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
+** of the result column in the form: DATABASE.TABLE.COLUMN. This later
+** form is used for name resolution with nested FROM clauses.
*/
struct ExprList {
int nExpr; /* Number of expressions on the list */
- int nAlloc; /* Number of entries allocated below */
- int iECursor; /* VDBE Cursor associated with this ExprList */
- struct ExprList_item {
- Expr *pExpr; /* The list of expressions */
- char *zName; /* Token associated with this expression */
- char *zSpan; /* Original text of the expression */
- u8 sortOrder; /* 1 for DESC or 0 for ASC */
- u8 done; /* A flag to indicate when processing is finished */
- u16 iCol; /* For ORDER BY, column number in result set */
- u16 iAlias; /* Index into Parse.aAlias[] for zName */
- } *a; /* One entry for each expression */
+ struct ExprList_item { /* For each expression in the list */
+ Expr *pExpr; /* The list of expressions */
+ char *zName; /* Token associated with this expression */
+ char *zSpan; /* Original text of the expression */
+ u8 sortOrder; /* 1 for DESC or 0 for ASC */
+ unsigned done :1; /* A flag to indicate when processing is finished */
+ unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
+ unsigned reusable :1; /* Constant expression is reusable */
+ union {
+ struct {
+ u16 iOrderByCol; /* For ORDER BY, column number in result set */
+ u16 iAlias; /* Index into Parse.aAlias[] for zName */
+ } x;
+ int iConstExprReg; /* Register in which Expr value is cached */
+ } u;
+ } *a; /* Alloc a power of two greater or equal to nExpr */
};
/*
@@ -10403,7 +11661,6 @@ struct IdList {
int idx; /* Index in some Table.aCol[] of a column named zName */
} *a;
int nId; /* Number of identifiers on the list */
- int nAlloc; /* Number of entries allocated for a[] below */
};
/*
@@ -10421,6 +11678,12 @@ typedef u64 Bitmask;
#define BMS ((int)(sizeof(Bitmask)*8))
/*
+** A bit in a Bitmask
+*/
+#define MASKBIT(n) (((Bitmask)1)<<(n))
+#define MASKBIT32(n) (((unsigned int)1)<<(n))
+
+/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
** the SrcList.a[] array.
@@ -10440,17 +11703,23 @@ typedef u64 Bitmask;
** contains more than 63 columns and the 64-th or later column is used.
*/
struct SrcList {
- i16 nSrc; /* Number of tables or subqueries in the FROM clause */
- i16 nAlloc; /* Number of entries allocated in a[] below */
+ int nSrc; /* Number of tables or subqueries in the FROM clause */
+ u32 nAlloc; /* Number of entries allocated in a[] below */
struct SrcList_item {
+ Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
char *zName; /* Name of the table */
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
Table *pTab; /* An SQL table corresponding to zName */
Select *pSelect; /* A SELECT statement used in place of a table name */
- u8 isPopulated; /* Temporary table associated with SELECT is populated */
+ int addrFillSub; /* Address of subroutine to manifest a subquery */
+ int regReturn; /* Register holding return address of addrFillSub */
+ int regResult; /* Registers holding results of a co-routine */
u8 jointype; /* Type of join between this able and the previous */
- u8 notIndexed; /* True if there is a NOT INDEXED clause */
+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
+ unsigned isCorrelated :1; /* True if sub-query is correlated */
+ unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
@@ -10476,75 +11745,6 @@ struct SrcList {
/*
-** A WherePlan object holds information that describes a lookup
-** strategy.
-**
-** This object is intended to be opaque outside of the where.c module.
-** It is included here only so that that compiler will know how big it
-** is. None of the fields in this object should be used outside of
-** the where.c module.
-**
-** Within the union, pIdx is only used when wsFlags&WHERE_INDEXED is true.
-** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx
-** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the
-** case that more than one of these conditions is true.
-*/
-struct WherePlan {
- u32 wsFlags; /* WHERE_* flags that describe the strategy */
- u32 nEq; /* Number of == constraints */
- double nRow; /* Estimated number of rows (for EQP) */
- union {
- Index *pIdx; /* Index when WHERE_INDEXED is true */
- struct WhereTerm *pTerm; /* WHERE clause term for OR-search */
- sqlite3_index_info *pVtabIdx; /* Virtual table index to use */
- } u;
-};
-
-/*
-** For each nested loop in a WHERE clause implementation, the WhereInfo
-** structure contains a single instance of this structure. This structure
-** is intended to be private the the where.c module and should not be
-** access or modified by other modules.
-**
-** The pIdxInfo field is used to help pick the best index on a
-** virtual table. The pIdxInfo pointer contains indexing
-** information for the i-th table in the FROM clause before reordering.
-** All the pIdxInfo pointers are freed by whereInfoFree() in where.c.
-** All other information in the i-th WhereLevel object for the i-th table
-** after FROM clause ordering.
-*/
-struct WhereLevel {
- WherePlan plan; /* query plan for this element of the FROM clause */
- int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
- int iTabCur; /* The VDBE cursor used to access the table */
- int iIdxCur; /* The VDBE cursor used to access pIdx */
- int addrBrk; /* Jump here to break out of the loop */
- int addrNxt; /* Jump here to start the next IN combination */
- int addrCont; /* Jump here to continue with the next loop cycle */
- int addrFirst; /* First instruction of interior of the loop */
- u8 iFrom; /* Which entry in the FROM clause */
- u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
- union { /* Information that depends on plan.wsFlags */
- struct {
- int nIn; /* Number of entries in aInLoop[] */
- struct InLoop {
- int iCur; /* The VDBE cursor used by this IN operator */
- int addrInTop; /* Top of the IN loop */
- } *aInLoop; /* Information about each nested IN operator */
- } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */
- } u;
-
- /* The following field is really not part of the current level. But
- ** we need a place to cache virtual table index information for each
- ** virtual table in the FROM clause and the WhereLevel structure is
- ** a convenient place since there is one WhereLevel for each FROM clause
- ** element.
- */
- sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */
-};
-
-/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
** and the WhereInfo.wctrlFlags member.
*/
@@ -10553,33 +11753,22 @@ struct WhereLevel {
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
-#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */
-#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
-#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
-#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */
+#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */
+#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
+#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
+ /* 0x0080 // not currently used */
+#define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */
+#define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */
+#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
+#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
+#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
-/*
-** The WHERE clause processing routine has two halves. The
-** first part does the start of the WHERE loop and the second
-** half does the tail of the WHERE loop. An instance of
-** this structure is returned by the first half and passed
-** into the second half to give some continuity.
+/* Allowed return values from sqlite3WhereIsDistinct()
*/
-struct WhereInfo {
- Parse *pParse; /* Parsing and code generating context */
- u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
- u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
- u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
- SrcList *pTabList; /* List of tables in the join */
- int iTop; /* The very beginning of the WHERE loop */
- int iContinue; /* Jump here to continue with next record */
- int iBreak; /* Jump here to break out of the loop */
- int nLevel; /* Number of nested loop */
- struct WhereClause *pWC; /* Decomposition of the WHERE clause */
- double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
- double nRowOut; /* Estimated number of output rows */
- WhereLevel a[1]; /* Information about each nest loop in WHERE */
-};
+#define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */
+#define WHERE_DISTINCT_UNIQUE 1 /* No duplicates */
+#define WHERE_DISTINCT_ORDERED 2 /* All duplicates are adjacent */
+#define WHERE_DISTINCT_UNORDERED 3 /* Duplicates are scattered */
/*
** A NameContext defines a context in which to resolve table and column
@@ -10605,18 +11794,29 @@ struct WhereInfo {
struct NameContext {
Parse *pParse; /* The parser */
SrcList *pSrcList; /* One or more tables used to resolve names */
- ExprList *pEList; /* Optional list of named expressions */
- int nRef; /* Number of names resolved by this context */
- int nErr; /* Number of errors encountered while resolving names */
- u8 allowAgg; /* Aggregate functions allowed here */
- u8 hasAgg; /* True if aggregates are seen */
- u8 isCheck; /* True if resolving names in a CHECK constraint */
- int nDepth; /* Depth of subquery recursion. 1 for no recursion */
+ ExprList *pEList; /* Optional list of result-set columns */
AggInfo *pAggInfo; /* Information about aggregates at this level */
NameContext *pNext; /* Next outer name context. NULL for outermost */
+ int nRef; /* Number of names resolved by this context */
+ int nErr; /* Number of errors encountered while resolving names */
+ u16 ncFlags; /* Zero or more NC_* flags defined below */
};
/*
+** Allowed values for the NameContext, ncFlags field.
+**
+** Note: NC_MinMaxAgg must have the same value as SF_MinMaxAgg and
+** SQLITE_FUNC_MINMAX.
+**
+*/
+#define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */
+#define NC_HasAgg 0x0002 /* One or more aggregate functions seen */
+#define NC_IsCheck 0x0004 /* True if resolving names in a CHECK constraint */
+#define NC_InAggFunc 0x0008 /* True if analyzing arguments to an agg func */
+#define NC_PartIdx 0x0010 /* True if resolving a partial index WHERE */
+#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
+
+/*
** An instance of the following structure contains all information
** needed to generate code for a single SELECT statement.
**
@@ -10633,14 +11833,19 @@ struct NameContext {
** as the OP_OpenEphm instruction is coded because not
** enough information about the compound query is known at that point.
** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences
-** for the result set. The KeyInfo for addrOpenTran[2] contains collating
+** for the result set. The KeyInfo for addrOpenEphm[2] contains collating
** sequences for the ORDER BY clause.
*/
struct Select {
ExprList *pEList; /* The fields of the result */
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
- char affinity; /* MakeRecord with this affinity for SRT_Set */
u16 selFlags; /* Various SF_* values */
+ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
+#if SELECTTRACE_ENABLED
+ char zSelName[12]; /* Symbolic name of this SELECT use for debugging */
+#endif
+ int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
+ u64 nSelectRow; /* Estimated number of result rows */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -10648,12 +11853,9 @@ struct Select {
ExprList *pOrderBy; /* The ORDER BY clause */
Select *pPrior; /* Prior select in a compound select statement */
Select *pNext; /* Next select to the left in a compound */
- Select *pRightmost; /* Right-most select in a compound select statement */
Expr *pLimit; /* LIMIT expression. NULL means not used. */
Expr *pOffset; /* OFFSET expression. NULL means not used. */
- int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
- int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
- double nSelectRow; /* Estimated number of result rows */
+ With *pWith; /* WITH clause attached to this select. Or NULL. */
};
/*
@@ -10666,38 +11868,109 @@ struct Select {
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
+#define SF_Compound 0x0040 /* Part of a compound query */
+#define SF_Values 0x0080 /* Synthesized from VALUES clause */
+ /* 0x0100 NOT USED */
+#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
+#define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */
+#define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */
+#define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */
/*
-** The results of a select can be distributed in several ways. The
-** "SRT" prefix means "SELECT Result Type".
+** The results of a SELECT can be distributed in several ways, as defined
+** by one of the following macros. The "SRT" prefix means "SELECT Result
+** Type".
+**
+** SRT_Union Store results as a key in a temporary index
+** identified by pDest->iSDParm.
+**
+** SRT_Except Remove results from the temporary index pDest->iSDParm.
+**
+** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result
+** set is not empty.
+**
+** SRT_Discard Throw the results away. This is used by SELECT
+** statements within triggers whose only purpose is
+** the side-effects of functions.
+**
+** All of the above are free to ignore their ORDER BY clause. Those that
+** follow must honor the ORDER BY clause.
+**
+** SRT_Output Generate a row of output (using the OP_ResultRow
+** opcode) for each row in the result set.
+**
+** SRT_Mem Only valid if the result is a single column.
+** Store the first column of the first result row
+** in register pDest->iSDParm then abandon the rest
+** of the query. This destination implies "LIMIT 1".
+**
+** SRT_Set The result must be a single column. Store each
+** row of result as the key in table pDest->iSDParm.
+** Apply the affinity pDest->affSdst before storing
+** results. Used to implement "IN (SELECT ...)".
+**
+** SRT_EphemTab Create an temporary table pDest->iSDParm and store
+** the result there. The cursor is left open after
+** returning. This is like SRT_Table except that
+** this destination uses OP_OpenEphemeral to create
+** the table first.
+**
+** SRT_Coroutine Generate a co-routine that returns a new row of
+** results each time it is invoked. The entry point
+** of the co-routine is stored in register pDest->iSDParm
+** and the result row is stored in pDest->nDest registers
+** starting with pDest->iSdst.
+**
+** SRT_Table Store results in temporary table pDest->iSDParm.
+** SRT_Fifo This is like SRT_EphemTab except that the table
+** is assumed to already be open. SRT_Fifo has
+** the additional property of being able to ignore
+** the ORDER BY clause.
+**
+** SRT_DistFifo Store results in a temporary table pDest->iSDParm.
+** But also use temporary table pDest->iSDParm+1 as
+** a record of all prior results and ignore any duplicate
+** rows. Name means: "Distinct Fifo".
+**
+** SRT_Queue Store results in priority queue pDest->iSDParm (really
+** an index). Append a sequence number so that all entries
+** are distinct.
+**
+** SRT_DistQueue Store results in priority queue pDest->iSDParm only if
+** the same record has never been stored before. The
+** index at pDest->iSDParm+1 hold all prior stores.
*/
#define SRT_Union 1 /* Store result as keys in an index */
#define SRT_Except 2 /* Remove result from a UNION index */
#define SRT_Exists 3 /* Store 1 if the result is not empty */
#define SRT_Discard 4 /* Do not save the results anywhere */
+#define SRT_Fifo 5 /* Store result as data with an automatic rowid */
+#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */
+#define SRT_Queue 7 /* Store result in an queue */
+#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */
/* The ORDER BY clause is ignored for all of the above */
-#define IgnorableOrderby(X) ((X->eDest)<=SRT_Discard)
+#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue)
-#define SRT_Output 5 /* Output each row of result */
-#define SRT_Mem 6 /* Store result in a memory cell */
-#define SRT_Set 7 /* Store results as keys in an index */
-#define SRT_Table 8 /* Store result as data with an automatic rowid */
-#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */
-#define SRT_Coroutine 10 /* Generate a single row of result */
+#define SRT_Output 9 /* Output each row of result */
+#define SRT_Mem 10 /* Store result in a memory cell */
+#define SRT_Set 11 /* Store results as keys in an index */
+#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
+#define SRT_Coroutine 13 /* Generate a single row of result */
+#define SRT_Table 14 /* Store result as data with an automatic rowid */
/*
-** A structure used to customize the behavior of sqlite3Select(). See
-** comments above sqlite3Select() for details.
+** An instance of this object describes where to put of the results of
+** a SELECT statement.
*/
-typedef struct SelectDest SelectDest;
struct SelectDest {
- u8 eDest; /* How to dispose of the results */
- u8 affinity; /* Affinity used when eDest==SRT_Set */
- int iParm; /* A parameter used by the eDest disposal method */
- int iMem; /* Base register where results are written */
- int nMem; /* Number of registers allocated */
+ u8 eDest; /* How to dispose of the results. On of SRT_* above. */
+ char affSdst; /* Affinity used when eDest==SRT_Set */
+ int iSDParm; /* A parameter used by the eDest disposal method */
+ int iSdst; /* Base register where results are written */
+ int nSdst; /* Number of registers allocated */
+ ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
};
/*
@@ -10743,19 +12016,29 @@ struct AutoincInfo {
*/
struct TriggerPrg {
Trigger *pTrigger; /* Trigger this program was coded from */
- int orconf; /* Default ON CONFLICT policy */
+ TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */
SubProgram *pProgram; /* Program implementing pTrigger/orconf */
+ int orconf; /* Default ON CONFLICT policy */
u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */
- TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */
};
/*
** The yDbMask datatype for the bitmask of all attached databases.
*/
#if SQLITE_MAX_ATTACHED>30
- typedef sqlite3_uint64 yDbMask;
+ typedef unsigned char yDbMask[(SQLITE_MAX_ATTACHED+9)/8];
+# define DbMaskTest(M,I) (((M)[(I)/8]&(1<<((I)&7)))!=0)
+# define DbMaskZero(M) memset((M),0,sizeof(M))
+# define DbMaskSet(M,I) (M)[(I)/8]|=(1<<((I)&7))
+# define DbMaskAllZero(M) sqlite3DbMaskAllZero(M)
+# define DbMaskNonZero(M) (sqlite3DbMaskAllZero(M)==0)
#else
typedef unsigned int yDbMask;
+# define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0)
+# define DbMaskZero(M) (M)=0
+# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I))
+# define DbMaskAllZero(M) (M)==0
+# define DbMaskNonZero(M) (M)!=0
#endif
/*
@@ -10776,16 +12059,17 @@ struct TriggerPrg {
*/
struct Parse {
sqlite3 *db; /* The main database structure */
- int rc; /* Return code from execution */
char *zErrMsg; /* An error message */
Vdbe *pVdbe; /* An engine for executing database bytecode */
+ int rc; /* Return code from execution */
u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
- u8 nameClash; /* A permanent table name clashes with temp table name */
u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */
- u8 parseError; /* True after a parsing error. Ticket #1794 */
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
- u8 nTempInUse; /* Number of aTempReg[] currently checked out */
+ u8 isMultiWrite; /* True if statement may modify/insert multiple rows */
+ u8 mayAbort; /* True if statement may throw an ABORT exception */
+ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
+ u8 okConstFactor; /* OK to factor out constants */
int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
@@ -10793,77 +12077,95 @@ struct Parse {
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
int nSet; /* Number of sets used so far */
+ int nOnce; /* Number of OP_Once instructions so far */
+ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
+ int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */
int ckBase; /* Base register of data during check constraints */
+ int iPartIdxTab; /* Table corresponding to a partial index */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
- u8 nColCache; /* Number of entries in the column cache */
- u8 iColCache; /* Next entry of the cache to replace */
+ int nLabel; /* Number of labels used */
+ int *aLabel; /* Space to hold the labels */
struct yColCache {
int iTable; /* Table cursor number */
- int iColumn; /* Table column number */
+ i16 iColumn; /* Table column number */
u8 tempReg; /* iReg is a temp register that needs to be freed */
int iLevel; /* Nesting level */
int iReg; /* Reg with value of this column. 0 means none. */
int lru; /* Least recently used entry has the smallest value */
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
+ ExprList *pConstExpr;/* Constant expressions */
+ Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
- u8 isMultiWrite; /* True if statement may affect/insert multiple rows */
- u8 mayAbort; /* True if statement may throw an ABORT exception */
- int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */
+ int regRowid; /* Register holding rowid of CREATE TABLE entry */
+ int regRoot; /* Register holding root page number for new objects */
+ int nMaxArg; /* Max args passed to user function by sub-program */
+#if SELECTTRACE_ENABLED
+ int nSelect; /* Number of SELECT statements seen */
+ int nSelectIndent; /* How far to indent SELECTTRACE() output */
+#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
int nTableLock; /* Number of locks in aTableLock */
TableLock *aTableLock; /* Required table locks for shared-cache mode */
#endif
- int regRowid; /* Register holding rowid of CREATE TABLE entry */
- int regRoot; /* Register holding root page number for new objects */
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
- int nMaxArg; /* Max args passed to user function by sub-program */
/* Information used while coding trigger programs. */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
+ int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
+ int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */
+ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
- double nQueryLoop; /* Estimated number of iterations of a query */
-
- /* Above is constant between recursions. Below is reset before and after
- ** each recursion */
-
- int nVar; /* Number of '?' variables seen in the SQL so far */
- int nzVar; /* Number of available slots in azVar[] */
- char **azVar; /* Pointers to names of parameters */
- Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
- int nAlias; /* Number of aliased result set columns */
- int nAliasAlloc; /* Number of allocated slots for aAlias[] */
- int *aAlias; /* Register used to hold aliased result */
- u8 explain; /* True if the EXPLAIN flag is found on the query */
- Token sNameToken; /* Token with unqualified schema object name */
- Token sLastToken; /* The last token parsed */
- const char *zTail; /* All SQL text past the last semicolon parsed */
- Table *pNewTable; /* A table being constructed by CREATE TABLE */
- Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
- const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
+
+ /************************************************************************
+ ** Above is constant between recursions. Below is reset before and after
+ ** each recursion. The boundary between these two regions is determined
+ ** using offsetof(Parse,nVar) so the nVar field must be the first field
+ ** in the recursive region.
+ ************************************************************************/
+
+ int nVar; /* Number of '?' variables seen in the SQL so far */
+ int nzVar; /* Number of available slots in azVar[] */
+ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
+ u8 bFreeWith; /* True if pWith should be freed with parser */
+ u8 explain; /* True if the EXPLAIN flag is found on the query */
#ifndef SQLITE_OMIT_VIRTUALTABLE
- Token sArg; /* Complete text of a module argument */
- u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
- int nVtabLock; /* Number of virtual tables to lock */
- Table **apVtabLock; /* Pointer to virtual tables needing locking */
+ u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
+ int nVtabLock; /* Number of virtual tables to lock */
#endif
- int nHeight; /* Expression tree height of current sub-select */
- Table *pZombieTab; /* List of Table objects to delete after code gen */
- TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
-
+ int nAlias; /* Number of aliased result set columns */
+ int nHeight; /* Expression tree height of current sub-select */
#ifndef SQLITE_OMIT_EXPLAIN
- int iSelectId;
- int iNextSelectId;
+ int iSelectId; /* ID of current select for EXPLAIN output */
+ int iNextSelectId; /* Next available select ID for EXPLAIN output */
#endif
+ char **azVar; /* Pointers to names of parameters */
+ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
+ const char *zTail; /* All SQL text past the last semicolon parsed */
+ Table *pNewTable; /* A table being constructed by CREATE TABLE */
+ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
+ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
+ Token sNameToken; /* Token with unqualified schema object name */
+ Token sLastToken; /* The last token parsed */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ Token sArg; /* Complete text of a module argument */
+ Table **apVtabLock; /* Pointer to virtual tables needing locking */
+#endif
+ Table *pZombieTab; /* List of Table objects to delete after code gen */
+ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
+ With *pWith; /* Current WITH clause, or NULL */
};
+/*
+** Return true if currently inside an sqlite3_declare_vtab() call.
+*/
#ifdef SQLITE_OMIT_VIRTUALTABLE
#define IN_DECLARE_VTAB 0
#else
@@ -10880,14 +12182,19 @@ struct AuthContext {
};
/*
-** Bitfield flags for P5 value in OP_Insert and OP_Delete
+** Bitfield flags for P5 value in various opcodes.
*/
#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
+#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
-#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
+#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
+#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
+#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
+#define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */
+#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
/*
* Each trigger present in the database schema is stored as an instance of
@@ -10973,7 +12280,7 @@ struct TriggerStep {
Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */
Token target; /* Target table for DELETE, UPDATE, INSERT */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */
+ ExprList *pExprList; /* SET clause for UPDATE. */
IdList *pIdList; /* Column names for INSERT */
TriggerStep *pNext; /* Next in the link-list */
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
@@ -10987,6 +12294,8 @@ struct TriggerStep {
typedef struct DbFixer DbFixer;
struct DbFixer {
Parse *pParse; /* The parsing context. Error messages written here */
+ Schema *pSchema; /* Fix items to this schema */
+ int bVarOnly; /* Check for variable references only */
const char *zDb; /* Make sure all objects are contained in this database */
const char *zType; /* Type of the container - used for error messages */
const Token *pName; /* Name of the container - used for error messages */
@@ -11003,10 +12312,11 @@ struct StrAccum {
int nChar; /* Length of the string so far */
int nAlloc; /* Amount of space allocated in zText */
int mxAlloc; /* Maximum allowed string length */
- u8 mallocFailed; /* Becomes true if any memory allocation fails */
u8 useMalloc; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */
- u8 tooBig; /* Becomes true if string size exceeds limits */
+ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
};
+#define STRACCUM_NOMEM 1
+#define STRACCUM_TOOBIG 2
/*
** A pointer to this structure is used to communicate information
@@ -11014,8 +12324,8 @@ struct StrAccum {
*/
typedef struct {
sqlite3 *db; /* The database being initialized */
- int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
char **pzErrMsg; /* Error message stored here */
+ int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
int rc; /* Result code stored here */
} InitData;
@@ -11029,15 +12339,19 @@ struct Sqlite3Config {
int bCoreMutex; /* True to enable core mutexing */
int bFullMutex; /* True to enable full mutexing */
int bOpenUri; /* True to interpret filenames as URIs */
+ int bUseCis; /* Use covering indices for full-scans */
int mxStrlen; /* Maximum string length */
+ int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
int nLookaside; /* Default lookaside buffer count */
sqlite3_mem_methods m; /* Low-level memory allocation interface */
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
- sqlite3_pcache_methods pcache; /* Low-level page-cache interface */
+ sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */
void *pHeap; /* Heap storage space */
int nHeap; /* Size of pHeap[] */
int mnReq, mxReq; /* Min and max heap requests sizes */
+ sqlite3_int64 szMmap; /* mmap() space per open file */
+ sqlite3_int64 mxMmap; /* Maximum value for szMmap */
void *pScratch; /* Scratch memory */
int szScratch; /* Size of each scratch buffer */
int nScratch; /* Number of scratch buffers */
@@ -11053,23 +12367,59 @@ struct Sqlite3Config {
int isMutexInit; /* True after mutexes are initialized */
int isMallocInit; /* True after malloc is initialized */
int isPCacheInit; /* True after malloc is initialized */
- sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
int nRefInitMutex; /* Number of users of pInitMutex */
+ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
void (*xLog)(void*,int,const char*); /* Function for logging */
void *pLogArg; /* First argument to xLog() */
+#ifdef SQLITE_ENABLE_SQLLOG
+ void(*xSqllog)(void*,sqlite3*,const char*, int);
+ void *pSqllogArg;
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ /* The following callback (if not NULL) is invoked on every VDBE branch
+ ** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE.
+ */
+ void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */
+ void *pVdbeBranchArg; /* 1st argument */
+#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
};
/*
+** This macro is used inside of assert() statements to indicate that
+** the assert is only valid on a well-formed database. Instead of:
+**
+** assert( X );
+**
+** One writes:
+**
+** assert( X || CORRUPT_DB );
+**
+** CORRUPT_DB is true during normal operation. CORRUPT_DB does not indicate
+** that the database is definitely corrupt, only that it might be corrupt.
+** For most test cases, CORRUPT_DB is set to false using a special
+** sqlite3_test_control(). This enables assert() statements to prove
+** things that are always true for well-formed databases.
+*/
+#define CORRUPT_DB (sqlite3Config.neverCorrupt==0)
+
+/*
** Context pointer passed down through the tree-walk.
*/
struct Walker {
int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
+ void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */
Parse *pParse; /* Parser context. */
+ int walkerDepth; /* Number of subqueries */
union { /* Extra data for callback */
NameContext *pNC; /* Naming context */
int i; /* Integer value */
+ SrcList *pSrcList; /* FROM clause */
+ struct SrcCount *pSrcCount; /* Counting column references */
} u;
};
@@ -11089,6 +12439,32 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
#define WRC_Abort 2 /* Abandon the tree walk */
/*
+** An instance of this structure represents a set of one or more CTEs
+** (common table expressions) created by a single WITH clause.
+*/
+struct With {
+ int nCte; /* Number of CTEs in the WITH clause */
+ With *pOuter; /* Containing WITH clause, or NULL */
+ struct Cte { /* For each CTE in the WITH clause.... */
+ char *zName; /* Name of this CTE */
+ ExprList *pCols; /* List of explicit column names, or NULL */
+ Select *pSelect; /* The definition of this CTE */
+ const char *zErr; /* Error message for circular references */
+ } a[1];
+};
+
+#ifdef SQLITE_DEBUG
+/*
+** An instance of the TreeView object is used for printing the content of
+** data structures on sqlite3DebugPrintf() using a tree-like view.
+*/
+struct TreeView {
+ int iLevel; /* Which level of the tree we are on */
+ u8 bLine[100]; /* Draw vertical in column i if bLine[i] is true */
+};
+#endif /* SQLITE_DEBUG */
+
+/*
** Assuming zIn points to the first byte of a UTF-8 character,
** advance zIn to point to the first byte of the next UTF-8 character.
*/
@@ -11115,8 +12491,8 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
/*
** FTS4 is really an extension for FTS3. It is enabled using the
-** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all
-** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3.
+** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call
+** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3.
*/
#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3)
# define SQLITE_ENABLE_FTS3
@@ -11153,25 +12529,26 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
# define sqlite3Tolower(x) tolower((unsigned char)(x))
#endif
+SQLITE_PRIVATE int sqlite3IsIdChar(u8);
/*
** Internal function prototypes
*/
-SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *);
+#define sqlite3StrICmp sqlite3_stricmp
SQLITE_PRIVATE int sqlite3Strlen30(const char*);
#define sqlite3StrNICmp sqlite3_strnicmp
SQLITE_PRIVATE int sqlite3MallocInit(void);
SQLITE_PRIVATE void sqlite3MallocEnd(void);
-SQLITE_PRIVATE void *sqlite3Malloc(int);
-SQLITE_PRIVATE void *sqlite3MallocZero(int);
-SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, int);
-SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, int);
+SQLITE_PRIVATE void *sqlite3Malloc(u64);
+SQLITE_PRIVATE void *sqlite3MallocZero(u64);
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, u64);
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, u64);
SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3*,const char*);
-SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, int);
-SQLITE_PRIVATE void *sqlite3Realloc(void*, int);
-SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, int);
-SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, int);
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
+SQLITE_PRIVATE void *sqlite3Realloc(void*, u64);
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64);
SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*);
SQLITE_PRIVATE int sqlite3MallocSize(void*);
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*);
@@ -11227,10 +12604,20 @@ SQLITE_PRIVATE int sqlite3IsNaN(double);
# define sqlite3IsNaN(X) 0
#endif
-SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, int, const char*, va_list);
-#ifndef SQLITE_OMIT_TRACE
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, const char*, ...);
-#endif
+/*
+** An instance of the following structure holds information about SQL
+** functions arguments that are the parameters to the printf() function.
+*/
+struct PrintfArguments {
+ int nArg; /* Total number of arguments */
+ int nUsed; /* Number of arguments used so far */
+ sqlite3_value **apArg; /* The argument values */
+};
+
+#define SQLITE_PRINTF_INTERNAL 0x01
+#define SQLITE_PRINTF_SQLFUNC 0x02
+SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list);
+SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, u32, const char*, ...);
SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...);
SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
@@ -11240,6 +12627,18 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...);
#if defined(SQLITE_TEST)
SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*);
#endif
+
+#if defined(SQLITE_DEBUG)
+SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView*,u8);
+SQLITE_PRIVATE void sqlite3TreeViewPop(TreeView*);
+SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView*, const char*, ...);
+SQLITE_PRIVATE void sqlite3TreeViewItem(TreeView*, const char*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
+#endif
+
+
SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*, ...);
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...);
SQLITE_PRIVATE int sqlite3Dequote(char*);
@@ -11250,6 +12649,7 @@ SQLITE_PRIVATE int sqlite3GetTempReg(Parse*);
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int);
SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int);
SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int);
+SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*);
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
@@ -11265,11 +12665,15 @@ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
-SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3*, int);
+SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*);
+SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int);
+SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
+SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
+SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16);
SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*);
SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int);
@@ -11278,9 +12682,17 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3AddColumnType(Parse*,Token*);
SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*);
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
-SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*);
+SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
+SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
+SQLITE_PRIVATE int sqlite3CodeOnce(Parse *);
+
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+# define sqlite3FaultSim(X) SQLITE_OK
+#else
+SQLITE_PRIVATE int sqlite3FaultSim(int);
+#endif
SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32);
SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32);
@@ -11293,7 +12705,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int,int*);
SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
SQLITE_PRIVATE void sqlite3RowSetClear(RowSet*);
SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet*, i64);
-SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, u8 iBatch, i64);
+SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, int iBatch, i64);
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet*, i64*);
SQLITE_PRIVATE void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
@@ -11304,7 +12716,11 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse*,Table*);
# define sqlite3ViewGetColumnNames(A,B) 0
#endif
+#if SQLITE_MAX_ATTACHED>30
+SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask);
+#endif
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
+SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse);
@@ -11313,8 +12729,8 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse);
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
#endif
-SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
-SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*);
+SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
+SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*);
SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
@@ -11327,58 +12743,70 @@ SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*);
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
+SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
- Token*, int, int);
+ Expr*, int, int);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
- Expr*,ExprList*,int,Expr*,Expr*);
+ Expr*,ExprList*,u16,Expr*,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
-SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *);
+SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16);
+SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
-SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
+SQLITE_PRIVATE u64 sqlite3WhereOutputRowCount(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*);
+SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int);
-SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*);
-SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int);
+SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*);
SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int);
SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*);
SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int);
-SQLITE_PRIVATE int sqlite3ExprCode(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8);
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse*, Expr*, int);
-SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse*, Expr*);
-SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int);
+SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse*, Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8);
+#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
+#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*);
SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*);
+SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,int isView,struct SrcList_item *);
SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3Vacuum(Parse*);
SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*);
SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*);
-SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*);
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*);
+SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int);
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Expr*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
+SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
SQLITE_PRIVATE void sqlite3PrngSaveState(void);
SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
-SQLITE_PRIVATE void sqlite3PrngResetState(void);
-SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*);
+SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int);
SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int);
SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int);
@@ -11386,32 +12814,40 @@ SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse*);
SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*);
SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *);
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
-SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*);
+SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
-SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
-SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int);
-SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
-SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
-SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
- int*,int,int,int,int,int*);
-SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
-SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
+SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8);
+SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
+SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
+SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse*,int);
+SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
+ u8,u8,int,int*);
+SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
+SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*);
SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int);
SQLITE_PRIVATE void sqlite3MultiWrite(Parse*);
SQLITE_PRIVATE void sqlite3MayAbort(Parse*);
-SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, char*, int);
+SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8);
+SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*);
+SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*);
SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*);
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int);
+#if SELECTTRACE_ENABLED
+SQLITE_PRIVATE void sqlite3SelectSetName(Select*,const char*);
+#else
+# define sqlite3SelectSetName(A,B)
+#endif
SQLITE_PRIVATE void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
-SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
+SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,u8);
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3*);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void);
@@ -11438,7 +12874,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, i
SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
- ExprList*,Select*,u8);
+ Select*,u8);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*);
@@ -11474,7 +12910,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(Parse*, const char *, const char *, int)
#endif
SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*);
-SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
+SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*);
SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*);
SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*);
@@ -11485,61 +12921,63 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*);
SQLITE_PRIVATE int sqlite3Atoi(const char*);
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
-SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8*, const u8**);
+SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
+#endif
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
/*
** Routines to read and write variable-length integers. These used to
** be defined locally, but now we use the varint routines in the util.c
-** file. Code should use the MACRO forms below, as the Varint32 versions
-** are coded to assume the single byte case is already handled (which
-** the MACRO form does).
+** file.
*/
SQLITE_PRIVATE int sqlite3PutVarint(unsigned char*, u64);
-SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char*, u32);
SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *, u64 *);
SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *, u32 *);
SQLITE_PRIVATE int sqlite3VarintLen(u64 v);
/*
-** The header of a record consists of a sequence variable-length integers.
-** These integers are almost always small and are encoded as a single byte.
-** The following macros take advantage this fact to provide a fast encode
-** and decode of the integers in a record header. It is faster for the common
-** case where the integer is a single byte. It is a little slower when the
-** integer is two or more bytes. But overall it is faster.
-**
-** The following expressions are equivalent:
-**
-** x = sqlite3GetVarint32( A, &B );
-** x = sqlite3PutVarint32( A, B );
-**
-** x = getVarint32( A, B );
-** x = putVarint32( A, B );
-**
+** The common case is for a varint to be a single byte. They following
+** macros handle the common case without a procedure call, but then call
+** the procedure for larger varints.
*/
-#define getVarint32(A,B) (u8)((*(A)<(u8)0x80) ? ((B) = (u32)*(A)),1 : sqlite3GetVarint32((A), (u32 *)&(B)))
-#define putVarint32(A,B) (u8)(((u32)(B)<(u32)0x80) ? (*(A) = (unsigned char)(B)),1 : sqlite3PutVarint32((A), (B)))
+#define getVarint32(A,B) \
+ (u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite3GetVarint32((A),(u32 *)&(B)))
+#define putVarint32(A,B) \
+ (u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1:\
+ sqlite3PutVarint((A),(B)))
#define getVarint sqlite3GetVarint
#define putVarint sqlite3PutVarint
SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *);
-SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *);
+SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int);
SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2);
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
-SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...);
+SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
+SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
+SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
+
+#if defined(SQLITE_TEST)
+SQLITE_PRIVATE const char *sqlite3ErrName(int);
+#endif
+
SQLITE_PRIVATE const char *sqlite3ErrStr(int);
SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse);
SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
-SQLITE_PRIVATE Expr *sqlite3ExprSetColl(Expr*, CollSeq*);
-SQLITE_PRIVATE Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
@@ -11552,18 +12990,16 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
-SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z);
+SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8);
SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8);
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8);
SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
void(*)(void*));
+SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value*);
SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*);
SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *);
SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
-#ifdef SQLITE_ENABLE_STAT2
-SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
-#endif
SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
#ifndef SQLITE_AMALGAMATION
@@ -11586,14 +13022,16 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
-SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(sqlite3*, u8, CollSeq *, const char*);
-SQLITE_PRIVATE char sqlite3AffinityType(const char*);
+SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
+SQLITE_PRIVATE char sqlite3AffinityType(const char*, u8*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
@@ -11607,7 +13045,13 @@ SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int);
SQLITE_PRIVATE void sqlite3SchemaClear(void *);
SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
-SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
+SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
+#endif
SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
@@ -11618,6 +13062,8 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int);
SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*);
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum*,int,char);
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*);
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int);
@@ -11626,10 +13072,18 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *);
SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
+SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
+SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord*);
+SQLITE_PRIVATE int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
+#endif
+
/*
** The interface to the LEMON-generated parser
*/
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(size_t));
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(u64));
SQLITE_PRIVATE void sqlite3ParserFree(void*, void(*)(void*));
SQLITE_PRIVATE void sqlite3Parser(void*, int, Token, Parse*);
#ifdef YYTRACKMAXSTACKDEPTH
@@ -11663,19 +13117,23 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
# define sqlite3VtabUnlock(X)
# define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
+# define sqlite3GetVTable(X,Y) ((VTable*)0)
#else
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*);
-SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **);
+SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
+SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, Vdbe*);
SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
+SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
+SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*);
-SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*);
+SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int);
SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse*, Token*);
SQLITE_PRIVATE void sqlite3VtabArgInit(Parse*);
SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse*, Token*);
@@ -11685,16 +13143,27 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
+SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
+SQLITE_PRIVATE void sqlite3ParserReset(Parse*);
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*);
-SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*);
SQLITE_PRIVATE const char *sqlite3JournalModename(int);
-SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
-SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
+SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
+#endif
+#ifndef SQLITE_OMIT_CTE
+SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
+SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*);
+SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8);
+#else
+#define sqlite3WithPush(x,y,z)
+#define sqlite3WithDelete(x,y)
+#endif
/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
@@ -11704,23 +13173,25 @@ SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
** provided (enforcement of FK constraints requires the triggers sub-system).
*/
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int);
+SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
SQLITE_PRIVATE void sqlite3FkDropTable(Parse*, SrcList *, Table*);
-SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int);
+SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int);
SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*);
SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *);
#else
- #define sqlite3FkActions(a,b,c,d)
- #define sqlite3FkCheck(a,b,c,d)
+ #define sqlite3FkActions(a,b,c,d,e,f)
+ #define sqlite3FkCheck(a,b,c,d,e,f)
#define sqlite3FkDropTable(a,b,c)
- #define sqlite3FkOldmask(a,b) 0
- #define sqlite3FkRequired(a,b,c,d) 0
+ #define sqlite3FkOldmask(a,b) 0
+ #define sqlite3FkRequired(a,b,c,d) 0
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
+SQLITE_PRIVATE int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**);
#else
#define sqlite3FkDelete(a,b)
+ #define sqlite3FkLocateIndex(a,b,c,d,e)
#endif
@@ -11743,17 +13214,30 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void);
#define sqlite3EndBenignMalloc()
#endif
-#define IN_INDEX_ROWID 1
-#define IN_INDEX_EPH 2
-#define IN_INDEX_INDEX 3
-SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, int*);
+/*
+** Allowed return values from sqlite3FindInIndex()
+*/
+#define IN_INDEX_ROWID 1 /* Search the rowid of the table */
+#define IN_INDEX_EPH 2 /* Search an ephemeral b-tree */
+#define IN_INDEX_INDEX_ASC 3 /* Existing index ASCENDING */
+#define IN_INDEX_INDEX_DESC 4 /* Existing index DESCENDING */
+#define IN_INDEX_NOOP 5 /* No table available. Use comparisons */
+/*
+** Allowed flags for the 3rd parameter to sqlite3FindInIndex().
+*/
+#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
+#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
+#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
+SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *);
+SQLITE_PRIVATE int sqlite3JournalExists(sqlite3_file *p);
#else
#define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
+ #define sqlite3JournalExists(p) 1
#endif
SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *);
@@ -11839,10 +13323,17 @@ SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8);
# define sqlite3MemdebugNoType(X,Y) 1
#endif
#define MEMTYPE_HEAP 0x01 /* General heap allocations */
-#define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */
+#define MEMTYPE_LOOKASIDE 0x02 /* Heap that might have been lookaside */
#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */
#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
-#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */
+
+/*
+** Threading interface
+*/
+#if SQLITE_MAX_WORKER_THREADS>0
+SQLITE_PRIVATE int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread*, void**);
+#endif
#endif /* _SQLITEINT_H_ */
@@ -11860,7 +13351,7 @@ SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8);
**
*************************************************************************
**
-** This file contains definitions of global variables and contants.
+** This file contains definitions of global variables and constants.
*/
/* An array to map all upper-case characters into their corresponding
@@ -11978,10 +13469,21 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
};
#endif
+/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards
+** compatibility for legacy applications, the URI filename capability is
+** disabled by default.
+**
+** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled
+** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options.
+*/
#ifndef SQLITE_USE_URI
# define SQLITE_USE_URI 0
#endif
+#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN
+# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
+#endif
+
/*
** The following singleton contains the global configuration for
** the SQLite library.
@@ -11991,15 +13493,19 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
1, /* bCoreMutex */
SQLITE_THREADSAFE==1, /* bFullMutex */
SQLITE_USE_URI, /* bOpenUri */
+ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0x7ffffffe, /* mxStrlen */
- 100, /* szLookaside */
+ 0, /* neverCorrupt */
+ 128, /* szLookaside */
500, /* nLookaside */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
- {0,0,0,0,0,0,0,0,0,0,0}, /* pcache */
+ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
(void*)0, /* pHeap */
0, /* nHeap */
0, 0, /* mnHeap, mxHeap */
+ SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */
+ SQLITE_MAX_MMAP_SIZE, /* mxMmap */
(void*)0, /* pScratch */
0, /* szScratch */
0, /* nScratch */
@@ -12014,14 +13520,24 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* isMutexInit */
0, /* isMallocInit */
0, /* isPCacheInit */
- 0, /* pInitMutex */
0, /* nRefInitMutex */
+ 0, /* pInitMutex */
0, /* xLog */
0, /* pLogArg */
- 0, /* bLocaltimeFault */
+#ifdef SQLITE_ENABLE_SQLLOG
+ 0, /* xSqllog */
+ 0, /* pSqllogArg */
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ 0, /* xVdbeBranch */
+ 0, /* pVbeBranchArg */
+#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ 0, /* xTestCallback */
+#endif
+ 0 /* bLocaltimeFault */
};
-
/*
** Hash table for global functions - functions common to all
** database connections. After initialization, this table is
@@ -12125,6 +13641,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_DEFAULT_LOCKING_MODE
"DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
#endif
+#if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc)
+ "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE),
+#endif
#ifdef SQLITE_DISABLE_DIRSYNC
"DISABLE_DIRSYNC",
#endif
@@ -12185,8 +13704,10 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_ENABLE_RTREE
"ENABLE_RTREE",
#endif
-#ifdef SQLITE_ENABLE_STAT2
- "ENABLE_STAT2",
+#if defined(SQLITE_ENABLE_STAT4)
+ "ENABLE_STAT4",
+#elif defined(SQLITE_ENABLE_STAT3)
+ "ENABLE_STAT3",
#endif
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
"ENABLE_UNLOCK_NOTIFY",
@@ -12215,6 +13736,12 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_LOCK_TRACE
"LOCK_TRACE",
#endif
+#if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc)
+ "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE),
+#endif
+#ifdef SQLITE_MAX_SCHEMA_RETRY
+ "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
+#endif
#ifdef SQLITE_MEMDEBUG
"MEMDEBUG",
#endif
@@ -12269,17 +13796,15 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_OMIT_CHECK
"OMIT_CHECK",
#endif
-/* // redundant
-** #ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS
-** "OMIT_COMPILEOPTION_DIAGS",
-** #endif
-*/
#ifdef SQLITE_OMIT_COMPLETE
"OMIT_COMPLETE",
#endif
#ifdef SQLITE_OMIT_COMPOUND_SELECT
"OMIT_COMPOUND_SELECT",
#endif
+#ifdef SQLITE_OMIT_CTE
+ "OMIT_CTE",
+#endif
#ifdef SQLITE_OMIT_DATETIME_FUNCS
"OMIT_DATETIME_FUNCS",
#endif
@@ -12400,6 +13925,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_PROXY_DEBUG
"PROXY_DEBUG",
#endif
+#ifdef SQLITE_RTREE_INT_ONLY
+ "RTREE_INT_ONLY",
+#endif
#ifdef SQLITE_SECURE_DELETE
"SECURE_DELETE",
#endif
@@ -12409,21 +13937,30 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_SOUNDEX
"SOUNDEX",
#endif
+#ifdef SQLITE_SYSTEM_MALLOC
+ "SYSTEM_MALLOC",
+#endif
#ifdef SQLITE_TCL
"TCL",
#endif
-#ifdef SQLITE_TEMP_STORE
+#if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc)
"TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE),
#endif
#ifdef SQLITE_TEST
"TEST",
#endif
-#ifdef SQLITE_THREADSAFE
+#if defined(SQLITE_THREADSAFE)
"THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE),
#endif
#ifdef SQLITE_USE_ALLOCA
"USE_ALLOCA",
#endif
+#ifdef SQLITE_USER_AUTHENTICATION
+ "USER_AUTHENTICATION",
+#endif
+#ifdef SQLITE_WIN32_MALLOC
+ "WIN32_MALLOC",
+#endif
#ifdef SQLITE_ZERO_MALLOC
"ZERO_MALLOC"
#endif
@@ -12444,8 +13981,11 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){
/* Since ArraySize(azCompileOpt) is normally in single digits, a
** linear search is adequate. No need for a binary search. */
for(i=0; i<ArraySize(azCompileOpt); i++){
- if( (sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0)
- && ( (azCompileOpt[i][n]==0) || (azCompileOpt[i][n]=='=') ) ) return 1;
+ if( sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0
+ && sqlite3IsIdChar((unsigned char)azCompileOpt[i][n])==0
+ ){
+ return 1;
+ }
}
return 0;
}
@@ -12503,6 +14043,14 @@ SQLITE_API const char *sqlite3_compileoption_get(int N){
#define _VDBEINT_H_
/*
+** The maximum number of times that a statement will try to reparse
+** itself before giving up and returning SQLITE_SCHEMA.
+*/
+#ifndef SQLITE_MAX_SCHEMA_RETRY
+# define SQLITE_MAX_SCHEMA_RETRY 50
+#endif
+
+/*
** SQL is translated into a sequence of instructions to be
** executed by a virtual machine. Each instruction is an instance
** of the following structure.
@@ -12512,7 +14060,16 @@ typedef struct VdbeOp Op;
/*
** Boolean values
*/
-typedef unsigned char Bool;
+typedef unsigned Bool;
+
+/* Opaque type used by code in vdbesort.c */
+typedef struct VdbeSorter VdbeSorter;
+
+/* Opaque type used by the explainer */
+typedef struct Explain Explain;
+
+/* Elements of the linked list at Vdbe.pAuxData */
+typedef struct AuxData AuxData;
/*
** A cursor is a pointer into a single BTree within a database file.
@@ -12520,6 +14077,9 @@ typedef unsigned char Bool;
** loop over all entries of the Btree. You can also insert new BTree
** entries or retrieve the key or data from the entry that the cursor
** is currently pointing to.
+**
+** Cursors can also point to virtual tables, sorters, or "pseudo-tables".
+** A pseudo-table is a single-row table implemented by registers.
**
** Every cursor that the virtual machine has open is represented by an
** instance of the following structure.
@@ -12528,27 +14088,25 @@ struct VdbeCursor {
BtCursor *pCursor; /* The cursor structure of the backend */
Btree *pBt; /* Separate file holding temporary table */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
- int iDb; /* Index of cursor database in db->aDb[] (or -1) */
+ int seekResult; /* Result of previous sqlite3BtreeMoveto() */
int pseudoTableReg; /* Register holding pseudotable content. */
- int nField; /* Number of fields in the header */
- Bool zeroed; /* True if zeroed out and ready for reuse */
- Bool rowidIsValid; /* True if lastRowid is valid */
- Bool atFirst; /* True if pointing to first entry */
- Bool useRandomRowid; /* Generate new record numbers semi-randomly */
- Bool nullRow; /* True if pointing to a row with no data */
- Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
- Bool isTable; /* True if a table requiring integer keys */
- Bool isIndex; /* True if an index containing keys only - no data */
- Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
+ i16 nField; /* Number of fields in the header */
+ u16 nHdrParsed; /* Number of header fields parsed so far */
+#ifdef SQLITE_DEBUG
+ u8 seekOp; /* Most recent seek operation on this cursor */
+#endif
+ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
+ u8 nullRow; /* True if pointing to a row with no data */
+ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
+ Bool isEphemeral:1; /* True for an ephemeral table */
+ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
+ Bool isTable:1; /* True if a table requiring integer keys */
+ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */
+ Pgno pgnoRoot; /* Root page of the open btree cursor */
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
- const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
- i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
-
- /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
- ** OP_IsUnique opcode on this cursor. */
- int seekResult;
+ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
/* Cached information about the header for the data record that the
** cursor is currently pointing to. Only valid if cacheStatus matches
@@ -12560,10 +14118,15 @@ struct VdbeCursor {
** be NULL.
*/
u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
- int payloadSize; /* Total number of bytes in the record */
- u32 *aType; /* Type values for all entries in the record */
- u32 *aOffset; /* Cached offsets to the start of each columns data */
- u8 *aRow; /* Data for the current row, if all on one page */
+ u32 payloadSize; /* Total number of bytes in the record */
+ u32 szRow; /* Byte available in aRow */
+ u32 iHdrOffset; /* Offset to next unparsed byte of the header */
+ const u8 *aRow; /* Data for the current row, if all on one page */
+ u32 *aOffset; /* Pointer to aType[nField] */
+ u32 aType[1]; /* Type values for all entries in the record */
+ /* 2*nField extra array elements allocated for aType[], beyond the one
+ ** static element declared in the structure. nField total array slots for
+ ** aType[] and nField+1 array slots for aOffset[] */
};
typedef struct VdbeCursor VdbeCursor;
@@ -12591,19 +14154,21 @@ typedef struct VdbeCursor VdbeCursor;
typedef struct VdbeFrame VdbeFrame;
struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */
- int pc; /* Program Counter in parent (calling) frame */
+ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
Op *aOp; /* Program instructions for parent frame */
- int nOp; /* Size of aOp array */
Mem *aMem; /* Array of memory cells for parent frame */
- int nMem; /* Number of entries in aMem */
+ u8 *aOnceFlag; /* Array of OP_Once flags for parent frame */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
- u16 nCursor; /* Number of entries in apCsr */
void *token; /* Copy of SubProgram.token */
+ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
+ int nCursor; /* Number of entries in apCsr */
+ int pc; /* Program Counter in parent (calling) frame */
+ int nOp; /* Size of aOp array */
+ int nMem; /* Number of entries in aMem */
+ int nOnceFlag; /* Number of entries in aOnceFlag */
int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */
- i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
int nChange; /* Statement changes (Vdbe.nChanges) */
- VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
};
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
@@ -12619,26 +14184,28 @@ struct VdbeFrame {
** integer etc.) of the same value.
*/
struct Mem {
- sqlite3 *db; /* The associated database connection */
- char *z; /* String or BLOB value */
- double r; /* Real value */
- union {
+ union MemValue {
+ double r; /* Real value used when MEM_Real is set in flags */
i64 i; /* Integer value used when MEM_Int is set in flags */
int nZero; /* Used when bit MEM_Zero is set in flags */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
} u;
- int n; /* Number of characters in string value, excluding '\0' */
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
- u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
+ int n; /* Number of characters in string value, excluding '\0' */
+ char *z; /* String or BLOB value */
+ /* ShallowCopy only needs to copy the information above */
+ char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
+ int szMalloc; /* Size of the zMalloc allocation */
+ u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
+ sqlite3 *db; /* The associated database connection */
+ void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
#ifdef SQLITE_DEBUG
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
#endif
- void (*xDel)(void *); /* If not null, call this function to delete Mem.z */
- char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */
};
/* One or more of the following flags are set to indicate the validOK
@@ -12658,10 +14225,13 @@ struct Mem {
#define MEM_Int 0x0004 /* Value is an integer */
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
+#define MEM_AffMask 0x001f /* Mask of affinity bits */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
-#define MEM_Invalid 0x0080 /* Value is undefined */
-#define MEM_TypeMask 0x00ff /* Mask of type bits */
+#define MEM_Undefined 0x0080 /* Value is undefined */
+#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
+#define MEM_TypeMask 0x01ff /* Mask of type bits */
+
/* Whenever Mem contains a valid string or blob representation, one of
** the following flags must be set to determine the memory management
@@ -12669,7 +14239,7 @@ struct Mem {
** string is \000 or \u0000 terminated
*/
#define MEM_Term 0x0200 /* String rep is nul terminated */
-#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */
+#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */
#define MEM_Static 0x0800 /* Mem.z points to a static string */
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
@@ -12690,30 +14260,26 @@ struct Mem {
** is for use inside assert() statements only.
*/
#ifdef SQLITE_DEBUG
-#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
-#endif
-
-
-/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
-** additional information about auxiliary information bound to arguments
-** of the function. This is used to implement the sqlite3_get_auxdata()
-** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data
-** that can be associated with a constant argument to a function. This
-** allows functions such as "regexp" to compile their constant regular
-** expression argument once and reused the compiled code for multiple
-** invocations.
-*/
-struct VdbeFunc {
- FuncDef *pFunc; /* The definition of the function */
- int nAux; /* Number of entries allocated for apAux[] */
- struct AuxData {
- void *pAux; /* Aux data for the i-th argument */
- void (*xDelete)(void *); /* Destructor for the aux data */
- } apAux[1]; /* One slot for each function argument */
+#define memIsValid(M) ((M)->flags & MEM_Undefined)==0
+#endif
+
+/*
+** Each auxiliary data pointer stored by a user defined function
+** implementation calling sqlite3_set_auxdata() is stored in an instance
+** of this structure. All such structures associated with a single VM
+** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed
+** when the VM is halted (if not before).
+*/
+struct AuxData {
+ int iOp; /* Instruction number of OP_Function opcode */
+ int iArg; /* Index of function argument. */
+ void *pAux; /* Aux data pointer */
+ void (*xDelete)(void *); /* Destructor for the aux data */
+ AuxData *pNext; /* Next element in list */
};
/*
-** The "context" argument for a installable function. A pointer to an
+** The "context" argument for an installable function. A pointer to an
** instance of this structure is the first argument to the routines used
** implement the SQL functions.
**
@@ -12726,14 +14292,33 @@ struct VdbeFunc {
** (Mem) which are only defined there.
*/
struct sqlite3_context {
- FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */
- VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */
- Mem s; /* The return value is stored here */
+ Mem *pOut; /* The return value is stored here */
+ FuncDef *pFunc; /* Pointer to function information */
Mem *pMem; /* Memory cell used to store aggregate context */
+ Vdbe *pVdbe; /* The VM that owns this context */
+ int iOp; /* Instruction number of OP_Function */
int isError; /* Error code returned by the function. */
- CollSeq *pColl; /* Collating sequence */
+ u8 skipFlag; /* Skip accumulator loading if true */
+ u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
+};
+
+/*
+** An Explain object accumulates indented output which is helpful
+** in describing recursive data structures.
+*/
+struct Explain {
+ Vdbe *pVdbe; /* Attach the explanation to this Vdbe */
+ StrAccum str; /* The string being accumulated */
+ int nIndent; /* Number of elements in aIndent */
+ u16 aIndent[100]; /* Levels of indentation */
+ char zBase[100]; /* Initial space */
};
+/* A bitfield type for use inside of structures. Always follow with :N where
+** N is the number of bits.
+*/
+typedef unsigned bft; /* Bit Field Type */
+
/*
** An instance of the virtual machine. This structure contains the complete
** state of the virtual machine.
@@ -12756,14 +14341,10 @@ struct Vdbe {
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
Mem *pResultSet; /* Pointer to an array of results */
+ Parse *pParse; /* Parsing context used to create this Vdbe */
int nMem; /* Number of memory locations currently allocated */
int nOp; /* Number of instructions in the program */
- int nOpAlloc; /* Number of slots allocated for aOp[] */
- int nLabel; /* Number of labels used */
- int nLabelAlloc; /* Number of slots allocated in aLabel[] */
- int *aLabel; /* Space to hold the labels */
- u16 nResColumn; /* Number of columns in one row of the result set */
- u16 nCursor; /* Number of slots in apCsr[] */
+ int nCursor; /* Number of slots in apCsr[] */
u32 magic; /* Magic number for sanity checking */
char *zErrMsg; /* Error message written here */
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
@@ -12775,36 +14356,41 @@ struct Vdbe {
u32 cacheCtr; /* VdbeCursor row cache generation counter */
int pc; /* The program counter */
int rc; /* Value to return */
+ u16 nResColumn; /* Number of columns in one row of the result set */
u8 errorAction; /* Recovery action to do in case of an error */
- u8 explain; /* True if EXPLAIN present on SQL command */
- u8 changeCntOn; /* True to update the change-counter */
- u8 expired; /* True if the VM needs to be recompiled */
- u8 runOnlyOnce; /* Automatically expire on reset */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
- u8 inVtabMethod; /* See comments above */
- u8 usesStmtJournal; /* True if uses a statement journal */
- u8 readOnly; /* True for read-only statements */
- u8 isPrepareV2; /* True if prepared with prepare_v2() */
+ bft explain:2; /* True if EXPLAIN present on SQL command */
+ bft inVtabMethod:2; /* See comments above */
+ bft changeCntOn:1; /* True to update the change-counter */
+ bft expired:1; /* True if the VM needs to be recompiled */
+ bft runOnlyOnce:1; /* Automatically expire on reset */
+ bft usesStmtJournal:1; /* True if uses a statement journal */
+ bft readOnly:1; /* True for statements that do not write */
+ bft bIsReader:1; /* True for statements that read */
+ bft isPrepareV2:1; /* True if prepared with prepare_v2() */
+ bft doingRerun:1; /* True if rerunning after an auto-reprepare */
int nChange; /* Number of db changes made since last reset */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
int iStatement; /* Statement number (or 0 if has not opened stmt) */
- int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
+ u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif
+ i64 iCurrentTime; /* Value of julianday('now') for this statement */
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
+ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
-#ifdef SQLITE_DEBUG
- FILE *trace; /* Write an execution trace here, if not NULL */
-#endif
VdbeFrame *pFrame; /* Parent frame */
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
+ int nOnceFlag; /* Size of array aOnceFlag[] */
+ u8 *aOnceFlag; /* Flags for OP_Once */
+ AuxData *pAuxData; /* Linked list of auxdata allocations */
};
/*
@@ -12821,19 +14407,19 @@ struct Vdbe {
SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor*);
+SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int);
+SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
-SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*);
-SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *);
-SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
+SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
+SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
@@ -12850,27 +14436,40 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64);
#else
SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double);
#endif
+SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int);
SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*);
-SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, int);
+SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8);
SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*);
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*);
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*);
-SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
+SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem*,u8,u8);
+SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
-SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p);
+#define VdbeMemDynamic(X) \
+ (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
+SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
-SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem);
+SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
+
+SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
+SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
+SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
@@ -12881,7 +14480,8 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*);
#endif
#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
+SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
+SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
@@ -12899,8 +14499,10 @@ SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem);
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *);
+ #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
#else
#define sqlite3VdbeMemExpandBlob(x) SQLITE_OK
+ #define ExpandBlob(P) SQLITE_OK
#endif
#endif /* !defined(_VDBEINT_H_) */
@@ -13100,16 +14702,53 @@ SQLITE_API int sqlite3_db_status(
db->pnBytesFreed = &nByte;
for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){
- sqlite3VdbeDeleteObject(db, pVdbe);
+ sqlite3VdbeClearObject(db, pVdbe);
+ sqlite3DbFree(db, pVdbe);
}
db->pnBytesFreed = 0;
- *pHighwater = 0;
+ *pHighwater = 0; /* IMP: R-64479-57858 */
*pCurrent = nByte;
break;
}
+ /*
+ ** Set *pCurrent to the total cache hits or misses encountered by all
+ ** pagers the database handle is connected to. *pHighwater is always set
+ ** to zero.
+ */
+ case SQLITE_DBSTATUS_CACHE_HIT:
+ case SQLITE_DBSTATUS_CACHE_MISS:
+ case SQLITE_DBSTATUS_CACHE_WRITE:{
+ int i;
+ int nRet = 0;
+ assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
+ assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
+
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt ){
+ Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
+ sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
+ }
+ }
+ *pHighwater = 0; /* IMP: R-42420-56072 */
+ /* IMP: R-54100-20147 */
+ /* IMP: R-29431-39229 */
+ *pCurrent = nRet;
+ break;
+ }
+
+ /* Set *pCurrent to non-zero if there are unresolved deferred foreign
+ ** key constraints. Set *pCurrent to zero if all foreign key constraints
+ ** have been satisfied. The *pHighwater is always set to zero.
+ */
+ case SQLITE_DBSTATUS_DEFERRED_FKS: {
+ *pHighwater = 0; /* IMP: R-11967-56545 */
+ *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
}
@@ -13146,7 +14785,7 @@ SQLITE_API int sqlite3_db_status(
** 1970-01-01 00:00:00 is JD 2440587.5
** 2000-01-01 00:00:00 is JD 2451544.5
**
-** This implemention requires years to be expressed as a 4-digit number
+** This implementation requires years to be expressed as a 4-digit number
** which means that only dates between 0000-01-01 and 9999-12-31 can
** be represented, even though julian day numbers allow a much wider
** range of dates.
@@ -13165,6 +14804,8 @@ SQLITE_API int sqlite3_db_status(
** Willmann-Bell, Inc
** Richmond, Virginia (USA)
*/
+/* #include <stdlib.h> */
+/* #include <assert.h> */
#include <time.h>
#ifndef SQLITE_OMIT_DATETIME_FUNCS
@@ -13408,12 +15049,18 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
}
/*
-** Set the time to the current time reported by the VFS
+** Set the time to the current time reported by the VFS.
+**
+** Return the number of errors.
*/
-static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
- sqlite3 *db = sqlite3_context_db_handle(context);
- sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD);
- p->validJD = 1;
+static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
+ p->iJD = sqlite3StmtCurrentTime(context);
+ if( p->iJD>0 ){
+ p->validJD = 1;
+ return 0;
+ }else{
+ return 1;
+ }
}
/*
@@ -13443,8 +15090,7 @@ static int parseDateOrTime(
}else if( parseHhMmSs(zDate, p)==0 ){
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0){
- setDateTimeToCurrent(context, p);
- return 0;
+ return setDateTimeToCurrent(context, p);
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
p->validJD = 1;
@@ -13540,13 +15186,19 @@ static void clearYMD_HMS_TZ(DateTime *p){
**
** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
** routine will always fail.
+**
+** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
+** library function localtime_r() is used to assist in the calculation of
+** local time.
*/
static int osLocaltime(time_t *t, struct tm *pTm){
int rc;
#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \
&& (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S)
struct tm *pX;
+#if SQLITE_THREADSAFE>0
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
sqlite3_mutex_enter(mutex);
pX = localtime(t);
#ifndef SQLITE_OMIT_BUILTIN_TEST
@@ -13594,6 +15246,11 @@ static sqlite3_int64 localtimeOffset(
x = *p;
computeYMD_HMS(&x);
if( x.Y<1971 || x.Y>=2038 ){
+ /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
+ ** works for years between 1970 and 2037. For dates outside this range,
+ ** SQLite attempts to map the year into an equivalent year within this
+ ** range, do the calculation, then map the year back.
+ */
x.Y = 2000;
x.M = 1;
x.D = 1;
@@ -13869,8 +15526,9 @@ static int isDate(
int eType;
memset(p, 0, sizeof(*p));
if( argc==0 ){
- setDateTimeToCurrent(context, p);
- }else if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
+ return setDateTimeToCurrent(context, p);
+ }
+ if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|| eType==SQLITE_INTEGER ){
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
p->validJD = 1;
@@ -14182,31 +15840,28 @@ static void currentTimeFunc(
char *zFormat = (char *)sqlite3_user_data(context);
sqlite3 *db;
sqlite3_int64 iT;
+ struct tm *pTm;
+ struct tm sNow;
char zBuf[20];
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
- db = sqlite3_context_db_handle(context);
- sqlite3OsCurrentTimeInt64(db->pVfs, &iT);
+ iT = sqlite3StmtCurrentTime(context);
+ if( iT<=0 ) return;
t = iT/1000 - 10000*(sqlite3_int64)21086676;
#ifdef HAVE_GMTIME_R
- {
- struct tm sNow;
- gmtime_r(&t, &sNow);
- strftime(zBuf, 20, zFormat, &sNow);
- }
+ pTm = gmtime_r(&t, &sNow);
#else
- {
- struct tm *pTm;
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- pTm = gmtime(&t);
- strftime(zBuf, 20, zFormat, pTm);
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- }
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ pTm = gmtime(&t);
+ if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
#endif
-
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ if( pTm ){
+ strftime(zBuf, 20, zFormat, &sNow);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ }
}
#endif
@@ -14271,11 +15926,18 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
** The following functions are instrumented for malloc() failure
** testing:
**
-** sqlite3OsOpen()
** sqlite3OsRead()
** sqlite3OsWrite()
** sqlite3OsSync()
+** sqlite3OsFileSize()
** sqlite3OsLock()
+** sqlite3OsCheckReservedLock()
+** sqlite3OsFileControl()
+** sqlite3OsShmMap()
+** sqlite3OsOpen()
+** sqlite3OsDelete()
+** sqlite3OsAccess()
+** sqlite3OsFullPathname()
**
*/
#if defined(SQLITE_TEST)
@@ -14334,9 +15996,37 @@ SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
DO_OS_MALLOC_TEST(id);
return id->pMethods->xCheckReservedLock(id, pResOut);
}
+
+/*
+** Use sqlite3OsFileControl() when we are doing something that might fail
+** and we need to know about the failures. Use sqlite3OsFileControlHint()
+** when simply tossing information over the wall to the VFS and we do not
+** really care if the VFS receives and understands the information since it
+** is only a hint and can be safely ignored. The sqlite3OsFileControlHint()
+** routine has no return value since the return value would be meaningless.
+*/
SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
+#ifdef SQLITE_TEST
+ if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
+ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
+ ** is using a regular VFS, it is called after the corresponding
+ ** transaction has been committed. Injecting a fault at this point
+ ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
+ ** but the transaction is committed anyway.
+ **
+ ** The core must call OsFileControl() though, not OsFileControlHint(),
+ ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
+ ** means the commit really has failed and an error should be returned
+ ** to the user. */
+ DO_OS_MALLOC_TEST(id);
+ }
+#endif
return id->pMethods->xFileControl(id, op, pArg);
}
+SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
+ (void)id->pMethods->xFileControl(id, op, pArg);
+}
+
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
@@ -14360,9 +16050,30 @@ SQLITE_PRIVATE int sqlite3OsShmMap(
int bExtend, /* True to extend file if necessary */
void volatile **pp /* OUT: Pointer to mapping */
){
+ DO_OS_MALLOC_TEST(id);
return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp);
}
+#if SQLITE_MAX_MMAP_SIZE>0
+/* The real implementation of xFetch and xUnfetch */
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){
+ DO_OS_MALLOC_TEST(id);
+ return id->pMethods->xFetch(id, iOff, iAmt, pp);
+}
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
+ return id->pMethods->xUnfetch(id, iOff, p);
+}
+#else
+/* No-op stubs to use when memory-mapped I/O is disabled */
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){
+ *pp = 0;
+ return SQLITE_OK;
+}
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
+ return SQLITE_OK;
+}
+#endif
+
/*
** The next group of routines are convenience wrappers around the
** VFS methods.
@@ -14376,15 +16087,17 @@ SQLITE_PRIVATE int sqlite3OsOpen(
){
int rc;
DO_OS_MALLOC_TEST(0);
- /* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed
+ /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
** reaching the VFS. */
- rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f3f, pFlagsOut);
+ rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x87f7f, pFlagsOut);
assert( rc==SQLITE_OK || pFile->pMethods==0 );
return rc;
}
SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ DO_OS_MALLOC_TEST(0);
+ assert( dirSync==0 || dirSync==1 );
return pVfs->xDelete(pVfs, zPath, dirSync);
}
SQLITE_PRIVATE int sqlite3OsAccess(
@@ -14402,6 +16115,7 @@ SQLITE_PRIVATE int sqlite3OsFullPathname(
int nPathOut,
char *zPathOut
){
+ DO_OS_MALLOC_TEST(0);
zPathOut[0] = 0;
return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
}
@@ -14452,7 +16166,7 @@ SQLITE_PRIVATE int sqlite3OsOpenMalloc(
){
int rc = SQLITE_NOMEM;
sqlite3_file *pFile;
- pFile = (sqlite3_file *)sqlite3Malloc(pVfs->szOsFile);
+ pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
if( pFile ){
rc = sqlite3OsOpen(pVfs, zFile, pFile, flags, pOutFlags);
if( rc!=SQLITE_OK ){
@@ -14541,12 +16255,12 @@ static void vfsUnlink(sqlite3_vfs *pVfs){
** true.
*/
SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
- sqlite3_mutex *mutex = 0;
+ MUTEX_LOGIC(sqlite3_mutex *mutex;)
#ifndef SQLITE_OMIT_AUTOINIT
int rc = sqlite3_initialize();
if( rc ) return rc;
#endif
- mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
sqlite3_mutex_enter(mutex);
vfsUnlink(pVfs);
if( makeDflt || vfsList==0 ){
@@ -14743,7 +16457,31 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** to obtain the memory it needs.
**
** This file contains implementations of the low-level memory allocation
-** routines specified in the sqlite3_mem_methods object.
+** routines specified in the sqlite3_mem_methods object. The content of
+** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The
+** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the
+** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The
+** default configuration is to use memory allocation routines in this
+** file.
+**
+** C-preprocessor macro summary:
+**
+** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if
+** the malloc_usable_size() interface exists
+** on the target platform. Or, this symbol
+** can be set manually, if desired.
+** If an equivalent interface exists by
+** a different name, using a separate -D
+** option to rename it.
+**
+** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone
+** memory allocator. Set this symbol to enable
+** building on older macs.
+**
+** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of
+** _msize() on windows systems. This might
+** be necessary when compiling for Delphi,
+** for example.
*/
/*
@@ -14752,6 +16490,71 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** macros.
*/
#ifdef SQLITE_SYSTEM_MALLOC
+#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
+
+/*
+** Use the zone allocator available on apple products unless the
+** SQLITE_WITHOUT_ZONEMALLOC symbol is defined.
+*/
+#include <sys/sysctl.h>
+#include <malloc/malloc.h>
+#include <libkern/OSAtomic.h>
+static malloc_zone_t* _sqliteZone_;
+#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
+#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
+#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
+#define SQLITE_MALLOCSIZE(x) \
+ (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x))
+
+#else /* if not __APPLE__ */
+
+/*
+** Use standard C library malloc and free on non-Apple systems.
+** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
+*/
+#define SQLITE_MALLOC(x) malloc(x)
+#define SQLITE_FREE(x) free(x)
+#define SQLITE_REALLOC(x,y) realloc((x),(y))
+
+/*
+** The malloc.h header file is needed for malloc_usable_size() function
+** on some systems (e.g. Linux).
+*/
+#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE)
+# define SQLITE_USE_MALLOC_H
+# define SQLITE_USE_MALLOC_USABLE_SIZE
+/*
+** The MSVCRT has malloc_usable_size(), but it is called _msize(). The
+** use of _msize() is automatic, but can be disabled by compiling with
+** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
+** the malloc.h header file.
+*/
+#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
+# define SQLITE_USE_MALLOC_H
+# define SQLITE_USE_MSIZE
+#endif
+
+/*
+** Include the malloc.h header file, if necessary. Also set define macro
+** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize()
+** for MSVC and malloc_usable_size() for most other systems (e.g. Linux).
+** The memory size function can always be overridden manually by defining
+** the macro SQLITE_MALLOCSIZE to the desired function name.
+*/
+#if defined(SQLITE_USE_MALLOC_H)
+# include <malloc.h>
+# if defined(SQLITE_USE_MALLOC_USABLE_SIZE)
+# if !defined(SQLITE_MALLOCSIZE)
+# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
+# endif
+# elif defined(SQLITE_USE_MSIZE)
+# if !defined(SQLITE_MALLOCSIZE)
+# define SQLITE_MALLOCSIZE _msize
+# endif
+# endif
+#endif /* defined(SQLITE_USE_MALLOC_H) */
+
+#endif /* __APPLE__ or not __APPLE__ */
/*
** Like malloc(), but remember the size of the allocation
@@ -14762,10 +16565,18 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** routines.
*/
static void *sqlite3MemMalloc(int nByte){
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_MALLOC( nByte );
+ if( p==0 ){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
+ }
+ return p;
+#else
sqlite3_int64 *p;
assert( nByte>0 );
nByte = ROUND8(nByte);
- p = malloc( nByte+8 );
+ p = SQLITE_MALLOC( nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -14774,6 +16585,7 @@ static void *sqlite3MemMalloc(int nByte){
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
}
return (void *)p;
+#endif
}
/*
@@ -14785,10 +16597,14 @@ static void *sqlite3MemMalloc(int nByte){
** by higher-level routines.
*/
static void sqlite3MemFree(void *pPrior){
+#ifdef SQLITE_MALLOCSIZE
+ SQLITE_FREE(pPrior);
+#else
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 );
p--;
- free(p);
+ SQLITE_FREE(p);
+#endif
}
/*
@@ -14796,11 +16612,15 @@ static void sqlite3MemFree(void *pPrior){
** or xRealloc().
*/
static int sqlite3MemSize(void *pPrior){
+#ifdef SQLITE_MALLOCSIZE
+ return pPrior ? (int)SQLITE_MALLOCSIZE(pPrior) : 0;
+#else
sqlite3_int64 *p;
if( pPrior==0 ) return 0;
p = (sqlite3_int64*)pPrior;
p--;
return (int)p[0];
+#endif
}
/*
@@ -14809,16 +16629,26 @@ static int sqlite3MemSize(void *pPrior){
**
** For this low-level interface, we know that pPrior!=0. Cases where
** pPrior==0 while have been intercepted by higher-level routine and
-** redirected to xMalloc. Similarly, we know that nByte>0 becauses
+** redirected to xMalloc. Similarly, we know that nByte>0 because
** cases where nByte<=0 will have been intercepted by higher-level
** routines and redirected to xFree.
*/
static void *sqlite3MemRealloc(void *pPrior, int nByte){
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_REALLOC(pPrior, nByte);
+ if( p==0 ){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ sqlite3_log(SQLITE_NOMEM,
+ "failed memory resize %u to %u bytes",
+ SQLITE_MALLOCSIZE(pPrior), nByte);
+ }
+ return p;
+#else
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 && nByte>0 );
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
p--;
- p = realloc(p, nByte+8 );
+ p = SQLITE_REALLOC(p, nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -14829,6 +16659,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
sqlite3MemSize(pPrior), nByte);
}
return (void*)p;
+#endif
}
/*
@@ -14842,6 +16673,34 @@ static int sqlite3MemRoundup(int n){
** Initialize this module.
*/
static int sqlite3MemInit(void *NotUsed){
+#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
+ int cpuCount;
+ size_t len;
+ if( _sqliteZone_ ){
+ return SQLITE_OK;
+ }
+ len = sizeof(cpuCount);
+ /* One usually wants to use hw.acctivecpu for MT decisions, but not here */
+ sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
+ if( cpuCount>1 ){
+ /* defer MT decisions to system malloc */
+ _sqliteZone_ = malloc_default_zone();
+ }else{
+ /* only 1 core, use our own zone to contention over global locks,
+ ** e.g. we have our own dedicated locks */
+ bool success;
+ malloc_zone_t* newzone = malloc_create_zone(4096, 0);
+ malloc_set_zone_name(newzone, "Sqlite_Heap");
+ do{
+ success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
+ (void * volatile *)&_sqliteZone_);
+ }while(!_sqliteZone_);
+ if( !success ){
+ /* somebody registered a zone first */
+ malloc_destroy_zone(newzone);
+ }
+ }
+#endif
UNUSED_PARAMETER(NotUsed);
return SQLITE_OK;
}
@@ -14916,6 +16775,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
# define backtrace(A,B) 1
# define backtrace_symbols_fd(A,B,C)
#endif
+/* #include <stdio.h> */
/*
** Each memory allocation looks like this:
@@ -15057,7 +16917,7 @@ static int sqlite3MemSize(void *p){
return 0;
}
pHdr = sqlite3MemsysGetHeader(p);
- return pHdr->iSize;
+ return (int)pHdr->iSize;
}
/*
@@ -15099,7 +16959,7 @@ static void randomFill(char *pBuf, int nByte){
x = SQLITE_PTR_TO_INT(pBuf);
y = nByte | 1;
while( nByte >= 4 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(int*)pBuf = r;
@@ -15107,7 +16967,7 @@ static void randomFill(char *pBuf, int nByte){
nByte -= 4;
}
while( nByte-- > 0 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(pBuf++) = r & 0xff;
@@ -15202,9 +17062,9 @@ static void sqlite3MemFree(void *pPrior){
}
z = (char*)pBt;
z -= pHdr->nTitle;
- adjustStats(pHdr->iSize, -1);
+ adjustStats((int)pHdr->iSize, -1);
randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
- pHdr->iSize + sizeof(int) + pHdr->nTitle);
+ (int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
free(z);
sqlite3_mutex_leave(mem.mutex);
}
@@ -15226,9 +17086,9 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
pOldHdr = sqlite3MemsysGetHeader(pPrior);
pNew = sqlite3MemMalloc(nByte);
if( pNew ){
- memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize);
+ memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize));
if( nByte>pOldHdr->iSize ){
- randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize);
+ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize);
}
sqlite3MemFree(pPrior);
}
@@ -15272,7 +17132,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
-** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
+** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
*/
SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
int rc = 1;
@@ -15294,7 +17154,7 @@ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
-** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
+** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
*/
SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
int rc = 1;
@@ -15343,7 +17203,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSync(){
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
void **pBt = (void**)pHdr;
pBt -= pHdr->nBacktraceSlots;
- mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
+ mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
}
}
@@ -15841,7 +17701,7 @@ static void *memsys3MallocUnsafe(int nByte){
** This function assumes that the necessary mutexes, if any, are
** already held by the caller. Hence "Unsafe".
*/
-void memsys3FreeUnsafe(void *pOld){
+static void memsys3FreeUnsafe(void *pOld){
Mem3Block *p = (Mem3Block*)pOld;
int i;
u32 size, x;
@@ -15916,7 +17776,7 @@ static void *memsys3Malloc(int nBytes){
/*
** Free memory.
*/
-void memsys3Free(void *pPrior){
+static void memsys3Free(void *pPrior){
assert( pPrior );
memsys3Enter();
memsys3FreeUnsafe(pPrior);
@@ -15926,7 +17786,7 @@ void memsys3Free(void *pPrior){
/*
** Change the size of an existing memory allocation
*/
-void *memsys3Realloc(void *pPrior, int nBytes){
+static void *memsys3Realloc(void *pPrior, int nBytes){
int nOld;
void *p;
if( pPrior==0 ){
@@ -16126,7 +17986,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
** 1. All memory allocations sizes are rounded up to a power of 2.
**
** 2. If two adjacent free blocks are the halves of a larger block,
-** then the two blocks are coalesed into the single larger block.
+** then the two blocks are coalesced into the single larger block.
**
** 3. New memory is allocated from the first available free block.
**
@@ -16227,13 +18087,13 @@ static SQLITE_WSD struct Mem5Global {
} mem5;
/*
-** Access the static variable through a macro for SQLITE_OMIT_WSD
+** Access the static variable through a macro for SQLITE_OMIT_WSD.
*/
#define mem5 GLOBAL(struct Mem5Global, mem5)
/*
** Assuming mem5.zPool is divided up into an array of Mem5Link
-** structures, return a pointer to the idx-th such lik.
+** structures, return a pointer to the idx-th such link.
*/
#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom]))
@@ -16299,7 +18159,7 @@ static void memsys5Leave(void){
static int memsys5Size(void *p){
int iSize = 0;
if( p ){
- int i = ((u8 *)p-mem5.zPool)/mem5.szAtom;
+ int i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
assert( i>=0 && i<mem5.nBlock );
iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
}
@@ -16307,29 +18167,10 @@ static int memsys5Size(void *p){
}
/*
-** Find the first entry on the freelist iLogsize. Unlink that
-** entry and return its index.
-*/
-static int memsys5UnlinkFirst(int iLogsize){
- int i;
- int iFirst;
-
- assert( iLogsize>=0 && iLogsize<=LOGMAX );
- i = iFirst = mem5.aiFreelist[iLogsize];
- assert( iFirst>=0 );
- while( i>0 ){
- if( i<iFirst ) iFirst = i;
- i = MEM5LINK(i)->next;
- }
- memsys5Unlink(iFirst, iLogsize);
- return iFirst;
-}
-
-/*
** Return a block of memory of at least nBytes in size.
** Return NULL if unable. Return NULL if nBytes==0.
**
-** The caller guarantees that nByte positive.
+** The caller guarantees that nByte is positive.
**
** The caller has obtained a mutex prior to invoking this
** routine so there is never any chance that two or more
@@ -16364,13 +18205,14 @@ static void *memsys5MallocUnsafe(int nByte){
** block. If not, then split a block of the next larger power of
** two in order to create a new free block of size iLogsize.
*/
- for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){}
+ for(iBin=iLogsize; iBin<=LOGMAX && mem5.aiFreelist[iBin]<0; iBin++){}
if( iBin>LOGMAX ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte);
return 0;
}
- i = memsys5UnlinkFirst(iBin);
+ i = mem5.aiFreelist[iBin];
+ memsys5Unlink(i, iBin);
while( iBin>iLogsize ){
int newSize;
@@ -16390,6 +18232,12 @@ static void *memsys5MallocUnsafe(int nByte){
if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
+#ifdef SQLITE_DEBUG
+ /* Make sure the allocated memory does not assume that it is set to zero
+ ** or retains a value from a previous allocation */
+ memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz);
+#endif
+
/* Return a pointer to the allocated memory. */
return (void*)&mem5.zPool[i*mem5.szAtom];
}
@@ -16404,7 +18252,7 @@ static void memsys5FreeUnsafe(void *pOld){
/* Set iBlock to the index of the block pointed to by pOld in
** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
*/
- iBlock = ((u8 *)pOld-mem5.zPool)/mem5.szAtom;
+ iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
/* Check that the pointer pOld points to a valid, non-free block. */
assert( iBlock>=0 && iBlock<mem5.nBlock );
@@ -16447,11 +18295,18 @@ static void memsys5FreeUnsafe(void *pOld){
}
size *= 2;
}
+
+#ifdef SQLITE_DEBUG
+ /* Overwrite freed memory with the 0x55 bit pattern to verify that it is
+ ** not used after being freed */
+ memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size);
+#endif
+
memsys5Link(iBlock, iLogsize);
}
/*
-** Allocate nBytes of memory
+** Allocate nBytes of memory.
*/
static void *memsys5Malloc(int nBytes){
sqlite3_int64 *p = 0;
@@ -16761,7 +18616,7 @@ SQLITE_PRIVATE int sqlite3MutexEnd(void){
*/
SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int id){
#ifndef SQLITE_OMIT_AUTOINIT
- if( sqlite3_initialize() ) return 0;
+ if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
#endif
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
}
@@ -16830,7 +18685,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
}
#endif
-#endif /* SQLITE_MUTEX_OMIT */
+#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.c ***********************************************/
/************** Begin file mutex_noop.c **************************************/
@@ -16942,7 +18797,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
** that means that a mutex could not be allocated.
*/
static sqlite3_mutex *debugMutexAlloc(int id){
- static sqlite3_debug_mutex aStatic[6];
+ static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1];
sqlite3_debug_mutex *pNew = 0;
switch( id ){
case SQLITE_MUTEX_FAST:
@@ -17037,286 +18892,10 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){
SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
return sqlite3NoopMutex();
}
-#endif /* SQLITE_MUTEX_NOOP */
-#endif /* SQLITE_MUTEX_OMIT */
+#endif /* defined(SQLITE_MUTEX_NOOP) */
+#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex_noop.c ******************************************/
-/************** Begin file mutex_os2.c ***************************************/
-/*
-** 2007 August 28
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This file contains the C functions that implement mutexes for OS/2
-*/
-
-/*
-** The code in this file is only used if SQLITE_MUTEX_OS2 is defined.
-** See the mutex.h file for details.
-*/
-#ifdef SQLITE_MUTEX_OS2
-
-/********************** OS/2 Mutex Implementation **********************
-**
-** This implementation of mutexes is built using the OS/2 API.
-*/
-
-/*
-** The mutex object
-** Each recursive mutex is an instance of the following structure.
-*/
-struct sqlite3_mutex {
- HMTX mutex; /* Mutex controlling the lock */
- int id; /* Mutex type */
-#ifdef SQLITE_DEBUG
- int trace; /* True to trace changes */
-#endif
-};
-
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0 }
-#endif
-
-/*
-** Initialize and deinitialize the mutex subsystem.
-*/
-static int os2MutexInit(void){ return SQLITE_OK; }
-static int os2MutexEnd(void){ return SQLITE_OK; }
-
-/*
-** The sqlite3_mutex_alloc() routine allocates a new
-** mutex and returns a pointer to it. If it returns NULL
-** that means that a mutex could not be allocated.
-** SQLite will unwind its stack and return an error. The argument
-** to sqlite3_mutex_alloc() is one of these integer constants:
-**
-** <ul>
-** <li> SQLITE_MUTEX_FAST
-** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
-** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
-** <li> SQLITE_MUTEX_STATIC_PRNG
-** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
-** </ul>
-**
-** The first two constants cause sqlite3_mutex_alloc() to create
-** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
-** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
-** The mutex implementation does not need to make a distinction
-** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
-** not want to. But SQLite will only request a recursive mutex in
-** cases where it really needs one. If a faster non-recursive mutex
-** implementation is available on the host platform, the mutex subsystem
-** might return such a mutex in response to SQLITE_MUTEX_FAST.
-**
-** The other allowed parameters to sqlite3_mutex_alloc() each return
-** a pointer to a static preexisting mutex. Six static mutexes are
-** used by the current version of SQLite. Future versions of SQLite
-** may add additional static mutexes. Static mutexes are for internal
-** use by SQLite only. Applications that use SQLite mutexes should
-** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
-** SQLITE_MUTEX_RECURSIVE.
-**
-** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
-** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
-** mutex types, the same mutex is returned on every call that has
-** the same type number.
-*/
-static sqlite3_mutex *os2MutexAlloc(int iType){
- sqlite3_mutex *p = NULL;
- switch( iType ){
- case SQLITE_MUTEX_FAST:
- case SQLITE_MUTEX_RECURSIVE: {
- p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
- p->id = iType;
- if( DosCreateMutexSem( 0, &p->mutex, 0, FALSE ) != NO_ERROR ){
- sqlite3_free( p );
- p = NULL;
- }
- }
- break;
- }
- default: {
- static volatile int isInit = 0;
- static sqlite3_mutex staticMutexes[6] = {
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- };
- if ( !isInit ){
- APIRET rc;
- PTIB ptib;
- PPIB ppib;
- HMTX mutex;
- char name[32];
- DosGetInfoBlocks( &ptib, &ppib );
- sqlite3_snprintf( sizeof(name), name, "\\SEM32\\SQLITE%04x",
- ppib->pib_ulpid );
- while( !isInit ){
- mutex = 0;
- rc = DosCreateMutexSem( name, &mutex, 0, FALSE);
- if( rc == NO_ERROR ){
- unsigned int i;
- if( !isInit ){
- for( i = 0; i < sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++ ){
- DosCreateMutexSem( 0, &staticMutexes[i].mutex, 0, FALSE );
- }
- isInit = 1;
- }
- DosCloseMutexSem( mutex );
- }else if( rc == ERROR_DUPLICATE_NAME ){
- DosSleep( 1 );
- }else{
- return p;
- }
- }
- }
- assert( iType-2 >= 0 );
- assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
- p = &staticMutexes[iType-2];
- p->id = iType;
- break;
- }
- }
- return p;
-}
-
-
-/*
-** This routine deallocates a previously allocated mutex.
-** SQLite is careful to deallocate every mutex that it allocates.
-*/
-static void os2MutexFree(sqlite3_mutex *p){
-#ifdef SQLITE_DEBUG
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- assert( ulCount==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
-#endif
- DosCloseMutexSem( p->mutex );
- sqlite3_free( p );
-}
-
-#ifdef SQLITE_DEBUG
-/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use inside assert() statements.
-*/
-static int os2MutexHeld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) )
- return 0;
- DosGetInfoBlocks(&ptib, NULL);
- return tid==ptib->tib_ptib2->tib2_ultid;
-}
-static int os2MutexNotheld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 )
- return 1;
- DosGetInfoBlocks(&ptib, NULL);
- return tid!=ptib->tib_ptib2->tib2_ultid;
-}
-static void os2MutexTrace(sqlite3_mutex *p, char *pAction){
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount);
-}
-#endif
-
-/*
-** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
-** to enter a mutex. If another thread is already within the mutex,
-** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
-** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
-** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
-** be entered multiple times by the same thread. In such cases the,
-** mutex must be exited an equal number of times before another thread
-** can enter. If the same thread tries to enter any other kind of mutex
-** more than once, the behavior is undefined.
-*/
-static void os2MutexEnter(sqlite3_mutex *p){
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT);
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "enter");
-#endif
-}
-static int os2MutexTry(sqlite3_mutex *p){
- int rc = SQLITE_BUSY;
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) {
- rc = SQLITE_OK;
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "try");
-#endif
- }
- return rc;
-}
-
-/*
-** The sqlite3_mutex_leave() routine exits a mutex that was
-** previously entered by the same thread. The behavior
-** is undefined if the mutex is not currently entered or
-** is not currently allocated. SQLite will never do either.
-*/
-static void os2MutexLeave(sqlite3_mutex *p){
- assert( os2MutexHeld(p) );
- DosReleaseMutexSem(p->mutex);
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "leave");
-#endif
-}
-
-SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
- static const sqlite3_mutex_methods sMutex = {
- os2MutexInit,
- os2MutexEnd,
- os2MutexAlloc,
- os2MutexFree,
- os2MutexEnter,
- os2MutexTry,
- os2MutexLeave,
-#ifdef SQLITE_DEBUG
- os2MutexHeld,
- os2MutexNotheld
-#else
- 0,
- 0
-#endif
- };
-
- return &sMutex;
-}
-#endif /* SQLITE_MUTEX_OS2 */
-
-/************** End of mutex_os2.c *******************************************/
/************** Begin file mutex_unix.c **************************************/
/*
** 2007 August 28
@@ -17415,10 +18994,13 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -17452,6 +19034,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
sqlite3_mutex *p;
@@ -17667,7 +19252,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
return &sMutex;
}
-#endif /* SQLITE_MUTEX_PTHREAD */
+#endif /* SQLITE_MUTEX_PTHREADS */
/************** End of mutex_unix.c ******************************************/
/************** Begin file mutex_w32.c ***************************************/
@@ -17682,12 +19267,313 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement mutexes for win32
+** This file contains the C functions that implement mutexes for Win32.
+*/
+
+#if SQLITE_OS_WIN
+/*
+** Include code that is common to all os_*.c files
+*/
+/************** Include os_common.h in the middle of mutex_w32.c *************/
+/************** Begin file os_common.h ***************************************/
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains macros and a little bit of code that is common to
+** all of the platform-specific files (os_*.c) and is #included into those
+** files.
+**
+** This file should be #included by the os_*.c files only. It is not a
+** general purpose header file.
+*/
+#ifndef _OS_COMMON_H_
+#define _OS_COMMON_H_
+
+/*
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the
+** switch. The following code should catch this problem at compile-time.
+*/
+#ifdef MEMORY_DEBUG
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
+#endif
+
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+#else
+# define OSTRACE(X)
+#endif
+
+/*
+** Macros for performance tracing. Normally turned off. Only works
+** on i486 hardware.
+*/
+#ifdef SQLITE_PERFORMANCE_TRACE
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+/************** Include hwtime.h in the middle of os_common.h ****************/
+/************** Begin file hwtime.h ******************************************/
+/*
+** 2008 May 27
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains inline asm code for retrieving "high-performance"
+** counters for x86 class CPUs.
+*/
+#ifndef _HWTIME_H_
+#define _HWTIME_H_
+
+/*
+** The following routine only works on pentium-class (or newer) processors.
+** It uses the RDTSC opcode to read the cycle count value out of the
+** processor and returns that value. This can be used for high-res
+** profiling.
+*/
+#if (defined(__GNUC__) || defined(_MSC_VER)) && \
+ (defined(i386) || defined(__i386__) || defined(_M_IX86))
+
+ #if defined(__GNUC__)
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+ #elif defined(_MSC_VER)
+
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
+ __asm {
+ rdtsc
+ ret ; return value at EDX:EAX
+ }
+ }
+
+ #endif
+
+#elif (defined(__GNUC__) && defined(__x86_64__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long val;
+ __asm__ __volatile__ ("rdtsc" : "=A" (val));
+ return val;
+ }
+
+#elif (defined(__GNUC__) && defined(__ppc__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long long retval;
+ unsigned long junk;
+ __asm__ __volatile__ ("\n\
+ 1: mftbu %1\n\
+ mftb %L0\n\
+ mftbu %0\n\
+ cmpw %0,%1\n\
+ bne 1b"
+ : "=r" (retval), "=r" (junk));
+ return retval;
+ }
+
+#else
+
+ #error Need implementation of sqlite3Hwtime() for your platform.
+
+ /*
+ ** To compile without implementing sqlite3Hwtime() for your platform,
+ ** you can remove the above #error and use the following
+ ** stub function. You will lose timing support for many
+ ** of the debugging and testing utilities, but it should at
+ ** least compile and run.
+ */
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
+
+#endif
+
+#endif /* !defined(_HWTIME_H_) */
+
+/************** End of hwtime.h **********************************************/
+/************** Continuing where we left off in os_common.h ******************/
+
+static sqlite_uint64 g_start;
+static sqlite_uint64 g_elapsed;
+#define TIMER_START g_start=sqlite3Hwtime()
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
+#define TIMER_ELAPSED g_elapsed
+#else
+#define TIMER_START
+#define TIMER_END
+#define TIMER_ELAPSED ((sqlite_uint64)0)
+#endif
+
+/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
+*/
+#ifdef SQLITE_TEST
+SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
+SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
+SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
+SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
+SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
+SQLITE_API int sqlite3_diskfull_pending = 0;
+SQLITE_API int sqlite3_diskfull = 0;
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
+#define SimulateIOError(CODE) \
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
+ || sqlite3_io_error_pending-- == 1 ) \
+ { local_ioerr(); CODE; }
+static void local_ioerr(){
+ IOTRACE(("IOERR\n"));
+ sqlite3_io_error_hit++;
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
+}
+#define SimulateDiskfullError(CODE) \
+ if( sqlite3_diskfull_pending ){ \
+ if( sqlite3_diskfull_pending == 1 ){ \
+ local_ioerr(); \
+ sqlite3_diskfull = 1; \
+ sqlite3_io_error_hit = 1; \
+ CODE; \
+ }else{ \
+ sqlite3_diskfull_pending--; \
+ } \
+ }
+#else
+#define SimulateIOErrorBenign(X)
+#define SimulateIOError(A)
+#define SimulateDiskfullError(A)
+#endif
+
+/*
+** When testing, keep a count of the number of open files.
+*/
+#ifdef SQLITE_TEST
+SQLITE_API int sqlite3_open_file_count = 0;
+#define OpenCounter(X) sqlite3_open_file_count+=(X)
+#else
+#define OpenCounter(X)
+#endif
+
+#endif /* !defined(_OS_COMMON_H_) */
+
+/************** End of os_common.h *******************************************/
+/************** Continuing where we left off in mutex_w32.c ******************/
+
+/*
+** Include the header file for the Windows VFS.
*/
+/************** Include os_win.h in the middle of mutex_w32.c ****************/
+/************** Begin file os_win.h ******************************************/
+/*
+** 2013 November 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains code that is specific to Windows.
+*/
+#ifndef _OS_WIN_H_
+#define _OS_WIN_H_
+
+/*
+** Include the primary Windows SDK header file.
+*/
+#include "windows.h"
+
+#ifdef __CYGWIN__
+# include <sys/cygwin.h>
+# include <errno.h> /* amalgamator: dontcache */
+#endif
+
+/*
+** Determine if we are dealing with Windows NT.
+**
+** We ought to be able to determine if we are compiling for Windows 9x or
+** Windows NT using the _WIN32_WINNT macro as follows:
+**
+** #if defined(_WIN32_WINNT)
+** # define SQLITE_OS_WINNT 1
+** #else
+** # define SQLITE_OS_WINNT 0
+** #endif
+**
+** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as
+** it ought to, so the above test does not work. We'll just assume that
+** everything is Windows NT unless the programmer explicitly says otherwise
+** by setting SQLITE_OS_WINNT to 0.
+*/
+#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
+# define SQLITE_OS_WINNT 1
+#endif
+
+/*
+** Determine if we are dealing with Windows CE - which has a much reduced
+** API.
+*/
+#if defined(_WIN32_WCE)
+# define SQLITE_OS_WINCE 1
+#else
+# define SQLITE_OS_WINCE 0
+#endif
+
+/*
+** Determine if we are dealing with WinRT, which provides only a subset of
+** the full Win32 API.
+*/
+#if !defined(SQLITE_OS_WINRT)
+# define SQLITE_OS_WINRT 0
+#endif
+
+/*
+** For WinCE, some API function parameters do not appear to be declared as
+** volatile.
+*/
+#if SQLITE_OS_WINCE
+# define SQLITE_WIN32_VOLATILE
+#else
+# define SQLITE_WIN32_VOLATILE volatile
+#endif
+
+#endif /* _OS_WIN_H_ */
+
+/************** End of os_win.h **********************************************/
+/************** Continuing where we left off in mutex_w32.c ******************/
+#endif
/*
** The code in this file is only used if we are compiling multithreaded
-** on a win32 system.
+** on a Win32 system.
*/
#ifdef SQLITE_MUTEX_W32
@@ -17700,48 +19586,22 @@ struct sqlite3_mutex {
#ifdef SQLITE_DEBUG
volatile int nRef; /* Number of enterances */
volatile DWORD owner; /* Thread holding this mutex */
- int trace; /* True to trace changes */
+ volatile int trace; /* True to trace changes */
#endif
};
-#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
-#endif
/*
-** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
-** or WinCE. Return false (zero) for Win95, Win98, or WinME.
-**
-** Here is an interesting observation: Win95, Win98, and WinME lack
-** the LockFileEx() API. But we can still statically link against that
-** API as long as we don't call it win running Win95/98/ME. A call to
-** this routine is used to determine if the host is Win95/98/ME or
-** WinNT/2K/XP so that we will know whether or not we can safely call
-** the LockFileEx() API.
-**
-** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
-** which is only available if your application was compiled with
-** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
-** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
-** this out as well.
+** These are the initializer values used when declaring a "static" mutex
+** on Win32. It should be noted that all mutexes require initialization
+** on the Win32 platform.
*/
-#if 0
-#if SQLITE_OS_WINCE
-# define mutexIsNT() (1)
+#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
+
+#ifdef SQLITE_DEBUG
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
+ 0L, (DWORD)0, 0 }
#else
- static int mutexIsNT(void){
- static int osType = 0;
- if( osType==0 ){
- OSVERSIONINFO sInfo;
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
- GetVersionEx(&sInfo);
- osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
- }
- return osType==2;
- }
-#endif /* SQLITE_OS_WINCE */
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
#endif
#ifdef SQLITE_DEBUG
@@ -17752,20 +19612,24 @@ struct sqlite3_mutex {
static int winMutexHeld(sqlite3_mutex *p){
return p->nRef!=0 && p->owner==GetCurrentThreadId();
}
+
static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
return p->nRef==0 || p->owner!=tid;
}
+
static int winMutexNotheld(sqlite3_mutex *p){
- DWORD tid = GetCurrentThreadId();
+ DWORD tid = GetCurrentThreadId();
return winMutexNotheld2(p, tid);
}
#endif
-
/*
** Initialize and deinitialize the mutex subsystem.
*/
-static sqlite3_mutex winMutex_staticMutexes[6] = {
+static sqlite3_mutex winMutex_staticMutexes[] = {
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
@@ -17773,33 +19637,43 @@ static sqlite3_mutex winMutex_staticMutexes[6] = {
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
+
static int winMutex_isInit = 0;
-/* As winMutexInit() and winMutexEnd() are called as part
-** of the sqlite3_initialize and sqlite3_shutdown()
-** processing, the "interlocked" magic is probably not
-** strictly necessary.
+static int winMutex_isNt = -1; /* <0 means "need to query" */
+
+/* As the winMutexInit() and winMutexEnd() functions are called as part
+** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
+** "interlocked" magic used here is probably not strictly necessary.
*/
-static long winMutex_lock = 0;
+static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0;
+
+SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */
+SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
-static int winMutexInit(void){
+static int winMutexInit(void){
/* The first to increment to 1 does actual initialization */
if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
int i;
for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
+#if SQLITE_OS_WINRT
+ InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0);
+#else
InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
+#endif
}
winMutex_isInit = 1;
}else{
- /* Someone else is in the process of initing the static mutexes */
+ /* Another thread is (in the process of) initializing the static
+ ** mutexes */
while( !winMutex_isInit ){
- Sleep(1);
+ sqlite3_win32_sleep(1);
}
}
- return SQLITE_OK;
+ return SQLITE_OK;
}
-static int winMutexEnd(void){
- /* The first to decrement to 0 does actual shutdown
+static int winMutexEnd(void){
+ /* The first to decrement to 0 does actual shutdown
** (which should be the last to shutdown.) */
if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
if( winMutex_isInit==1 ){
@@ -17810,7 +19684,7 @@ static int winMutexEnd(void){
winMutex_isInit = 0;
}
}
- return SQLITE_OK;
+ return SQLITE_OK;
}
/*
@@ -17825,10 +19699,13 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -17851,7 +19728,7 @@ static int winMutexEnd(void){
**
** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
+** returns a different mutex on every call. But for the static
** mutex types, the same mutex is returned on every call that has
** the same type number.
*/
@@ -17862,21 +19739,31 @@ static sqlite3_mutex *winMutexAlloc(int iType){
case SQLITE_MUTEX_FAST:
case SQLITE_MUTEX_RECURSIVE: {
p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
+ if( p ){
#ifdef SQLITE_DEBUG
p->id = iType;
+#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC
+ p->trace = 1;
#endif
+#endif
+#if SQLITE_OS_WINRT
+ InitializeCriticalSectionEx(&p->mutex, 0, 0);
+#else
InitializeCriticalSection(&p->mutex);
+#endif
}
break;
}
default: {
- assert( winMutex_isInit==1 );
assert( iType-2 >= 0 );
assert( iType-2 < ArraySize(winMutex_staticMutexes) );
+ assert( winMutex_isInit==1 );
p = &winMutex_staticMutexes[iType-2];
#ifdef SQLITE_DEBUG
p->id = iType;
+#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
+ p->trace = 1;
+#endif
#endif
break;
}
@@ -17892,8 +19779,11 @@ static sqlite3_mutex *winMutexAlloc(int iType){
*/
static void winMutexFree(sqlite3_mutex *p){
assert( p );
+#ifdef SQLITE_DEBUG
assert( p->nRef==0 && p->owner==0 );
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
+#endif
+ assert( winMutex_isInit==1 );
DeleteCriticalSection(&p->mutex);
sqlite3_free(p);
}
@@ -17910,30 +19800,39 @@ static void winMutexFree(sqlite3_mutex *p){
** more than once, the behavior is undefined.
*/
static void winMutexEnter(sqlite3_mutex *p){
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ DWORD tid = GetCurrentThreadId();
+#endif
#ifdef SQLITE_DEBUG
- DWORD tid = GetCurrentThreadId();
+ assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
+#else
+ assert( p );
#endif
+ assert( winMutex_isInit==1 );
EnterCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
assert( p->nRef>0 || p->owner==0 );
- p->owner = tid;
+ p->owner = tid;
p->nRef++;
if( p->trace ){
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ OSTRACE(("ENTER-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
+ tid, p, p->trace, p->nRef));
}
#endif
}
+
static int winMutexTry(sqlite3_mutex *p){
-#ifndef NDEBUG
- DWORD tid = GetCurrentThreadId();
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ DWORD tid = GetCurrentThreadId();
#endif
int rc = SQLITE_BUSY;
+ assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
/*
** The sqlite3_mutex_try() routine is very rarely used, and when it
** is used it is merely an optimization. So it is OK for it to always
- ** fail.
+ ** fail.
**
** The TryEnterCriticalSection() interface is only available on WinNT.
** And some windows compilers complain if you try to use it without
@@ -17941,18 +19840,27 @@ static int winMutexTry(sqlite3_mutex *p){
** For that reason, we will omit this optimization for now. See
** ticket #2685.
*/
-#if 0
- if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
+ assert( winMutex_isInit==1 );
+ assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
+ if( winMutex_isNt<0 ){
+ winMutex_isNt = sqlite3_win32_is_nt();
+ }
+ assert( winMutex_isNt==0 || winMutex_isNt==1 );
+ if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
+#ifdef SQLITE_DEBUG
p->owner = tid;
p->nRef++;
+#endif
rc = SQLITE_OK;
}
#else
UNUSED_PARAMETER(p);
#endif
#ifdef SQLITE_DEBUG
- if( rc==SQLITE_OK && p->trace ){
- printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ if( p->trace ){
+ OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
+ tid, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc)));
}
#endif
return rc;
@@ -17965,18 +19873,23 @@ static int winMutexTry(sqlite3_mutex *p){
** is not currently allocated. SQLite will never do either.
*/
static void winMutexLeave(sqlite3_mutex *p){
-#ifndef NDEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
DWORD tid = GetCurrentThreadId();
+#endif
+ assert( p );
+#ifdef SQLITE_DEBUG
assert( p->nRef>0 );
assert( p->owner==tid );
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
+ assert( winMutex_isInit==1 );
LeaveCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
if( p->trace ){
- printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
+ tid, p, p->trace, p->nRef));
}
#endif
}
@@ -17998,9 +19911,9 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
0
#endif
};
-
return &sMutex;
}
+
#endif /* SQLITE_MUTEX_W32 */
/************** End of mutex_w32.c *******************************************/
@@ -18019,6 +19932,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
**
** Memory allocation functions used throughout sqlite.
*/
+/* #include <stdarg.h> */
/*
** Attempt to release up to n bytes of non-essential memory currently
@@ -18135,7 +20049,8 @@ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_int64 priorLimit;
sqlite3_int64 excess;
#ifndef SQLITE_OMIT_AUTOINIT
- sqlite3_initialize();
+ int rc = sqlite3_initialize();
+ if( rc ) return -1;
#endif
sqlite3_mutex_enter(mem0.mutex);
priorLimit = mem0.alarmThreshold;
@@ -18298,11 +20213,9 @@ static int mallocWithAlarm(int n, void **pp){
** Allocate memory. This routine is like sqlite3_malloc() except that it
** assumes the memory subsystem has already been initialized.
*/
-SQLITE_PRIVATE void *sqlite3Malloc(int n){
+SQLITE_PRIVATE void *sqlite3Malloc(u64 n){
void *p;
- if( n<=0 /* IMP: R-65312-04917 */
- || n>=0x7fffff00
- ){
+ if( n==0 || n>=0x7fffff00 ){
/* A memory allocation of a number of bytes which is near the maximum
** signed integer value might cause an integer overflow inside of the
** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
@@ -18311,12 +20224,12 @@ SQLITE_PRIVATE void *sqlite3Malloc(int n){
p = 0;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- mallocWithAlarm(n, &p);
+ mallocWithAlarm((int)n, &p);
sqlite3_mutex_leave(mem0.mutex);
}else{
- p = sqlite3GlobalConfig.m.xMalloc(n);
+ p = sqlite3GlobalConfig.m.xMalloc((int)n);
}
- assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */
+ assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */
return p;
}
@@ -18329,6 +20242,12 @@ SQLITE_API void *sqlite3_malloc(int n){
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
+ return n<=0 ? 0 : sqlite3Malloc(n);
+}
+SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
return sqlite3Malloc(n);
}
@@ -18356,22 +20275,20 @@ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
assert( n>0 );
sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
p = mem0.pScratchFree;
mem0.pScratchFree = mem0.pScratchFree->pNext;
mem0.nScratchFree--;
sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
sqlite3_mutex_leave(mem0.mutex);
}else{
- if( sqlite3GlobalConfig.bMemstat ){
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
- n = mallocWithAlarm(n, &p);
- if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
- sqlite3_mutex_leave(mem0.mutex);
- }else{
+ sqlite3_mutex_leave(mem0.mutex);
+ p = sqlite3Malloc(n);
+ if( sqlite3GlobalConfig.bMemstat && p ){
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p));
sqlite3_mutex_leave(mem0.mutex);
- p = sqlite3GlobalConfig.m.xMalloc(n);
}
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
}
@@ -18437,7 +20354,7 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, void *p){
- return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
+ return p>=db->lookaside.pStart && p<db->lookaside.pEnd;
}
#else
#define isLookaside(A,B) 0
@@ -18449,28 +20366,37 @@ static int isLookaside(sqlite3 *db, void *p){
*/
SQLITE_PRIVATE int sqlite3MallocSize(void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
return sqlite3GlobalConfig.m.xSize(p);
}
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
- if( db && isLookaside(db, p) ){
- return db->lookaside.sz;
+ if( db==0 ){
+ assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ return sqlite3MallocSize(p);
}else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
- assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
- return sqlite3GlobalConfig.m.xSize(p);
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( isLookaside(db, p) ){
+ return db->lookaside.sz;
+ }else{
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ return sqlite3GlobalConfig.m.xSize(p);
+ }
}
}
+SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){
+ assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ return (sqlite3_uint64)sqlite3GlobalConfig.m.xSize(p);
+}
/*
** Free memory previously obtained from sqlite3Malloc().
*/
SQLITE_API void sqlite3_free(void *p){
if( p==0 ) return; /* IMP: R-49053-54554 */
- assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugNoType(p, ~MEMTYPE_HEAP) );
if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
@@ -18483,26 +20409,39 @@ SQLITE_API void sqlite3_free(void *p){
}
/*
+** Add the size of memory allocation "p" to the count in
+** *db->pnBytesFreed.
+*/
+static SQLITE_NOINLINE void measureAllocationSize(sqlite3 *db, void *p){
+ *db->pnBytesFreed += sqlite3DbMallocSize(db,p);
+}
+
+/*
** Free memory that might be associated with a particular database
** connection.
*/
SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
+ if( p==0 ) return;
if( db ){
if( db->pnBytesFreed ){
- *db->pnBytesFreed += sqlite3DbMallocSize(db, p);
+ measureAllocationSize(db, p);
return;
}
if( isLookaside(db, p) ){
LookasideSlot *pBuf = (LookasideSlot*)p;
+#if SQLITE_DEBUG
+ /* Trash all content in the buffer being freed */
+ memset(p, 0xaa, db->lookaside.sz);
+#endif
pBuf->pNext = db->lookaside.pFree;
db->lookaside.pFree = pBuf;
db->lookaside.nOut--;
return;
}
}
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
sqlite3_free(p);
@@ -18511,14 +20450,16 @@ SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
/*
** Change the size of an existing memory allocation
*/
-SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
+SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
int nOld, nNew, nDiff;
void *pNew;
+ assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
if( pOld==0 ){
- return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
+ return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */
}
- if( nBytes<=0 ){
- sqlite3_free(pOld); /* IMP: R-31593-10574 */
+ if( nBytes==0 ){
+ sqlite3_free(pOld); /* IMP: R-26507-47431 */
return 0;
}
if( nBytes>=0x7fffff00 ){
@@ -18529,22 +20470,20 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
/* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second
** argument to xRealloc is always a value returned by a prior call to
** xRoundup. */
- nNew = sqlite3GlobalConfig.m.xRoundup(nBytes);
+ nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes);
if( nOld==nNew ){
pNew = pOld;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
+ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
nDiff = nNew - nOld;
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
mem0.alarmThreshold-nDiff ){
sqlite3MallocAlarm(nDiff);
}
- assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
- assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
if( pNew==0 && mem0.alarmCallback ){
- sqlite3MallocAlarm(nBytes);
+ sqlite3MallocAlarm((int)nBytes);
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
if( pNew ){
@@ -18555,7 +20494,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
}else{
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
- assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */
return pNew;
}
@@ -18567,6 +20506,13 @@ SQLITE_API void *sqlite3_realloc(void *pOld, int n){
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
+ if( n<0 ) n = 0; /* IMP: R-26507-47431 */
+ return sqlite3Realloc(pOld, n);
+}
+SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
return sqlite3Realloc(pOld, n);
}
@@ -18574,10 +20520,10 @@ SQLITE_API void *sqlite3_realloc(void *pOld, int n){
/*
** Allocate and zero memory.
*/
-SQLITE_PRIVATE void *sqlite3MallocZero(int n){
+SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){
void *p = sqlite3Malloc(n);
if( p ){
- memset(p, 0, n);
+ memset(p, 0, (size_t)n);
}
return p;
}
@@ -18586,10 +20532,10 @@ SQLITE_PRIVATE void *sqlite3MallocZero(int n){
** Allocate and zero memory. If the allocation fails, make
** the mallocFailed flag in the connection pointer.
*/
-SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, int n){
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
void *p = sqlite3DbMallocRaw(db, n);
if( p ){
- memset(p, 0, n);
+ memset(p, 0, (size_t)n);
}
return p;
}
@@ -18612,7 +20558,7 @@ SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, int n){
** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
** that all prior mallocs (ex: "a") worked too.
*/
-SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int n){
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
void *p;
assert( db==0 || sqlite3_mutex_held(db->mutex) );
assert( db==0 || db->pnBytesFreed==0 );
@@ -18647,8 +20593,8 @@ SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int n){
if( !p && db ){
db->mallocFailed = 1;
}
- sqlite3MemdebugSetType(p, MEMTYPE_DB |
- ((db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
+ sqlite3MemdebugSetType(p,
+ (db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
return p;
}
@@ -18656,7 +20602,7 @@ SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int n){
** Resize the block of memory pointed to by p to n bytes. If the
** resize fails, set the mallocFailed flag in the connection object.
*/
-SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
void *pNew = 0;
assert( db!=0 );
assert( sqlite3_mutex_held(db->mutex) );
@@ -18674,15 +20620,14 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
sqlite3DbFree(db, p);
}
}else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, ~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- pNew = sqlite3_realloc(p, n);
+ pNew = sqlite3_realloc64(p, n);
if( !pNew ){
- sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP);
db->mallocFailed = 1;
}
- sqlite3MemdebugSetType(pNew, MEMTYPE_DB |
+ sqlite3MemdebugSetType(pNew,
(db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
}
}
@@ -18693,7 +20638,7 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
** Attempt to reallocate p. If the reallocation fails, then free p
** and set the mallocFailed flag in the database connection.
*/
-SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, int n){
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n){
void *pNew;
pNew = sqlite3DbRealloc(db, p, n);
if( !pNew ){
@@ -18723,7 +20668,7 @@ SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){
}
return zNew;
}
-SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, int n){
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
char *zNew;
if( z==0 ){
return 0;
@@ -18731,7 +20676,7 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, int n){
assert( (n&0x7fffffff)==n );
zNew = sqlite3DbMallocRaw(db, n+1);
if( zNew ){
- memcpy(zNew, z, n);
+ memcpy(zNew, z, (size_t)n);
zNew[n] = 0;
}
return zNew;
@@ -18753,6 +20698,14 @@ SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat
*pz = z;
}
+/*
+** Take actions at the end of an API call to indicate an OOM error
+*/
+static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
+ db->mallocFailed = 0;
+ sqlite3Error(db, SQLITE_NOMEM);
+ return SQLITE_NOMEM;
+}
/*
** This function must be called before exiting any API function (i.e.
@@ -18773,12 +20726,11 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
** is unsafe, as is the call to sqlite3Error().
*/
assert( !db || sqlite3_mutex_held(db->mutex) );
- if( db && (db->mallocFailed || rc==SQLITE_IOERR_NOMEM) ){
- sqlite3Error(db, SQLITE_NOMEM, 0);
- db->mallocFailed = 0;
- rc = SQLITE_NOMEM;
+ if( db==0 ) return rc & 0xff;
+ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
+ return apiOomError(db);
}
- return rc & (db ? db->errMask : 0xff);
+ return rc & db->errMask;
}
/************** End of malloc.c **********************************************/
@@ -18792,51 +20744,24 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
**
**************************************************************************
**
-** The following modules is an enhanced replacement for the "printf" subroutines
-** found in the standard C library. The following enhancements are
-** supported:
-**
-** + Additional functions. The standard set of "printf" functions
-** includes printf, fprintf, sprintf, vprintf, vfprintf, and
-** vsprintf. This module adds the following:
-**
-** * snprintf -- Works like sprintf, but has an extra argument
-** which is the size of the buffer written to.
-**
-** * mprintf -- Similar to sprintf. Writes output to memory
-** obtained from malloc.
-**
-** * xprintf -- Calls a function to dispose of output.
-**
-** * nprintf -- No output, but returns the number of characters
-** that would have been output by printf.
-**
-** * A v- version (ex: vsnprintf) of every function is also
-** supplied.
-**
-** + A few extensions to the formatting notation are supported:
-**
-** * The "=" flag (similar to "-") causes the output to be
-** be centered in the appropriately sized field.
-**
-** * The %b field outputs an integer in binary notation.
-**
-** * The %c field now accepts a precision. The character output
-** is repeated by the number of times the precision specifies.
-**
-** * The %' field works like %c, but takes as its character the
-** next character of the format string, instead of the next
-** argument. For example, printf("%.78'-") prints 78 minus
-** signs, the same as printf("%.78c",'-').
-**
-** + When compiled using GCC on a SPARC, this version of printf is
-** faster than the library printf for SUN OS 4.1.
-**
-** + All functions are fully reentrant.
-**
+** This file contains code for a set of "printf"-like routines. These
+** routines format strings much like the printf() from the standard C
+** library, though the implementation here has enhancements to support
+** SQLlite.
*/
/*
+** If the strchrnul() library function is available, then set
+** HAVE_STRCHRNUL. If that routine is not available, this module
+** will supply its own. The built-in version is slower than
+** the glibc version so the glibc version is definitely preferred.
+*/
+#if !defined(HAVE_STRCHRNUL)
+# define HAVE_STRCHRNUL 0
+#endif
+
+
+/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
@@ -18946,7 +20871,8 @@ static const et_info fmtinfo[] = {
static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
int digit;
LONGDOUBLE_TYPE d;
- if( (*cnt)++ >= 16 ) return '0';
+ if( (*cnt)<=0 ) return '0';
+ (*cnt)--;
digit = (int)*val;
d = digit;
digit += '0';
@@ -18956,64 +20882,47 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
#endif /* SQLITE_OMIT_FLOATING_POINT */
/*
-** Append N space characters to the given string buffer.
+** Set the StrAccum object to an error mode.
*/
-static void appendSpace(StrAccum *pAccum, int N){
- static const char zSpaces[] = " ";
- while( N>=(int)sizeof(zSpaces)-1 ){
- sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
- N -= sizeof(zSpaces)-1;
- }
- if( N>0 ){
- sqlite3StrAccumAppend(pAccum, zSpaces, N);
- }
+static void setStrAccumError(StrAccum *p, u8 eError){
+ p->accError = eError;
+ p->nAlloc = 0;
}
/*
+** Extra argument values from a PrintfArguments object
+*/
+static sqlite3_int64 getIntArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0;
+ return sqlite3_value_int64(p->apArg[p->nUsed++]);
+}
+static double getDoubleArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0.0;
+ return sqlite3_value_double(p->apArg[p->nUsed++]);
+}
+static char *getTextArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0;
+ return (char*)sqlite3_value_text(p->apArg[p->nUsed++]);
+}
+
+
+/*
** On machines with a small stack size, you can redefine the
-** SQLITE_PRINT_BUF_SIZE to be less than 350.
+** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
*/
#ifndef SQLITE_PRINT_BUF_SIZE
-# if defined(SQLITE_SMALL_STACK)
-# define SQLITE_PRINT_BUF_SIZE 50
-# else
-# define SQLITE_PRINT_BUF_SIZE 350
-# endif
+# define SQLITE_PRINT_BUF_SIZE 70
#endif
#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
/*
-** The root program. All variations call this core.
-**
-** INPUTS:
-** func This is a pointer to a function taking three arguments
-** 1. A pointer to anything. Same as the "arg" parameter.
-** 2. A pointer to the list of characters to be output
-** (Note, this list is NOT null terminated.)
-** 3. An integer number of characters to be output.
-** (Note: This number might be zero.)
-**
-** arg This is the pointer to anything which will be passed as the
-** first argument to "func". Use it for whatever you like.
-**
-** fmt This is the format string, as in the usual print.
-**
-** ap This is a pointer to a list of arguments. Same as in
-** vfprint.
-**
-** OUTPUTS:
-** The return value is the total number of characters sent to
-** the function "func". Returns -1 on a error.
-**
-** Note that the order in which automatic variables are declared below
-** seems to make a big difference in determining how fast this beast
-** will run.
+** Render a string given by "fmt" into the StrAccum object.
*/
SQLITE_PRIVATE void sqlite3VXPrintf(
- StrAccum *pAccum, /* Accumulate results here */
- int useExtended, /* Allow extended %-conversions */
- const char *fmt, /* Format string */
- va_list ap /* arguments */
+ StrAccum *pAccum, /* Accumulate results here */
+ u32 bFlags, /* SQLITE_PRINTF_* flags */
+ const char *fmt, /* Format string */
+ va_list ap /* arguments */
){
int c; /* Next character in the format string */
char *bufpt; /* Pointer to the conversion buffer */
@@ -19030,32 +20939,45 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
etByte flag_long; /* True if "l" flag is present */
etByte flag_longlong; /* True if the "ll" flag is present */
etByte done; /* Loop termination flag */
+ etByte xtype = 0; /* Conversion paradigm */
+ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
+ u8 useIntern; /* Ok to use internal conversions (ex: %T) */
+ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
sqlite_uint64 longvalue; /* Value for integer types */
LONGDOUBLE_TYPE realvalue; /* Value for real types */
const et_info *infop; /* Pointer to the appropriate info structure */
- char buf[etBUFSIZE]; /* Conversion buffer */
- char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
- etByte xtype = 0; /* Conversion paradigm */
- char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
+ char *zOut; /* Rendering buffer */
+ int nOut; /* Size of the rendering buffer */
+ char *zExtra = 0; /* Malloced memory used by some conversion */
#ifndef SQLITE_OMIT_FLOATING_POINT
int exp, e2; /* exponent of real numbers */
+ int nsd; /* Number of significant digits returned */
double rounder; /* Used for rounding floating point values */
etByte flag_dp; /* True if decimal point should be shown */
etByte flag_rtz; /* True if trailing zeros should be removed */
- etByte flag_exp; /* True to force display of the exponent */
- int nsd; /* Number of significant digits returned */
#endif
+ PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
+ char buf[etBUFSIZE]; /* Conversion buffer */
- length = 0;
bufpt = 0;
+ if( bFlags ){
+ if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
+ pArgList = va_arg(ap, PrintfArguments*);
+ }
+ useIntern = bFlags & SQLITE_PRINTF_INTERNAL;
+ }else{
+ bArgList = useIntern = 0;
+ }
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
- int amt;
bufpt = (char *)fmt;
- amt = 1;
- while( (c=(*++fmt))!='%' && c!=0 ) amt++;
- sqlite3StrAccumAppend(pAccum, bufpt, amt);
- if( c==0 ) break;
+#if HAVE_STRCHRNUL
+ fmt = strchrnul(fmt, '%');
+#else
+ do{ fmt++; }while( *fmt && *fmt != '%' );
+#endif
+ sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
+ if( *fmt==0 ) break;
}
if( (c=(*++fmt))==0 ){
sqlite3StrAccumAppend(pAccum, "%", 1);
@@ -19079,7 +21001,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
/* Get the field width */
width = 0;
if( c=='*' ){
- width = va_arg(ap,int);
+ if( bArgList ){
+ width = (int)getIntArg(pArgList);
+ }else{
+ width = va_arg(ap,int);
+ }
if( width<0 ){
flag_leftjustify = 1;
width = -width;
@@ -19091,15 +21017,16 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
c = *++fmt;
}
}
- if( width > etBUFSIZE-10 ){
- width = etBUFSIZE-10;
- }
/* Get the precision */
if( c=='.' ){
precision = 0;
c = *++fmt;
if( c=='*' ){
- precision = va_arg(ap,int);
+ if( bArgList ){
+ precision = (int)getIntArg(pArgList);
+ }else{
+ precision = va_arg(ap,int);
+ }
if( precision<0 ) precision = -precision;
c = *++fmt;
}else{
@@ -19130,7 +21057,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
- if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
+ if( useIntern || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
return;
@@ -19138,13 +21065,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
break;
}
}
- zExtra = 0;
-
-
- /* Limit the precision to prevent overflowing buf[] during conversion */
- if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
- precision = etBUFSIZE-40;
- }
/*
** At this point, variables are initialized as follows:
@@ -19176,7 +21096,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
case etRADIX:
if( infop->flags & FLAG_SIGNED ){
i64 v;
- if( flag_longlong ){
+ if( bArgList ){
+ v = getIntArg(pArgList);
+ }else if( flag_longlong ){
v = va_arg(ap,i64);
}else if( flag_long ){
v = va_arg(ap,long int);
@@ -19197,7 +21119,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
else prefix = 0;
}
}else{
- if( flag_longlong ){
+ if( bArgList ){
+ longvalue = (u64)getIntArg(pArgList);
+ }else if( flag_longlong ){
longvalue = va_arg(ap,u64);
}else if( flag_long ){
longvalue = va_arg(ap,unsigned long int);
@@ -19210,28 +21134,36 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( flag_zeropad && precision<width-(prefix!=0) ){
precision = width-(prefix!=0);
}
- bufpt = &buf[etBUFSIZE-1];
+ if( precision<etBUFSIZE-10 ){
+ nOut = etBUFSIZE;
+ zOut = buf;
+ }else{
+ nOut = precision + 10;
+ zOut = zExtra = sqlite3Malloc( nOut );
+ if( zOut==0 ){
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
+ return;
+ }
+ }
+ bufpt = &zOut[nOut-1];
if( xtype==etORDINAL ){
static const char zOrd[] = "thstndrd";
int x = (int)(longvalue % 10);
if( x>=4 || (longvalue/10)%10==1 ){
x = 0;
}
- buf[etBUFSIZE-3] = zOrd[x*2];
- buf[etBUFSIZE-2] = zOrd[x*2+1];
- bufpt -= 2;
+ *(--bufpt) = zOrd[x*2+1];
+ *(--bufpt) = zOrd[x*2];
}
{
- register const char *cset; /* Use registers for speed */
- register int base;
- cset = &aDigits[infop->charset];
- base = infop->base;
+ const char *cset = &aDigits[infop->charset];
+ u8 base = infop->base;
do{ /* Convert to ascii */
*(--bufpt) = cset[longvalue%base];
longvalue = longvalue/base;
}while( longvalue>0 );
}
- length = (int)(&buf[etBUFSIZE-1]-bufpt);
+ length = (int)(&zOut[nOut-1]-bufpt);
for(idx=precision-length; idx>0; idx--){
*(--bufpt) = '0'; /* Zero pad */
}
@@ -19242,17 +21174,20 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
pre = &aPrefix[infop->prefix];
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
}
- length = (int)(&buf[etBUFSIZE-1]-bufpt);
+ length = (int)(&zOut[nOut-1]-bufpt);
break;
case etFLOAT:
case etEXP:
case etGENERIC:
- realvalue = va_arg(ap,double);
+ if( bArgList ){
+ realvalue = getDoubleArg(pArgList);
+ }else{
+ realvalue = va_arg(ap,double);
+ }
#ifdef SQLITE_OMIT_FLOATING_POINT
length = 0;
#else
if( precision<0 ) precision = 6; /* Set default precision */
- if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10;
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
@@ -19262,13 +21197,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
else prefix = 0;
}
if( xtype==etGENERIC && precision>0 ) precision--;
-#if 0
- /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
- for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
-#else
- /* It makes more sense to use 0.5 */
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
-#endif
if( xtype==etFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
@@ -19278,9 +21207,12 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
break;
}
if( realvalue>0.0 ){
- while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
- while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
- while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+ LONGDOUBLE_TYPE scale = 1.0;
+ while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
+ while( realvalue>=1e64*scale && exp<=350 ){ scale *= 1e64; exp+=64; }
+ while( realvalue>=1e8*scale && exp<=350 ){ scale *= 1e8; exp+=8; }
+ while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
+ realvalue /= scale;
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
@@ -19300,7 +21232,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
- flag_exp = xtype==etEXP;
if( xtype!=etFLOAT ){
realvalue += rounder;
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
@@ -19314,14 +21245,22 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
xtype = etFLOAT;
}
}else{
- flag_rtz = 0;
+ flag_rtz = flag_altform2;
}
if( xtype==etEXP ){
e2 = 0;
}else{
e2 = exp;
}
- nsd = 0;
+ if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){
+ bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
+ if( bufpt==0 ){
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
+ return;
+ }
+ }
+ zOut = bufpt;
+ nsd = 16 + flag_altform2*10;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
@@ -19352,7 +21291,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
- assert( bufpt>buf );
+ assert( bufpt>zOut );
if( bufpt[-1]=='.' ){
if( flag_altform2 ){
*(bufpt++) = '0';
@@ -19362,7 +21301,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}
}
/* Add the "eNNN" suffix */
- if( flag_exp || xtype==etEXP ){
+ if( xtype==etEXP ){
*(bufpt++) = aDigits[infop->charset];
if( exp<0 ){
*(bufpt++) = '-'; exp = -exp;
@@ -19381,8 +21320,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
/* The converted number is in buf[] and zero terminated. Output it.
** Note that the number is in the usual order, not reversed as with
** integer conversions. */
- length = (int)(bufpt-buf);
- bufpt = buf;
+ length = (int)(bufpt-zOut);
+ bufpt = zOut;
/* Special case: Add leading zeros if the flag_zeropad flag is
** set and we are not left justified */
@@ -19399,7 +21338,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
break;
case etSIZE:
- *(va_arg(ap,int*)) = pAccum->nChar;
+ if( !bArgList ){
+ *(va_arg(ap,int*)) = pAccum->nChar;
+ }
length = width = 0;
break;
case etPERCENT:
@@ -19408,22 +21349,34 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
length = 1;
break;
case etCHARX:
- c = va_arg(ap,int);
- buf[0] = (char)c;
- if( precision>=0 ){
- for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
- length = precision;
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ c = bufpt ? bufpt[0] : 0;
}else{
- length =1;
+ c = va_arg(ap,int);
}
+ if( precision>1 ){
+ width -= precision-1;
+ if( width>1 && !flag_leftjustify ){
+ sqlite3AppendChar(pAccum, width-1, ' ');
+ width = 0;
+ }
+ sqlite3AppendChar(pAccum, precision-1, c);
+ }
+ length = 1;
+ buf[0] = c;
bufpt = buf;
break;
case etSTRING:
case etDYNSTRING:
- bufpt = va_arg(ap,char*);
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ }else{
+ bufpt = va_arg(ap,char*);
+ }
if( bufpt==0 ){
bufpt = "";
- }else if( xtype==etDYNSTRING ){
+ }else if( xtype==etDYNSTRING && !bArgList ){
zExtra = bufpt;
}
if( precision>=0 ){
@@ -19439,7 +21392,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
int needQuote;
char ch;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
- char *escarg = va_arg(ap,char*);
+ char *escarg;
+
+ if( bArgList ){
+ escarg = getTextArg(pArgList);
+ }else{
+ escarg = va_arg(ap,char*);
+ }
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
k = precision;
@@ -19451,7 +21410,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3Malloc( n );
if( bufpt==0 ){
- pAccum->mallocFailed = 1;
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
return;
}
}else{
@@ -19474,7 +21433,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}
case etTOKEN: {
Token *pToken = va_arg(ap, Token*);
- if( pToken ){
+ assert( bArgList==0 );
+ if( pToken && pToken->n ){
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
@@ -19484,12 +21444,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
SrcList *pSrc = va_arg(ap, SrcList*);
int k = va_arg(ap, int);
struct SrcList_item *pItem = &pSrc->a[k];
+ assert( bArgList==0 );
assert( k>=0 && k<pSrc->nSrc );
if( pItem->zDatabase ){
- sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
+ sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
sqlite3StrAccumAppend(pAccum, ".", 1);
}
- sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
+ sqlite3StrAccumAppendAll(pAccum, pItem->zName);
length = width = 0;
break;
}
@@ -19503,84 +21464,117 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
** "length" characters long. The field width is "width". Do
** the output.
*/
- if( !flag_leftjustify ){
- register int nspace;
- nspace = width-length;
- if( nspace>0 ){
- appendSpace(pAccum, nspace);
- }
- }
- if( length>0 ){
- sqlite3StrAccumAppend(pAccum, bufpt, length);
- }
- if( flag_leftjustify ){
- register int nspace;
- nspace = width-length;
- if( nspace>0 ){
- appendSpace(pAccum, nspace);
- }
- }
+ width -= length;
+ if( width>0 && !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
+ sqlite3StrAccumAppend(pAccum, bufpt, length);
+ if( width>0 && flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
+
if( zExtra ){
sqlite3_free(zExtra);
+ zExtra = 0;
}
}/* End for loop over the format string */
} /* End of function */
/*
-** Append N bytes of text from z to the StrAccum object.
+** Enlarge the memory allocation on a StrAccum object so that it is
+** able to accept at least N more bytes of text.
+**
+** Return the number of bytes of text that StrAccum is able to accept
+** after the attempted enlargement. The value returned might be zero.
*/
-SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
- assert( z!=0 || N==0 );
- if( p->tooBig | p->mallocFailed ){
- testcase(p->tooBig);
- testcase(p->mallocFailed);
- return;
+static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
+ char *zNew;
+ assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */
+ if( p->accError ){
+ testcase(p->accError==STRACCUM_TOOBIG);
+ testcase(p->accError==STRACCUM_NOMEM);
+ return 0;
}
- if( N<0 ){
- N = sqlite3Strlen30(z);
+ if( !p->useMalloc ){
+ N = p->nAlloc - p->nChar - 1;
+ setStrAccumError(p, STRACCUM_TOOBIG);
+ return N;
+ }else{
+ char *zOld = (p->zText==p->zBase ? 0 : p->zText);
+ i64 szNew = p->nChar;
+ szNew += N + 1;
+ if( szNew > p->mxAlloc ){
+ sqlite3StrAccumReset(p);
+ setStrAccumError(p, STRACCUM_TOOBIG);
+ return 0;
+ }else{
+ p->nAlloc = (int)szNew;
+ }
+ if( p->useMalloc==1 ){
+ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
+ }else{
+ zNew = sqlite3_realloc(zOld, p->nAlloc);
+ }
+ if( zNew ){
+ assert( p->zText!=0 || p->nChar==0 );
+ if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
+ p->zText = zNew;
+ }else{
+ sqlite3StrAccumReset(p);
+ setStrAccumError(p, STRACCUM_NOMEM);
+ return 0;
+ }
}
- if( N==0 || NEVER(z==0) ){
- return;
+ return N;
+}
+
+/*
+** Append N copies of character c to the given string buffer.
+*/
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){
+ if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return;
+ while( (N--)>0 ) p->zText[p->nChar++] = c;
+}
+
+/*
+** The StrAccum "p" is not large enough to accept N new bytes of z[].
+** So enlarge if first, then do the append.
+**
+** This is a helper routine to sqlite3StrAccumAppend() that does special-case
+** work (enlarging the buffer) using tail recursion, so that the
+** sqlite3StrAccumAppend() routine can use fast calling semantics.
+*/
+static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
+ N = sqlite3StrAccumEnlarge(p, N);
+ if( N>0 ){
+ memcpy(&p->zText[p->nChar], z, N);
+ p->nChar += N;
}
+}
+
+/*
+** Append N bytes of text from z to the StrAccum object. Increase the
+** size of the memory allocation for StrAccum if necessary.
+*/
+SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
+ assert( z!=0 );
+ assert( p->zText!=0 || p->nChar==0 || p->accError );
+ assert( N>=0 );
+ assert( p->accError==0 || p->nAlloc==0 );
if( p->nChar+N >= p->nAlloc ){
- char *zNew;
- if( !p->useMalloc ){
- p->tooBig = 1;
- N = p->nAlloc - p->nChar - 1;
- if( N<=0 ){
- return;
- }
- }else{
- char *zOld = (p->zText==p->zBase ? 0 : p->zText);
- i64 szNew = p->nChar;
- szNew += N + 1;
- if( szNew > p->mxAlloc ){
- sqlite3StrAccumReset(p);
- p->tooBig = 1;
- return;
- }else{
- p->nAlloc = (int)szNew;
- }
- if( p->useMalloc==1 ){
- zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
- }else{
- zNew = sqlite3_realloc(zOld, p->nAlloc);
- }
- if( zNew ){
- if( zOld==0 ) memcpy(zNew, p->zText, p->nChar);
- p->zText = zNew;
- }else{
- p->mallocFailed = 1;
- sqlite3StrAccumReset(p);
- return;
- }
- }
+ enlargeAndAppend(p,z,N);
+ }else{
+ assert( p->zText );
+ p->nChar += N;
+ memcpy(&p->zText[p->nChar-N], z, N);
}
- memcpy(&p->zText[p->nChar], z, N);
- p->nChar += N;
}
/*
+** Append the complete text of zero-terminated string z[] to the p string.
+*/
+SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
+ sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
+}
+
+
+/*
** Finish off a string by making sure it is zero-terminated.
** Return a pointer to the resulting string. Return a NULL
** pointer if any kind of error was encountered.
@@ -19597,7 +21591,7 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
memcpy(p->zText, p->zBase, p->nChar+1);
}else{
- p->mallocFailed = 1;
+ setStrAccumError(p, STRACCUM_NOMEM);
}
}
}
@@ -19628,8 +21622,7 @@ SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx)
p->nAlloc = n;
p->mxAlloc = mx;
p->useMalloc = 1;
- p->tooBig = 0;
- p->mallocFailed = 0;
+ p->accError = 0;
}
/*
@@ -19644,9 +21637,9 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a
sqlite3StrAccumInit(&acc, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
acc.db = db;
- sqlite3VXPrintf(&acc, 1, zFormat, ap);
+ sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
- if( acc.mallocFailed ){
+ if( acc.accError==STRACCUM_NOMEM ){
db->mallocFailed = 1;
}
return z;
@@ -19667,7 +21660,7 @@ SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
/*
** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
-** the string and before returnning. This routine is intended to be used
+** the string and before returning. This routine is intended to be used
** to modify an existing string. For example:
**
** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
@@ -19800,17 +21793,78 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
}
#endif
-#ifndef SQLITE_OMIT_TRACE
+#ifdef SQLITE_DEBUG
+/*************************************************************************
+** Routines for implementing the "TreeView" display of hierarchical
+** data structures for debugging.
+**
+** The main entry points (coded elsewhere) are:
+** sqlite3TreeViewExpr(0, pExpr, 0);
+** sqlite3TreeViewExprList(0, pList, 0, 0);
+** sqlite3TreeViewSelect(0, pSelect, 0);
+** Insert calls to those routines while debugging in order to display
+** a diagram of Expr, ExprList, and Select objects.
+**
+*/
+/* Add a new subitem to the tree. The moreToFollow flag indicates that this
+** is not the last item in the tree. */
+SQLITE_PRIVATE TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
+ if( p==0 ){
+ p = sqlite3_malloc( sizeof(*p) );
+ if( p==0 ) return 0;
+ memset(p, 0, sizeof(*p));
+ }else{
+ p->iLevel++;
+ }
+ assert( moreToFollow==0 || moreToFollow==1 );
+ if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
+ return p;
+}
+/* Finished with one layer of the tree */
+SQLITE_PRIVATE void sqlite3TreeViewPop(TreeView *p){
+ if( p==0 ) return;
+ p->iLevel--;
+ if( p->iLevel<0 ) sqlite3_free(p);
+}
+/* Generate a single line of output for the tree, with a prefix that contains
+** all the appropriate tree lines */
+SQLITE_PRIVATE void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
+ va_list ap;
+ int i;
+ StrAccum acc;
+ char zBuf[500];
+ sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0);
+ acc.useMalloc = 0;
+ if( p ){
+ for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
+ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
+ }
+ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
+ }
+ va_start(ap, zFormat);
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ va_end(ap);
+ if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
+ sqlite3StrAccumFinish(&acc);
+ fprintf(stdout,"%s", zBuf);
+ fflush(stdout);
+}
+/* Shorthand for starting a new tree item that consists of a single label */
+SQLITE_PRIVATE void sqlite3TreeViewItem(TreeView *p, const char *zLabel, u8 moreToFollow){
+ p = sqlite3TreeViewPush(p, moreToFollow);
+ sqlite3TreeViewLine(p, "%s", zLabel);
+}
+#endif /* SQLITE_DEBUG */
+
/*
** variable-argument wrapper around sqlite3VXPrintf().
*/
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
+SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){
va_list ap;
va_start(ap,zFormat);
- sqlite3VXPrintf(p, 1, zFormat, ap);
+ sqlite3VXPrintf(p, bFlags, zFormat, ap);
va_end(ap);
}
-#endif
/************** End of printf.c **********************************************/
/************** Begin file random.c ******************************************/
@@ -19843,24 +21897,11 @@ static SQLITE_WSD struct sqlite3PrngType {
} sqlite3Prng;
/*
-** Get a single 8-bit random value from the RC4 PRNG. The Mutex
-** must be held while executing this routine.
-**
-** Why not just use a library random generator like lrand48() for this?
-** Because the OP_NewRowid opcode in the VDBE depends on having a very
-** good source of random numbers. The lrand48() library function may
-** well be good enough. But maybe not. Or maybe lrand48() has some
-** subtle problems on some systems that could cause problems. It is hard
-** to know. To minimize the risk of problems due to bad lrand48()
-** implementations, SQLite uses this random number generator based
-** on RC4, which we know works very well.
-**
-** (Later): Actually, OP_NewRowid does not depend on a good source of
-** randomness any more. But we will leave this code in all the same.
+** Return N random bytes.
*/
-static u8 randomByte(void){
+SQLITE_API void sqlite3_randomness(int N, void *pBuf){
unsigned char t;
-
+ unsigned char *zBuf = pBuf;
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
** state vector. If writable static data is unsupported on the target,
@@ -19875,6 +21916,16 @@ static u8 randomByte(void){
# define wsdPrng sqlite3Prng
#endif
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
+ sqlite3_mutex_enter(mutex);
+#endif
+
+ if( N<=0 ){
+ wsdPrng.isInit = 0;
+ sqlite3_mutex_leave(mutex);
+ return;
+ }
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
@@ -19903,29 +21954,16 @@ static u8 randomByte(void){
wsdPrng.isInit = 1;
}
- /* Generate and return single random byte
- */
- wsdPrng.i++;
- t = wsdPrng.s[wsdPrng.i];
- wsdPrng.j += t;
- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = t;
- t += wsdPrng.s[wsdPrng.i];
- return wsdPrng.s[t];
-}
-
-/*
-** Return N random bytes.
-*/
-SQLITE_API void sqlite3_randomness(int N, void *pBuf){
- unsigned char *zBuf = pBuf;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
-#endif
- sqlite3_mutex_enter(mutex);
- while( N-- ){
- *(zBuf++) = randomByte();
- }
+ assert( N>0 );
+ do{
+ wsdPrng.i++;
+ t = wsdPrng.s[wsdPrng.i];
+ wsdPrng.j += t;
+ wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
+ wsdPrng.s[wsdPrng.j] = t;
+ t += wsdPrng.s[wsdPrng.i];
+ *(zBuf++) = wsdPrng.s[t];
+ }while( --N );
sqlite3_mutex_leave(mutex);
}
@@ -19954,12 +21992,273 @@ SQLITE_PRIVATE void sqlite3PrngRestoreState(void){
sizeof(sqlite3Prng)
);
}
-SQLITE_PRIVATE void sqlite3PrngResetState(void){
- GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
-}
#endif /* SQLITE_OMIT_BUILTIN_TEST */
/************** End of random.c **********************************************/
+/************** Begin file threads.c *****************************************/
+/*
+** 2012 July 21
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file presents a simple cross-platform threading interface for
+** use internally by SQLite.
+**
+** A "thread" can be created using sqlite3ThreadCreate(). This thread
+** runs independently of its creator until it is joined using
+** sqlite3ThreadJoin(), at which point it terminates.
+**
+** Threads do not have to be real. It could be that the work of the
+** "thread" is done by the main thread at either the sqlite3ThreadCreate()
+** or sqlite3ThreadJoin() call. This is, in fact, what happens in
+** single threaded systems. Nothing in SQLite requires multiple threads.
+** This interface exists so that applications that want to take advantage
+** of multiple cores can do so, while also allowing applications to stay
+** single-threaded if desired.
+*/
+
+#if SQLITE_MAX_WORKER_THREADS>0
+
+/********************************* Unix Pthreads ****************************/
+#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
+
+#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
+/* #include <pthread.h> */
+
+/* A running thread */
+struct SQLiteThread {
+ pthread_t tid; /* Thread ID */
+ int done; /* Set to true when thread finishes */
+ void *pOut; /* Result returned by the thread */
+ void *(*xTask)(void*); /* The thread routine */
+ void *pIn; /* Argument to the thread */
+};
+
+/* Create a new thread */
+SQLITE_PRIVATE int sqlite3ThreadCreate(
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ SQLiteThread *p;
+ int rc;
+
+ assert( ppThread!=0 );
+ assert( xTask!=0 );
+ /* This routine is never used in single-threaded mode */
+ assert( sqlite3GlobalConfig.bCoreMutex!=0 );
+
+ *ppThread = 0;
+ p = sqlite3Malloc(sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ memset(p, 0, sizeof(*p));
+ p->xTask = xTask;
+ p->pIn = pIn;
+ if( sqlite3FaultSim(200) ){
+ rc = 1;
+ }else{
+ rc = pthread_create(&p->tid, 0, xTask, pIn);
+ }
+ if( rc ){
+ p->done = 1;
+ p->pOut = xTask(pIn);
+ }
+ *ppThread = p;
+ return SQLITE_OK;
+}
+
+/* Get the results of the thread */
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
+ int rc;
+
+ assert( ppOut!=0 );
+ if( NEVER(p==0) ) return SQLITE_NOMEM;
+ if( p->done ){
+ *ppOut = p->pOut;
+ rc = SQLITE_OK;
+ }else{
+ rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK;
+ }
+ sqlite3_free(p);
+ return rc;
+}
+
+#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
+/******************************** End Unix Pthreads *************************/
+
+
+/********************************* Win32 Threads ****************************/
+#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_THREADSAFE>0
+
+#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
+#include <process.h>
+
+/* A running thread */
+struct SQLiteThread {
+ void *tid; /* The thread handle */
+ unsigned id; /* The thread identifier */
+ void *(*xTask)(void*); /* The routine to run as a thread */
+ void *pIn; /* Argument to xTask */
+ void *pResult; /* Result of xTask */
+};
+
+/* Thread procedure Win32 compatibility shim */
+static unsigned __stdcall sqlite3ThreadProc(
+ void *pArg /* IN: Pointer to the SQLiteThread structure */
+){
+ SQLiteThread *p = (SQLiteThread *)pArg;
+
+ assert( p!=0 );
+#if 0
+ /*
+ ** This assert appears to trigger spuriously on certain
+ ** versions of Windows, possibly due to _beginthreadex()
+ ** and/or CreateThread() not fully setting their thread
+ ** ID parameter before starting the thread.
+ */
+ assert( p->id==GetCurrentThreadId() );
+#endif
+ assert( p->xTask!=0 );
+ p->pResult = p->xTask(p->pIn);
+
+ _endthreadex(0);
+ return 0; /* NOT REACHED */
+}
+
+/* Create a new thread */
+SQLITE_PRIVATE int sqlite3ThreadCreate(
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ SQLiteThread *p;
+
+ assert( ppThread!=0 );
+ assert( xTask!=0 );
+ *ppThread = 0;
+ p = sqlite3Malloc(sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ if( sqlite3GlobalConfig.bCoreMutex==0 ){
+ memset(p, 0, sizeof(*p));
+ }else{
+ p->xTask = xTask;
+ p->pIn = pIn;
+ p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
+ if( p->tid==0 ){
+ memset(p, 0, sizeof(*p));
+ }
+ }
+ if( p->xTask==0 ){
+ p->id = GetCurrentThreadId();
+ p->pResult = xTask(pIn);
+ }
+ *ppThread = p;
+ return SQLITE_OK;
+}
+
+SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject); /* os_win.c */
+
+/* Get the results of the thread */
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
+ DWORD rc;
+ BOOL bRc;
+
+ assert( ppOut!=0 );
+ if( NEVER(p==0) ) return SQLITE_NOMEM;
+ if( p->xTask==0 ){
+ assert( p->id==GetCurrentThreadId() );
+ rc = WAIT_OBJECT_0;
+ assert( p->tid==0 );
+ }else{
+ assert( p->id!=0 && p->id!=GetCurrentThreadId() );
+ rc = sqlite3Win32Wait((HANDLE)p->tid);
+ assert( rc!=WAIT_IO_COMPLETION );
+ bRc = CloseHandle((HANDLE)p->tid);
+ assert( bRc );
+ }
+ if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
+ sqlite3_free(p);
+ return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
+}
+
+#endif /* SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT */
+/******************************** End Win32 Threads *************************/
+
+
+/********************************* Single-Threaded **************************/
+#ifndef SQLITE_THREADS_IMPLEMENTED
+/*
+** This implementation does not actually create a new thread. It does the
+** work of the thread in the main thread, when either the thread is created
+** or when it is joined
+*/
+
+/* A running thread */
+struct SQLiteThread {
+ void *(*xTask)(void*); /* The routine to run as a thread */
+ void *pIn; /* Argument to xTask */
+ void *pResult; /* Result of xTask */
+};
+
+/* Create a new thread */
+SQLITE_PRIVATE int sqlite3ThreadCreate(
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ SQLiteThread *p;
+
+ assert( ppThread!=0 );
+ assert( xTask!=0 );
+ *ppThread = 0;
+ p = sqlite3Malloc(sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ if( (SQLITE_PTR_TO_INT(p)/17)&1 ){
+ p->xTask = xTask;
+ p->pIn = pIn;
+ }else{
+ p->xTask = 0;
+ p->pResult = xTask(pIn);
+ }
+ *ppThread = p;
+ return SQLITE_OK;
+}
+
+/* Get the results of the thread */
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
+
+ assert( ppOut!=0 );
+ if( NEVER(p==0) ) return SQLITE_NOMEM;
+ if( p->xTask ){
+ *ppOut = p->xTask(p->pIn);
+ }else{
+ *ppOut = p->pResult;
+ }
+ sqlite3_free(p);
+
+#if defined(SQLITE_TEST)
+ {
+ void *pTstAlloc = sqlite3Malloc(10);
+ if (!pTstAlloc) return SQLITE_NOMEM;
+ sqlite3_free(pTstAlloc);
+ }
+#endif
+
+ return SQLITE_OK;
+}
+
+#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */
+/****************************** End Single-Threaded *************************/
+#endif /* SQLITE_MAX_WORKER_THREADS>0 */
+
+/************** End of threads.c *********************************************/
/************** Begin file utf.c *********************************************/
/*
** 2004 April 13
@@ -19996,6 +22295,7 @@ SQLITE_PRIVATE void sqlite3PrngResetState(void){
** 0xfe 0xff big-endian utf-16 follows
**
*/
+/* #include <assert.h> */
#ifndef SQLITE_AMALGAMATION
/*
@@ -20108,8 +22408,8 @@ static const unsigned char sqlite3Utf8Trans1[] = {
** and rendered as themselves even though they are technically
** invalid characters.
**
-** * This routine accepts an infinite number of different UTF8 encodings
-** for unicode values 0x80 and greater. It do not change over-length
+** * This routine accepts over-length UTF8 encodings
+** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
#define READ_UTF8(zIn, zTerm, c) \
@@ -20124,25 +22424,23 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
SQLITE_PRIVATE u32 sqlite3Utf8Read(
- const unsigned char *zIn, /* First byte of UTF-8 character */
- const unsigned char **pzNext /* Write first byte past UTF-8 char here */
+ const unsigned char **pz /* Pointer to string from which to read char */
){
unsigned int c;
/* Same as READ_UTF8() above but without the zTerm parameter.
** For this routine, we assume the UTF8 string is always zero-terminated.
*/
- c = *(zIn++);
+ c = *((*pz)++);
if( c>=0xc0 ){
c = sqlite3Utf8Trans1[c-0xc0];
- while( (*zIn & 0xc0)==0x80 ){
- c = (c<<6) + (0x3f & *(zIn++));
+ while( (*(*pz) & 0xc0)==0x80 ){
+ c = (c<<6) + (0x3f & *((*pz)++));
}
if( c<0x80
|| (c&0xFFFFF800)==0xD800
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
}
- *pzNext = zIn;
return c;
}
@@ -20161,7 +22459,7 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
** desiredEnc. It is an error if the string is already of the desired
** encoding, or if *pMem does not contain a string value.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
int len; /* Maximum length of output string in bytes */
unsigned char *zOut; /* Output buffer */
unsigned char *zIn; /* Input iterator */
@@ -20243,7 +22541,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
if( desiredEnc==SQLITE_UTF16LE ){
/* UTF-8 -> UTF-16 Little-endian */
while( zIn<zTerm ){
- /* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
READ_UTF8(zIn, zTerm, c);
WRITE_UTF16LE(z, c);
}
@@ -20251,7 +22548,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
assert( desiredEnc==SQLITE_UTF16BE );
/* UTF-8 -> UTF-16 Big-endian */
while( zIn<zTerm ){
- /* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
READ_UTF8(zIn, zTerm, c);
WRITE_UTF16BE(z, c);
}
@@ -20278,12 +22574,13 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
*z = 0;
assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
+ c = pMem->flags;
sqlite3VdbeMemRelease(pMem);
- pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
+ pMem->flags = MEM_Str|MEM_Term|(c&MEM_AffMask);
pMem->enc = desiredEnc;
- pMem->flags |= (MEM_Term|MEM_Dyn);
pMem->z = (char*)zOut;
pMem->zMalloc = pMem->z;
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->z);
translate_out:
#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
@@ -20379,7 +22676,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){
u32 c;
while( zIn[0] && zOut<=zIn ){
- c = sqlite3Utf8Read(zIn, (const u8**)&zIn);
+ c = sqlite3Utf8Read((const u8**)&zIn);
if( c!=0xfffd ){
WRITE_UTF8(zOut, c);
}
@@ -20409,38 +22706,11 @@ SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 e
}
assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
- assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed );
assert( m.z || db->mallocFailed );
return m.z;
}
/*
-** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
-** enc. A pointer to the new string is returned, and the value of *pnOut
-** is set to the length of the returned string in bytes. The call should
-** arrange to call sqlite3DbFree() on the returned pointer when it is
-** no longer required.
-**
-** If a malloc failure occurs, NULL is returned and the db.mallocFailed
-** flag set.
-*/
-#ifdef SQLITE_ENABLE_STAT2
-SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
- Mem m;
- memset(&m, 0, sizeof(m));
- m.db = db;
- sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
- if( sqlite3VdbeMemTranslate(&m, enc) ){
- assert( db->mallocFailed );
- return 0;
- }
- assert( m.z==m.zMalloc );
- *pnOut = m.n;
- return m.z;
-}
-#endif
-
-/*
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
** Return the number of bytes in the first nChar unicode characters
** in pZ. nChar must be non-negative.
@@ -20484,7 +22754,7 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
assert( n>0 && n<=4 );
z[0] = 0;
z = zBuf;
- c = sqlite3Utf8Read(z, (const u8**)&z);
+ c = sqlite3Utf8Read((const u8**)&z);
t = i;
if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD;
if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD;
@@ -20538,6 +22808,7 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
** strings, and stuff like that.
**
*/
+/* #include <stdarg.h> */
#ifdef SQLITE_HAVE_ISNAN
# include <math.h>
#endif
@@ -20552,6 +22823,24 @@ SQLITE_PRIVATE void sqlite3Coverage(int x){
}
#endif
+/*
+** Give a callback to the test harness that can be used to simulate faults
+** in places where it is difficult or expensive to do so purely by means
+** of inputs.
+**
+** The intent of the integer argument is to let the fault simulator know
+** which of multiple sqlite3FaultSim() calls has been hit.
+**
+** Return whatever integer value the test callback returns, or return
+** SQLITE_OK if no test callback is installed.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+SQLITE_PRIVATE int sqlite3FaultSim(int iTest){
+ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback;
+ return xCallback ? xCallback(iTest) : SQLITE_OK;
+}
+#endif
+
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Return true if the floating point value is Not a Number (NaN).
@@ -20615,6 +22904,15 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
}
/*
+** Set the current error code to err_code and clear any prior error message.
+*/
+SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
+ assert( db!=0 );
+ db->errCode = err_code;
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr);
+}
+
+/*
** Set the most recent error code and error string for the sqlite
** handle "db". The error code is set to "err_code".
**
@@ -20635,19 +22933,18 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
** should be called with err_code set to SQLITE_OK and zFormat set
** to NULL.
*/
-SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
- if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
- db->errCode = err_code;
- if( zFormat ){
- char *z;
- va_list ap;
- va_start(ap, zFormat);
- z = sqlite3VMPrintf(db, zFormat, ap);
- va_end(ap);
- sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
- }else{
- sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
- }
+SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *zFormat, ...){
+ assert( db!=0 );
+ db->errCode = err_code;
+ if( zFormat==0 ){
+ sqlite3Error(db, err_code);
+ }else if( db->pErr || (db->pErr = sqlite3ValueNew(db))!=0 ){
+ char *z;
+ va_list ap;
+ va_start(ap, zFormat);
+ z = sqlite3VMPrintf(db, zFormat, ap);
+ va_end(ap);
+ sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
}
}
@@ -20661,12 +22958,12 @@ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat,
** %T Insert a token
** %S Insert the first element of a SrcList
**
-** This function should be used to report any error that occurs whilst
+** This function should be used to report any error that occurs while
** compiling an SQL statement (i.e. within sqlite3_prepare()). The
** last thing the sqlite3_prepare() function does is copy the error
** stored by this function into the database handle using sqlite3Error().
-** Function sqlite3Error() should be used during statement execution
-** (sqlite3_step() etc.).
+** Functions sqlite3Error() or sqlite3ErrorWithMsg() should be used
+** during statement execution (sqlite3_step() etc.).
*/
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
char *zMsg;
@@ -20699,7 +22996,7 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
** occur.
**
** 2002-Feb-14: This routine is extended to remove MS-Access style
-** brackets from around identifers. For example: "[a-b-c]" becomes
+** brackets from around identifiers. For example: "[a-b-c]" becomes
** "a-b-c".
*/
SQLITE_PRIVATE int sqlite3Dequote(char *z){
@@ -20714,7 +23011,8 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
case '[': quote = ']'; break; /* For MS SqlServer compatibility */
default: return -1;
}
- for(i=1, j=0; ALWAYS(z[i]); i++){
+ for(i=1, j=0;; i++){
+ assert( z[i] );
if( z[i]==quote ){
if( z[i+1]==quote ){
z[j++] = quote;
@@ -20737,13 +23035,13 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
** Some systems have stricmp(). Others have strcasecmp(). Because
** there is no consistency, we will define our own.
**
-** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows
-** applications and extensions to compare the contents of two buffers
-** containing UTF-8 strings in a case-independent fashion, using the same
-** definition of case independence that SQLite uses internally when
-** comparing identifiers.
+** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and
+** sqlite3_strnicmp() APIs allow applications and extensions to compare
+** the contents of two buffers containing UTF-8 strings in a
+** case-independent fashion, using the same definition of "case
+** independence" that SQLite uses internally when comparing identifiers.
*/
-SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){
+SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){
register unsigned char *a, *b;
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
@@ -20782,7 +23080,7 @@ SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
*/
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
#ifndef SQLITE_OMIT_FLOATING_POINT
- int incr = (enc==SQLITE_UTF8?1:2);
+ int incr;
const char *zEnd = z + length;
/* sign * significand * (10 ^ (esign * exponent)) */
int sign = 1; /* sign of significand */
@@ -20793,10 +23091,22 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
int eValid = 1; /* True exponent is either not used or is well-formed */
double result;
int nDigits = 0;
+ int nonNum = 0;
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
- if( enc==SQLITE_UTF16BE ) z++;
+ if( enc==SQLITE_UTF8 ){
+ incr = 1;
+ }else{
+ int i;
+ incr = 2;
+ assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ for(i=3-enc; i<length && z[i]==0; i+=2){}
+ nonNum = i<length;
+ zEnd = z+i+enc-3;
+ z += (enc&1);
+ }
/* skip leading spaces */
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
@@ -20852,7 +23162,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
}
/* copy digits to exponent */
while( z<zEnd && sqlite3Isdigit(*z) ){
- e = e*10 + (*z - '0');
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
z+=incr;
eValid = 1;
}
@@ -20892,7 +23202,7 @@ do_atof_calc:
/* if exponent, scale significand as appropriate
** and store in result. */
if( e ){
- double scale = 1.0;
+ LONGDOUBLE_TYPE scale = 1.0;
/* attempt to handle extremely small/large numbers better */
if( e>307 && e<342 ){
while( e%308 ) { scale *= 1.0e+1; e -= 1; }
@@ -20903,6 +23213,12 @@ do_atof_calc:
result = s * scale;
result *= 1.0e+308;
}
+ }else if( e>=342 ){
+ if( esign<0 ){
+ result = 0.0*s;
+ }else{
+ result = 1e308*1e308*s; /* Infinity */
+ }
}else{
/* 1.0e+22 is the largest power of 10 than can be
** represented exactly. */
@@ -20923,7 +23239,7 @@ do_atof_calc:
*pResult = result;
/* return true if number and no extra non-whitespace chracters after */
- return z>=zEnd && nDigits>0 && eValid;
+ return z>=zEnd && nDigits>0 && eValid && nonNum==0;
#else
return !sqlite3Atoi64(z, pResult, length, enc);
#endif /* SQLITE_OMIT_FLOATING_POINT */
@@ -20960,33 +23276,45 @@ static int compare2pow63(const char *zNum, int incr){
return c;
}
-
/*
-** Convert zNum to a 64-bit signed integer.
+** Convert zNum to a 64-bit signed integer. zNum must be decimal. This
+** routine does *not* accept hexadecimal notation.
**
** If the zNum value is representable as a 64-bit twos-complement
** integer, then write that value into *pNum and return 0.
**
-** If zNum is exactly 9223372036854665808, return 2. This special
-** case is broken out because while 9223372036854665808 cannot be a
-** signed 64-bit integer, its negative -9223372036854665808 can be.
+** If zNum is exactly 9223372036854775808, return 2. This special
+** case is broken out because while 9223372036854775808 cannot be a
+** signed 64-bit integer, its negative -9223372036854775808 can be.
**
** If zNum is too big for a 64-bit integer and is not
-** 9223372036854665808 then return 1.
+** 9223372036854775808 or if zNum contains any non-numeric text,
+** then return 1.
**
** length is the number of bytes in the string (bytes, not characters).
** The string is not necessarily zero-terminated. The encoding is
** given by enc.
*/
SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
- int incr = (enc==SQLITE_UTF8?1:2);
+ int incr;
u64 u = 0;
int neg = 0; /* assume positive */
int i;
int c = 0;
+ int nonNum = 0;
const char *zStart;
const char *zEnd = zNum + length;
- if( enc==SQLITE_UTF16BE ) zNum++;
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
+ if( enc==SQLITE_UTF8 ){
+ incr = 1;
+ }else{
+ incr = 2;
+ assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ for(i=3-enc; i<length && zNum[i]==0; i+=2){}
+ nonNum = i<length;
+ zEnd = zNum+i+enc-3;
+ zNum += (enc&1);
+ }
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
if( zNum<zEnd ){
if( *zNum=='-' ){
@@ -21002,7 +23330,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
u = u*10 + c - '0';
}
if( u>LARGEST_INT64 ){
- *pNum = SMALLEST_INT64;
+ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
}else if( neg ){
*pNum = -(i64)u;
}else{
@@ -21011,7 +23339,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
- if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
+ if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr || nonNum ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranteeing that it is too large) */
return 1;
@@ -21033,16 +23361,49 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
/* zNum is exactly 9223372036854775808. Fits if negative. The
** special case 2 overflow if positive */
assert( u-1==LARGEST_INT64 );
- assert( (*pNum)==SMALLEST_INT64 );
return neg ? 0 : 2;
}
}
}
/*
+** Transform a UTF-8 integer literal, in either decimal or hexadecimal,
+** into a 64-bit signed integer. This routine accepts hexadecimal literals,
+** whereas sqlite3Atoi64() does not.
+**
+** Returns:
+**
+** 0 Successful transformation. Fits in a 64-bit signed integer.
+** 1 Integer too large for a 64-bit signed integer or is malformed
+** 2 Special case of 9223372036854775808
+*/
+SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( z[0]=='0'
+ && (z[1]=='x' || z[1]=='X')
+ && sqlite3Isxdigit(z[2])
+ ){
+ u64 u = 0;
+ int i, k;
+ for(i=2; z[i]=='0'; i++){}
+ for(k=i; sqlite3Isxdigit(z[k]); k++){
+ u = u*16 + sqlite3HexToInt(z[k]);
+ }
+ memcpy(pOut, &u, 8);
+ return (z[k]==0 && k-i<=16) ? 0 : 1;
+ }else
+#endif /* SQLITE_OMIT_HEX_INTEGER */
+ {
+ return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
+ }
+}
+
+/*
** If zNum represents an integer that will fit in 32-bits, then set
** *pValue to that integer and return true. Otherwise return false.
**
+** This routine accepts both decimal and hexadecimal notation for integers.
+**
** Any non-numeric characters that following zNum are ignored.
** This is different from sqlite3Atoi64() which requires the
** input number to be zero-terminated.
@@ -21057,7 +23418,25 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){
}else if( zNum[0]=='+' ){
zNum++;
}
- while( zNum[0]=='0' ) zNum++;
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ else if( zNum[0]=='0'
+ && (zNum[1]=='x' || zNum[1]=='X')
+ && sqlite3Isxdigit(zNum[2])
+ ){
+ u32 u = 0;
+ zNum += 2;
+ while( zNum[0]=='0' ) zNum++;
+ for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){
+ u = u*16 + sqlite3HexToInt(zNum[i]);
+ }
+ if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
+ memcpy(pValue, &u, 4);
+ return 1;
+ }else{
+ return 0;
+ }
+ }
+#endif
for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){
v = v*10 + c;
}
@@ -21121,7 +23500,7 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){
** bit clear. Except, if we get to the 9th byte, it stores the full
** 8 bits and is the last byte.
*/
-SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
+static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){
int i, j, n;
u8 buf[10];
if( v & (((u64)0xff000000)<<32) ){
@@ -21145,28 +23524,17 @@ SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
}
return n;
}
-
-/*
-** This routine is a faster version of sqlite3PutVarint() that only
-** works for 32-bit positive integers and which is optimized for
-** the common case of small integers. A MACRO version, putVarint32,
-** is provided which inlines the single-byte case. All code should use
-** the MACRO version as this function assumes the single-byte case has
-** already been handled.
-*/
-SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char *p, u32 v){
-#ifndef putVarint32
- if( (v & ~0x7f)==0 ){
- p[0] = v;
+SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
+ if( v<=0x7f ){
+ p[0] = v&0x7f;
return 1;
}
-#endif
- if( (v & ~0x3fff)==0 ){
- p[0] = (u8)((v>>7) | 0x80);
- p[1] = (u8)(v & 0x7f);
+ if( v<=0x3fff ){
+ p[0] = ((v>>7)&0x7f)|0x80;
+ p[1] = v&0x7f;
return 2;
}
- return sqlite3PutVarint(p, v);
+ return putVarint64(p,v);
}
/*
@@ -21493,7 +23861,8 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
** Read or write a four-byte big-endian integer value.
*/
SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){
- return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+ testcase( p[0]&0x80 );
+ return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}
SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){
p[0] = (u8)(v>>24);
@@ -21614,13 +23983,12 @@ SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){
testcase( iA>0 && LARGEST_INT64 - iA == iB );
testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 );
if( iA>0 && LARGEST_INT64 - iA < iB ) return 1;
- *pA += iB;
}else{
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 );
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 );
if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
- *pA += iB;
}
+ *pA += iB;
return 0;
}
SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
@@ -21644,9 +24012,18 @@ SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
iA0 = iA % TWOPOWER32;
iB1 = iB/TWOPOWER32;
iB0 = iB % TWOPOWER32;
- if( iA1*iB1 != 0 ) return 1;
- assert( iA1*iB0==0 || iA0*iB1==0 );
- r = iA1*iB0 + iA0*iB1;
+ if( iA1==0 ){
+ if( iB1==0 ){
+ *pA *= iB;
+ return 0;
+ }
+ r = iA0*iB1;
+ }else if( iB1==0 ){
+ r = iA1*iB0;
+ }else{
+ /* If both iA1 and iB1 are non-zero, overflow will result */
+ return 1;
+ }
testcase( r==(-TWOPOWER31)-1 );
testcase( r==(-TWOPOWER31) );
testcase( r==TWOPOWER31 );
@@ -21670,30 +24047,114 @@ SQLITE_PRIVATE int sqlite3AbsInt32(int x){
#ifdef SQLITE_ENABLE_8_3_NAMES
/*
-** If SQLITE_ENABLE_8_3_NAME is set at compile-time and if the database
+** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
** three characters, then shorten the suffix on z[] to be the last three
** characters of the original suffix.
**
+** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
+** do the suffix shortening regardless of URI parameter.
+**
** Examples:
**
** test.db-journal => test.nal
** test.db-wal => test.wal
** test.db-shm => test.shm
+** test.db-mj7f3319fa => test.9fa
*/
SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
- const char *zOk;
- zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
- if( zOk && sqlite3GetBoolean(zOk) ){
+#if SQLITE_ENABLE_8_3_NAMES<2
+ if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) )
+#endif
+ {
int i, sz;
sz = sqlite3Strlen30(z);
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
- if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4);
+ if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4);
}
}
#endif
+/*
+** Find (an approximate) sum of two LogEst values. This computation is
+** not a simple "+" operator because LogEst is stored as a logarithmic
+** value.
+**
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
+ static const unsigned char x[] = {
+ 10, 10, /* 0,1 */
+ 9, 9, /* 2,3 */
+ 8, 8, /* 4,5 */
+ 7, 7, 7, /* 6,7,8 */
+ 6, 6, 6, /* 9,10,11 */
+ 5, 5, 5, /* 12-14 */
+ 4, 4, 4, 4, /* 15-18 */
+ 3, 3, 3, 3, 3, 3, /* 19-24 */
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
+ };
+ if( a>=b ){
+ if( a>b+49 ) return a;
+ if( a>b+31 ) return a+1;
+ return a+x[a-b];
+ }else{
+ if( b>a+49 ) return b;
+ if( b>a+31 ) return b+1;
+ return b+x[b-a];
+ }
+}
+
+/*
+** Convert an integer into a LogEst. In other words, compute an
+** approximation for 10*log2(x).
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
+ LogEst y = 40;
+ if( x<8 ){
+ if( x<2 ) return 0;
+ while( x<8 ){ y -= 10; x <<= 1; }
+ }else{
+ while( x>255 ){ y += 40; x >>= 4; }
+ while( x>15 ){ y += 10; x >>= 1; }
+ }
+ return a[x&7] + y - 10;
+}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Convert a double into a LogEst
+** In other words, compute an approximation for 10*log2(x).
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
+ u64 a;
+ LogEst e;
+ assert( sizeof(x)==8 && sizeof(a)==8 );
+ if( x<=1 ) return 0;
+ if( x<=2000000000 ) return sqlite3LogEst((u64)x);
+ memcpy(&a, &x, 8);
+ e = (a>>52) - 1022;
+ return e*10;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/*
+** Convert a LogEst into an integer.
+*/
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
+ u64 n;
+ if( x<10 ) return 1;
+ n = x%10;
+ x /= 10;
+ if( n>=5 ) n -= 2;
+ else if( n>=1 ) n -= 1;
+ if( x>=3 ){
+ return x>60 ? (u64)LARGEST_INT64 : (n+8)<<(x-3);
+ }
+ return (n+8)>>(3-x);
+}
+
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
@@ -21710,6 +24171,7 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
** This is the implementation of generic hash-tables
** used in SQLite.
*/
+/* #include <assert.h> */
/* Turn bulk memory into a hash table object by initializing the
** fields of the Hash structure.
@@ -21748,12 +24210,11 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){
/*
** The hashing function.
*/
-static unsigned int strHash(const char *z, int nKey){
- int h = 0;
- assert( nKey>=0 );
- while( nKey > 0 ){
- h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
- nKey--;
+static unsigned int strHash(const char *z){
+ unsigned int h = 0;
+ unsigned char c;
+ while( (c = (unsigned char)*z++)!=0 ){
+ h = (h<<3) ^ h ^ sqlite3UpperToLower[c];
}
return h;
}
@@ -21809,7 +24270,11 @@ static int rehash(Hash *pH, unsigned int new_size){
/* The inability to allocates space for a larger hash table is
** a performance hit but it is not a fatal error. So mark the
- ** allocation as a benign.
+ ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of
+ ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero()
+ ** only zeroes the requested number of bytes whereas this module will
+ ** use the actual amount of space allocated for the hash table (which
+ ** may be larger than the requested amount).
*/
sqlite3BeginBenignMalloc();
new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) );
@@ -21821,7 +24286,7 @@ static int rehash(Hash *pH, unsigned int new_size){
pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
memset(new_ht, 0, new_size*sizeof(struct _ht));
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
- unsigned int h = strHash(elem->pKey, elem->nKey) % new_size;
+ unsigned int h = strHash(elem->pKey) % new_size;
next_elem = elem->next;
insertElement(pH, &new_ht[h], elem);
}
@@ -21829,28 +24294,33 @@ static int rehash(Hash *pH, unsigned int new_size){
}
/* This function (for internal use only) locates an element in an
-** hash table that matches the given key. The hash for this key has
-** already been computed and is passed as the 4th parameter.
+** hash table that matches the given key. The hash for this key is
+** also computed and returned in the *pH parameter.
*/
-static HashElem *findElementGivenHash(
+static HashElem *findElementWithHash(
const Hash *pH, /* The pH to be searched */
const char *pKey, /* The key we are searching for */
- int nKey, /* Bytes in key (not counting zero terminator) */
- unsigned int h /* The hash for this key. */
+ unsigned int *pHash /* Write the hash value here */
){
HashElem *elem; /* Used to loop thru the element list */
int count; /* Number of elements left to test */
+ unsigned int h; /* The computed hash */
if( pH->ht ){
- struct _ht *pEntry = &pH->ht[h];
+ struct _ht *pEntry;
+ h = strHash(pKey) % pH->htsize;
+ pEntry = &pH->ht[h];
elem = pEntry->chain;
count = pEntry->count;
}else{
+ h = 0;
elem = pH->first;
count = pH->count;
}
- while( count-- && ALWAYS(elem) ){
- if( elem->nKey==nKey && sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
+ *pHash = h;
+ while( count-- ){
+ assert( elem!=0 );
+ if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
return elem;
}
elem = elem->next;
@@ -21885,7 +24355,7 @@ static void removeElementGivenHash(
}
sqlite3_free( elem );
pH->count--;
- if( pH->count<=0 ){
+ if( pH->count==0 ){
assert( pH->first==0 );
assert( pH->count==0 );
sqlite3HashClear(pH);
@@ -21893,26 +24363,20 @@ static void removeElementGivenHash(
}
/* Attempt to locate an element of the hash table pH with a key
-** that matches pKey,nKey. Return the data for this element if it is
+** that matches pKey. Return the data for this element if it is
** found, or NULL if there is no match.
*/
-SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey){
+SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){
HashElem *elem; /* The element that matches key */
unsigned int h; /* A hash on key */
assert( pH!=0 );
assert( pKey!=0 );
- assert( nKey>=0 );
- if( pH->ht ){
- h = strHash(pKey, nKey) % pH->htsize;
- }else{
- h = 0;
- }
- elem = findElementGivenHash(pH, pKey, nKey, h);
+ elem = findElementWithHash(pH, pKey, &h);
return elem ? elem->data : 0;
}
-/* Insert an element into the hash table pH. The key is pKey,nKey
+/* Insert an element into the hash table pH. The key is pKey
** and the data is "data".
**
** If no element exists with a matching key, then a new
@@ -21926,20 +24390,14 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey)
** If the "data" parameter to this function is NULL, then the
** element corresponding to "key" is removed from the hash table.
*/
-SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, void *data){
+SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){
unsigned int h; /* the hash of the key modulo hash table size */
HashElem *elem; /* Used to loop thru the element list */
HashElem *new_elem; /* New element added to the pH */
assert( pH!=0 );
assert( pKey!=0 );
- assert( nKey>=0 );
- if( pH->htsize ){
- h = strHash(pKey, nKey) % pH->htsize;
- }else{
- h = 0;
- }
- elem = findElementGivenHash(pH,pKey,nKey,h);
+ elem = findElementWithHash(pH,pKey,&h);
if( elem ){
void *old_data = elem->data;
if( data==0 ){
@@ -21947,7 +24405,6 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi
}else{
elem->data = data;
elem->pKey = pKey;
- assert(nKey==elem->nKey);
}
return old_data;
}
@@ -21955,20 +24412,15 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi
new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) );
if( new_elem==0 ) return data;
new_elem->pKey = pKey;
- new_elem->nKey = nKey;
new_elem->data = data;
pH->count++;
if( pH->count>=10 && pH->count > 2*pH->htsize ){
if( rehash(pH, pH->count*2) ){
assert( pH->htsize>0 );
- h = strHash(pKey, nKey) % pH->htsize;
+ h = strHash(pKey) % pH->htsize;
}
}
- if( pH->ht ){
- insertElement(pH, &pH->ht[h], new_elem);
- }else{
- insertElement(pH, 0, new_elem);
- }
+ insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem);
return 0;
}
@@ -21976,2291 +24428,175 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi
/************** Begin file opcodes.c *****************************************/
/* Automatically generated. Do not edit */
/* See the mkopcodec.awk script for details. */
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+#if !defined(SQLITE_OMIT_EXPLAIN) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)
+# define OpHelp(X) "\0" X
+#else
+# define OpHelp(X)
+#endif
SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
static const char *const azName[] = { "?",
- /* 1 */ "Goto",
- /* 2 */ "Gosub",
- /* 3 */ "Return",
- /* 4 */ "Yield",
- /* 5 */ "HaltIfNull",
- /* 6 */ "Halt",
- /* 7 */ "Integer",
- /* 8 */ "Int64",
- /* 9 */ "String",
- /* 10 */ "Null",
- /* 11 */ "Blob",
- /* 12 */ "Variable",
- /* 13 */ "Move",
- /* 14 */ "Copy",
- /* 15 */ "SCopy",
- /* 16 */ "ResultRow",
- /* 17 */ "CollSeq",
- /* 18 */ "Function",
- /* 19 */ "Not",
- /* 20 */ "AddImm",
- /* 21 */ "MustBeInt",
- /* 22 */ "RealAffinity",
- /* 23 */ "Permutation",
- /* 24 */ "Compare",
- /* 25 */ "Jump",
- /* 26 */ "If",
- /* 27 */ "IfNot",
- /* 28 */ "Column",
- /* 29 */ "Affinity",
- /* 30 */ "MakeRecord",
- /* 31 */ "Count",
- /* 32 */ "Savepoint",
- /* 33 */ "AutoCommit",
- /* 34 */ "Transaction",
- /* 35 */ "ReadCookie",
- /* 36 */ "SetCookie",
- /* 37 */ "VerifyCookie",
- /* 38 */ "OpenRead",
- /* 39 */ "OpenWrite",
- /* 40 */ "OpenAutoindex",
- /* 41 */ "OpenEphemeral",
- /* 42 */ "OpenPseudo",
- /* 43 */ "Close",
- /* 44 */ "SeekLt",
- /* 45 */ "SeekLe",
- /* 46 */ "SeekGe",
- /* 47 */ "SeekGt",
- /* 48 */ "Seek",
- /* 49 */ "NotFound",
- /* 50 */ "Found",
- /* 51 */ "IsUnique",
- /* 52 */ "NotExists",
- /* 53 */ "Sequence",
- /* 54 */ "NewRowid",
- /* 55 */ "Insert",
- /* 56 */ "InsertInt",
- /* 57 */ "Delete",
- /* 58 */ "ResetCount",
- /* 59 */ "RowKey",
- /* 60 */ "RowData",
- /* 61 */ "Rowid",
- /* 62 */ "NullRow",
- /* 63 */ "Last",
- /* 64 */ "Sort",
- /* 65 */ "Rewind",
- /* 66 */ "Prev",
- /* 67 */ "Next",
- /* 68 */ "Or",
- /* 69 */ "And",
- /* 70 */ "IdxInsert",
- /* 71 */ "IdxDelete",
- /* 72 */ "IdxRowid",
- /* 73 */ "IsNull",
- /* 74 */ "NotNull",
- /* 75 */ "Ne",
- /* 76 */ "Eq",
- /* 77 */ "Gt",
- /* 78 */ "Le",
- /* 79 */ "Lt",
- /* 80 */ "Ge",
- /* 81 */ "IdxLT",
- /* 82 */ "BitAnd",
- /* 83 */ "BitOr",
- /* 84 */ "ShiftLeft",
- /* 85 */ "ShiftRight",
- /* 86 */ "Add",
- /* 87 */ "Subtract",
- /* 88 */ "Multiply",
- /* 89 */ "Divide",
- /* 90 */ "Remainder",
- /* 91 */ "Concat",
- /* 92 */ "IdxGE",
- /* 93 */ "BitNot",
- /* 94 */ "String8",
- /* 95 */ "Destroy",
- /* 96 */ "Clear",
- /* 97 */ "CreateIndex",
- /* 98 */ "CreateTable",
- /* 99 */ "ParseSchema",
- /* 100 */ "LoadAnalysis",
- /* 101 */ "DropTable",
- /* 102 */ "DropIndex",
- /* 103 */ "DropTrigger",
- /* 104 */ "IntegrityCk",
- /* 105 */ "RowSetAdd",
- /* 106 */ "RowSetRead",
- /* 107 */ "RowSetTest",
- /* 108 */ "Program",
- /* 109 */ "Param",
- /* 110 */ "FkCounter",
- /* 111 */ "FkIfZero",
- /* 112 */ "MemMax",
- /* 113 */ "IfPos",
- /* 114 */ "IfNeg",
- /* 115 */ "IfZero",
- /* 116 */ "AggStep",
- /* 117 */ "AggFinal",
- /* 118 */ "Checkpoint",
- /* 119 */ "JournalMode",
- /* 120 */ "Vacuum",
- /* 121 */ "IncrVacuum",
- /* 122 */ "Expire",
- /* 123 */ "TableLock",
- /* 124 */ "VBegin",
- /* 125 */ "VCreate",
- /* 126 */ "VDestroy",
- /* 127 */ "VOpen",
- /* 128 */ "VFilter",
- /* 129 */ "VColumn",
- /* 130 */ "Real",
- /* 131 */ "VNext",
- /* 132 */ "VRename",
- /* 133 */ "VUpdate",
- /* 134 */ "Pagecount",
- /* 135 */ "MaxPgcnt",
- /* 136 */ "Trace",
- /* 137 */ "Noop",
- /* 138 */ "Explain",
- /* 139 */ "NotUsed_139",
- /* 140 */ "NotUsed_140",
- /* 141 */ "ToText",
- /* 142 */ "ToBlob",
- /* 143 */ "ToNumeric",
- /* 144 */ "ToInt",
- /* 145 */ "ToReal",
+ /* 1 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 2 */ "Savepoint" OpHelp(""),
+ /* 3 */ "AutoCommit" OpHelp(""),
+ /* 4 */ "Transaction" OpHelp(""),
+ /* 5 */ "SorterNext" OpHelp(""),
+ /* 6 */ "PrevIfOpen" OpHelp(""),
+ /* 7 */ "NextIfOpen" OpHelp(""),
+ /* 8 */ "Prev" OpHelp(""),
+ /* 9 */ "Next" OpHelp(""),
+ /* 10 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 11 */ "Checkpoint" OpHelp(""),
+ /* 12 */ "JournalMode" OpHelp(""),
+ /* 13 */ "Vacuum" OpHelp(""),
+ /* 14 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
+ /* 15 */ "VUpdate" OpHelp("data=r[P3@P2]"),
+ /* 16 */ "Goto" OpHelp(""),
+ /* 17 */ "Gosub" OpHelp(""),
+ /* 18 */ "Return" OpHelp(""),
+ /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
+ /* 20 */ "InitCoroutine" OpHelp(""),
+ /* 21 */ "EndCoroutine" OpHelp(""),
+ /* 22 */ "Yield" OpHelp(""),
+ /* 23 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
+ /* 24 */ "Halt" OpHelp(""),
+ /* 25 */ "Integer" OpHelp("r[P2]=P1"),
+ /* 26 */ "Int64" OpHelp("r[P2]=P4"),
+ /* 27 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
+ /* 28 */ "Null" OpHelp("r[P2..P3]=NULL"),
+ /* 29 */ "SoftNull" OpHelp("r[P1]=NULL"),
+ /* 30 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
+ /* 31 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 32 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
+ /* 33 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
+ /* 34 */ "SCopy" OpHelp("r[P2]=r[P1]"),
+ /* 35 */ "ResultRow" OpHelp("output=r[P1@P2]"),
+ /* 36 */ "CollSeq" OpHelp(""),
+ /* 37 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
+ /* 38 */ "MustBeInt" OpHelp(""),
+ /* 39 */ "RealAffinity" OpHelp(""),
+ /* 40 */ "Cast" OpHelp("affinity(r[P1])"),
+ /* 41 */ "Permutation" OpHelp(""),
+ /* 42 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
+ /* 43 */ "Jump" OpHelp(""),
+ /* 44 */ "Once" OpHelp(""),
+ /* 45 */ "If" OpHelp(""),
+ /* 46 */ "IfNot" OpHelp(""),
+ /* 47 */ "Column" OpHelp("r[P3]=PX"),
+ /* 48 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
+ /* 49 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
+ /* 50 */ "Count" OpHelp("r[P2]=count()"),
+ /* 51 */ "ReadCookie" OpHelp(""),
+ /* 52 */ "SetCookie" OpHelp(""),
+ /* 53 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 54 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 55 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 56 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 57 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 58 */ "SorterOpen" OpHelp(""),
+ /* 59 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
+ /* 60 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
+ /* 61 */ "Close" OpHelp(""),
+ /* 62 */ "SeekLT" OpHelp("key=r[P3@P4]"),
+ /* 63 */ "SeekLE" OpHelp("key=r[P3@P4]"),
+ /* 64 */ "SeekGE" OpHelp("key=r[P3@P4]"),
+ /* 65 */ "SeekGT" OpHelp("key=r[P3@P4]"),
+ /* 66 */ "Seek" OpHelp("intkey=r[P2]"),
+ /* 67 */ "NoConflict" OpHelp("key=r[P3@P4]"),
+ /* 68 */ "NotFound" OpHelp("key=r[P3@P4]"),
+ /* 69 */ "Found" OpHelp("key=r[P3@P4]"),
+ /* 70 */ "NotExists" OpHelp("intkey=r[P3]"),
+ /* 71 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
+ /* 72 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
+ /* 73 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
+ /* 74 */ "NewRowid" OpHelp("r[P2]=rowid"),
+ /* 75 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
+ /* 76 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
+ /* 77 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
+ /* 78 */ "Ne" OpHelp("if r[P1]!=r[P3] goto P2"),
+ /* 79 */ "Eq" OpHelp("if r[P1]==r[P3] goto P2"),
+ /* 80 */ "Gt" OpHelp("if r[P1]>r[P3] goto P2"),
+ /* 81 */ "Le" OpHelp("if r[P1]<=r[P3] goto P2"),
+ /* 82 */ "Lt" OpHelp("if r[P1]<r[P3] goto P2"),
+ /* 83 */ "Ge" OpHelp("if r[P1]>=r[P3] goto P2"),
+ /* 84 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
+ /* 85 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 86 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 87 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 88 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 89 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 90 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 91 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 92 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 93 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 94 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 95 */ "Delete" OpHelp(""),
+ /* 96 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
+ /* 97 */ "String8" OpHelp("r[P2]='P4'"),
+ /* 98 */ "ResetCount" OpHelp(""),
+ /* 99 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+ /* 100 */ "SorterData" OpHelp("r[P2]=data"),
+ /* 101 */ "RowKey" OpHelp("r[P2]=key"),
+ /* 102 */ "RowData" OpHelp("r[P2]=data"),
+ /* 103 */ "Rowid" OpHelp("r[P2]=rowid"),
+ /* 104 */ "NullRow" OpHelp(""),
+ /* 105 */ "Last" OpHelp(""),
+ /* 106 */ "SorterSort" OpHelp(""),
+ /* 107 */ "Sort" OpHelp(""),
+ /* 108 */ "Rewind" OpHelp(""),
+ /* 109 */ "SorterInsert" OpHelp(""),
+ /* 110 */ "IdxInsert" OpHelp("key=r[P2]"),
+ /* 111 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
+ /* 112 */ "IdxRowid" OpHelp("r[P2]=rowid"),
+ /* 113 */ "IdxLE" OpHelp("key=r[P3@P4]"),
+ /* 114 */ "IdxGT" OpHelp("key=r[P3@P4]"),
+ /* 115 */ "IdxLT" OpHelp("key=r[P3@P4]"),
+ /* 116 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 117 */ "Destroy" OpHelp(""),
+ /* 118 */ "Clear" OpHelp(""),
+ /* 119 */ "ResetSorter" OpHelp(""),
+ /* 120 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
+ /* 121 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
+ /* 122 */ "ParseSchema" OpHelp(""),
+ /* 123 */ "LoadAnalysis" OpHelp(""),
+ /* 124 */ "DropTable" OpHelp(""),
+ /* 125 */ "DropIndex" OpHelp(""),
+ /* 126 */ "DropTrigger" OpHelp(""),
+ /* 127 */ "IntegrityCk" OpHelp(""),
+ /* 128 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 129 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
+ /* 130 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 131 */ "Program" OpHelp(""),
+ /* 132 */ "Param" OpHelp(""),
+ /* 133 */ "Real" OpHelp("r[P2]=P4"),
+ /* 134 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
+ /* 135 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
+ /* 136 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
+ /* 137 */ "IfPos" OpHelp("if r[P1]>0 goto P2"),
+ /* 138 */ "IfNeg" OpHelp("r[P1]+=P3, if r[P1]<0 goto P2"),
+ /* 139 */ "IfZero" OpHelp("r[P1]+=P3, if r[P1]==0 goto P2"),
+ /* 140 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
+ /* 141 */ "IncrVacuum" OpHelp(""),
+ /* 142 */ "Expire" OpHelp(""),
+ /* 143 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
+ /* 144 */ "VBegin" OpHelp(""),
+ /* 145 */ "VCreate" OpHelp(""),
+ /* 146 */ "VDestroy" OpHelp(""),
+ /* 147 */ "VOpen" OpHelp(""),
+ /* 148 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 149 */ "VNext" OpHelp(""),
+ /* 150 */ "VRename" OpHelp(""),
+ /* 151 */ "Pagecount" OpHelp(""),
+ /* 152 */ "MaxPgcnt" OpHelp(""),
+ /* 153 */ "Init" OpHelp("Start at P2"),
+ /* 154 */ "Noop" OpHelp(""),
+ /* 155 */ "Explain" OpHelp(""),
};
return azName[i];
}
#endif
/************** End of opcodes.c *********************************************/
-/************** Begin file os_os2.c ******************************************/
-/*
-** 2006 Feb 14
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains code that is specific to OS/2.
-*/
-
-
-#if SQLITE_OS_OS2
-
-/*
-** A Note About Memory Allocation:
-**
-** This driver uses malloc()/free() directly rather than going through
-** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
-** are designed for use on embedded systems where memory is scarce and
-** malloc failures happen frequently. OS/2 does not typically run on
-** embedded systems, and when it does the developers normally have bigger
-** problems to worry about than running out of memory. So there is not
-** a compelling need to use the wrappers.
-**
-** But there is a good reason to not use the wrappers. If we use the
-** wrappers then we will get simulated malloc() failures within this
-** driver. And that causes all kinds of problems for our tests. We
-** could enhance SQLite to deal with simulated malloc failures within
-** the OS driver, but the code to deal with those failure would not
-** be exercised on Linux (which does not need to malloc() in the driver)
-** and so we would have difficulty writing coverage tests for that
-** code. Better to leave the code out, we think.
-**
-** The point of this discussion is as follows: When creating a new
-** OS layer for an embedded system, if you use this file as an example,
-** avoid the use of malloc()/free(). Those routines work ok on OS/2
-** desktops but not so well in embedded systems.
-*/
-
-/*
-** Macros used to determine whether or not to use threads.
-*/
-#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE
-# define SQLITE_OS2_THREADS 1
-#endif
-
-/*
-** Include code that is common to all os_*.c files
-*/
-/************** Include os_common.h in the middle of os_os2.c ****************/
-/************** Begin file os_common.h ***************************************/
-/*
-** 2004 May 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-#ifndef _OS_COMMON_H_
-#define _OS_COMMON_H_
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3OSTrace = 0;
-#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
-#else
-#define OSTRACE(X)
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef _HWTIME_H_
-#define _HWTIME_H_
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(_HWTIME_H_) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
-static sqlite_uint64 g_start;
-static sqlite_uint64 g_elapsed;
-#define TIMER_START g_start=sqlite3Hwtime()
-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
-#define TIMER_ELAPSED g_elapsed
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED ((sqlite_uint64)0)
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
-#define SimulateIOError(CODE) \
- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
- || sqlite3_io_error_pending-- == 1 ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit++;
- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOErrorBenign(X)
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif
-
-#endif /* !defined(_OS_COMMON_H_) */
-
-/************** End of os_common.h *******************************************/
-/************** Continuing where we left off in os_os2.c *********************/
-
-/* Forward references */
-typedef struct os2File os2File; /* The file structure */
-typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */
-typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */
-
-/*
-** The os2File structure is subclass of sqlite3_file specific for the OS/2
-** protability layer.
-*/
-struct os2File {
- const sqlite3_io_methods *pMethod; /* Always the first entry */
- HFILE h; /* Handle for accessing the file */
- int flags; /* Flags provided to os2Open() */
- int locktype; /* Type of lock currently held on this file */
- int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
- char *zFullPathCp; /* Full path name of this file */
- os2ShmLink *pShmLink; /* Instance of shared memory on this file */
-};
-
-#define LOCK_TIMEOUT 10L /* the default locking timeout */
-
-/*
-** Missing from some versions of the OS/2 toolkit -
-** used to allocate from high memory if possible
-*/
-#ifndef OBJ_ANY
-# define OBJ_ANY 0x00000400
-#endif
-
-/*****************************************************************************
-** The next group of routines implement the I/O methods specified
-** by the sqlite3_io_methods object.
-******************************************************************************/
-
-/*
-** Close a file.
-*/
-static int os2Close( sqlite3_file *id ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
-
- assert( id!=0 );
- OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp ));
-
- rc = DosClose( pFile->h );
-
- if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE )
- DosForceDelete( (PSZ)pFile->zFullPathCp );
-
- free( pFile->zFullPathCp );
- pFile->zFullPathCp = NULL;
- pFile->locktype = NO_LOCK;
- pFile->h = (HFILE)-1;
- pFile->flags = 0;
-
- OpenCounter( -1 );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-/*
-** Read data from a file into a buffer. Return SQLITE_OK if all
-** bytes were read successfully and SQLITE_IOERR if anything goes
-** wrong.
-*/
-static int os2Read(
- sqlite3_file *id, /* File to read from */
- void *pBuf, /* Write content into this buffer */
- int amt, /* Number of bytes to read */
- sqlite3_int64 offset /* Begin reading at this offset */
-){
- ULONG fileLocation = 0L;
- ULONG got;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_READ );
- OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){
- return SQLITE_IOERR_READ;
- }
- if( got == (ULONG)amt )
- return SQLITE_OK;
- else {
- /* Unread portions of the input buffer must be zero-filled */
- memset(&((char*)pBuf)[got], 0, amt-got);
- return SQLITE_IOERR_SHORT_READ;
- }
-}
-
-/*
-** Write data from a buffer into a file. Return SQLITE_OK on success
-** or some other error code on failure.
-*/
-static int os2Write(
- sqlite3_file *id, /* File to write into */
- const void *pBuf, /* The bytes to be written */
- int amt, /* Number of bytes to write */
- sqlite3_int64 offset /* Offset into the file to begin writing at */
-){
- ULONG fileLocation = 0L;
- APIRET rc = NO_ERROR;
- ULONG wrote;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_WRITE );
- SimulateDiskfullError( return SQLITE_FULL );
- OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- assert( amt>0 );
- while( amt > 0 &&
- ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR &&
- wrote > 0
- ){
- amt -= wrote;
- pBuf = &((char*)pBuf)[wrote];
- }
-
- return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
-}
-
-/*
-** Truncate an open file to a specified size
-*/
-static int os2Truncate( sqlite3_file *id, i64 nByte ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte ));
- SimulateIOError( return SQLITE_IOERR_TRUNCATE );
-
- /* If the user has configured a chunk-size for this file, truncate the
- ** file so that it consists of an integer number of chunks (i.e. the
- ** actual file size after the operation may be larger than the requested
- ** size).
- */
- if( pFile->szChunk ){
- nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
- }
-
- rc = DosSetFileSize( pFile->h, nByte );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
-}
-
-#ifdef SQLITE_TEST
-/*
-** Count the number of fullsyncs and normal syncs. This is used to test
-** that syncs and fullsyncs are occuring at the right times.
-*/
-SQLITE_API int sqlite3_sync_count = 0;
-SQLITE_API int sqlite3_fullsync_count = 0;
-#endif
-
-/*
-** Make sure all writes to a particular file are committed to disk.
-*/
-static int os2Sync( sqlite3_file *id, int flags ){
- os2File *pFile = (os2File*)id;
- OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype ));
-#ifdef SQLITE_TEST
- if( flags & SQLITE_SYNC_FULL){
- sqlite3_fullsync_count++;
- }
- sqlite3_sync_count++;
-#endif
- /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
- ** no-op
- */
-#ifdef SQLITE_NO_SYNC
- UNUSED_PARAMETER(pFile);
- return SQLITE_OK;
-#else
- return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-#endif
-}
-
-/*
-** Determine the current size of a file in bytes
-*/
-static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
- APIRET rc = NO_ERROR;
- FILESTATUS3 fsts3FileInfo;
- memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_FSTAT );
- rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
- if( rc == NO_ERROR ){
- *pSize = fsts3FileInfo.cbFile;
- return SQLITE_OK;
- }else{
- return SQLITE_IOERR_FSTAT;
- }
-}
-
-/*
-** Acquire a reader lock.
-*/
-static int getReadLock( os2File *pFile ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res ));
- return res;
-}
-
-/*
-** Undo a readlock
-*/
-static int unlockReadLock( os2File *id ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res ));
- return res;
-}
-
-/*
-** Lock the file with the lock specified by parameter locktype - one
-** of the following:
-**
-** (1) SHARED_LOCK
-** (2) RESERVED_LOCK
-** (3) PENDING_LOCK
-** (4) EXCLUSIVE_LOCK
-**
-** Sometimes when requesting one lock state, additional lock states
-** are inserted in between. The locking might fail on one of the later
-** transitions leaving the lock state different from what it started but
-** still short of its goal. The following chart shows the allowed
-** transitions and the inserted intermediate states:
-**
-** UNLOCKED -> SHARED
-** SHARED -> RESERVED
-** SHARED -> (PENDING) -> EXCLUSIVE
-** RESERVED -> (PENDING) -> EXCLUSIVE
-** PENDING -> EXCLUSIVE
-**
-** This routine will only increase a lock. The os2Unlock() routine
-** erases all locks at once and returns us immediately to locking level 0.
-** It is not possible to lower the locking level one step at a time. You
-** must go straight to locking level 0.
-*/
-static int os2Lock( sqlite3_file *id, int locktype ){
- int rc = SQLITE_OK; /* Return code from subroutines */
- APIRET res = NO_ERROR; /* Result of an OS/2 lock call */
- int newLocktype; /* Set pFile->locktype to this value before exiting */
- int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
- FILELOCK LockArea,
- UnlockArea;
- os2File *pFile = (os2File*)id;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- OSTRACE(( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype ));
-
- /* If there is already a lock of this type or more restrictive on the
- ** os2File, do nothing. Don't use the end_lock: exit path, as
- ** sqlite3_mutex_enter() hasn't been called yet.
- */
- if( pFile->locktype>=locktype ){
- OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, locktype ));
- return SQLITE_OK;
- }
-
- /* Make sure the locking sequence is correct
- */
- assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
- assert( locktype!=PENDING_LOCK );
- assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
-
- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
- ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
- ** the PENDING_LOCK byte is temporary.
- */
- newLocktype = pFile->locktype;
- if( pFile->locktype==NO_LOCK
- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
- ){
- LockArea.lOffset = PENDING_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
-
- /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L );
- if( res == NO_ERROR ){
- gotPendingLock = 1;
- OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res ));
- }
- }
-
- /* Acquire a shared lock
- */
- if( locktype==SHARED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
- if( res == NO_ERROR ){
- newLocktype = SHARED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a RESERVED lock
- */
- if( locktype==RESERVED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==SHARED_LOCK );
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = RESERVED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a PENDING lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- newLocktype = PENDING_LOCK;
- gotPendingLock = 0;
- OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n",
- pFile->h ));
- }
-
- /* Acquire an EXCLUSIVE lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
- OSTRACE(( "unreadlock = %d\n", res ));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = EXCLUSIVE_LOCK;
- }else{
- OSTRACE(( "OS/2 error-code = %d\n", res ));
- getReadLock(pFile);
- }
- OSTRACE(( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res ));
- }
-
- /* If we are holding a PENDING lock that ought to be released, then
- ** release it now.
- */
- if( gotPendingLock && locktype==SHARED_LOCK ){
- int r;
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r ));
- }
-
- /* Update the state of the lock has held in the file descriptor then
- ** return the appropriate result code.
- */
- if( res == NO_ERROR ){
- rc = SQLITE_OK;
- }else{
- OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
- locktype, newLocktype ));
- rc = SQLITE_BUSY;
- }
- pFile->locktype = newLocktype;
- OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** This routine checks if there is a RESERVED lock held on the specified
-** file by this or any other process. If such a lock is held, return
-** non-zero, otherwise zero.
-*/
-static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){
- int r = 0;
- os2File *pFile = (os2File*)id;
- assert( pFile!=0 );
- if( pFile->locktype>=RESERVED_LOCK ){
- r = 1;
- OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r ));
- }else{
- FILELOCK LockArea,
- UnlockArea;
- APIRET rc = NO_ERROR;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc ));
- if( rc == NO_ERROR ){
- APIRET rcu = NO_ERROR; /* return code for unlocking */
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu ));
- }
- r = !(rc == NO_ERROR);
- OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r ));
- }
- *pOut = r;
- return SQLITE_OK;
-}
-
-/*
-** Lower the locking level on file descriptor id to locktype. locktype
-** must be either NO_LOCK or SHARED_LOCK.
-**
-** If the locking level of the file descriptor is already at or below
-** the requested locking level, this routine is a no-op.
-**
-** It is not possible for this routine to fail if the second argument
-** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
-** might return SQLITE_IOERR;
-*/
-static int os2Unlock( sqlite3_file *id, int locktype ){
- int type;
- os2File *pFile = (os2File*)id;
- APIRET rc = SQLITE_OK;
- APIRET res = NO_ERROR;
- FILELOCK LockArea,
- UnlockArea;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- assert( locktype<=SHARED_LOCK );
- OSTRACE(( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype ));
- type = pFile->locktype;
- if( type>=EXCLUSIVE_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res ));
- if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
- /* This should never happen. We should always be able to
- ** reacquire the read lock */
- OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype ));
- rc = SQLITE_IOERR_UNLOCK;
- }
- }
- if( type>=RESERVED_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res ));
- }
- if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- res = unlockReadLock(pFile);
- OSTRACE(( "UNLOCK %d is %d want %d res=%d\n",
- pFile->h, type, locktype, res ));
- }
- if( type>=PENDING_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res ));
- }
- pFile->locktype = locktype;
- OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** Control and query of the open file handle.
-*/
-static int os2FileControl(sqlite3_file *id, int op, void *pArg){
- switch( op ){
- case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((os2File*)id)->locktype;
- OSTRACE(( "FCNTL_LOCKSTATE %d lock=%d\n",
- ((os2File*)id)->h, ((os2File*)id)->locktype ));
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_CHUNK_SIZE: {
- ((os2File*)id)->szChunk = *(int*)pArg;
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SIZE_HINT: {
- sqlite3_int64 sz = *(sqlite3_int64*)pArg;
- SimulateIOErrorBenign(1);
- os2Truncate(id, sz);
- SimulateIOErrorBenign(0);
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SYNC_OMITTED: {
- return SQLITE_OK;
- }
- }
- return SQLITE_NOTFOUND;
-}
-
-/*
-** Return the sector size in bytes of the underlying block device for
-** the specified file. This is almost always 512 bytes, but may be
-** larger for some devices.
-**
-** SQLite code assumes this function cannot fail. It also assumes that
-** if two files are created in the same file-system directory (i.e.
-** a database and its journal file) that the sector size will be the
-** same for both.
-*/
-static int os2SectorSize(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_DEFAULT_SECTOR_SIZE;
-}
-
-/*
-** Return a vector of device characteristics.
-*/
-static int os2DeviceCharacteristics(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
-}
-
-
-/*
-** Character set conversion objects used by conversion routines.
-*/
-static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */
-static UconvObject uclCp = NULL; /* convert between local codepage and UCS-2 */
-
-/*
-** Helper function to initialize the conversion objects from and to UTF-8.
-*/
-static void initUconvObjects( void ){
- if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS )
- ucUtf8 = NULL;
- if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS )
- uclCp = NULL;
-}
-
-/*
-** Helper function to free the conversion objects from and to UTF-8.
-*/
-static void freeUconvObjects( void ){
- if ( ucUtf8 )
- UniFreeUconvObject( ucUtf8 );
- if ( uclCp )
- UniFreeUconvObject( uclCp );
- ucUtf8 = NULL;
- uclCp = NULL;
-}
-
-/*
-** Helper function to convert UTF-8 filenames to local OS/2 codepage.
-** The two-step process: first convert the incoming UTF-8 string
-** into UCS-2 and then from UCS-2 to the current codepage.
-** The returned char pointer has to be freed.
-*/
-static char *convertUtf8PathToCp( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* conversion for current codepage which can be used for paths */
- UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-/*
-** Helper function to convert filenames from local codepage to UTF-8.
-** The two-step process: first convert the incoming codepage-specific
-** string into UCS-2 and then from UCS-2 to the codepage of UTF-8.
-** The returned char pointer has to be freed.
-**
-** This function is non-static to be able to use this in shell.c and
-** similar applications that take command line arguments.
-*/
-char *convertCpPathToUtf8( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* conversion for current codepage which can be used for paths */
- if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-
-#ifndef SQLITE_OMIT_WAL
-
-/*
-** Use main database file for interprocess locking. If un-defined
-** a separate file is created for this purpose. The file will be
-** used only to set file locks. There will be no data written to it.
-*/
-#define SQLITE_OS2_NO_WAL_LOCK_FILE
-
-#if 0
-static void _ERR_TRACE( const char *fmt, ... ) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- fflush(stderr);
-}
-#define ERR_TRACE(rc, msg) \
- if( (rc) != SQLITE_OK ) _ERR_TRACE msg;
-#else
-#define ERR_TRACE(rc, msg)
-#endif
-
-/*
-** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect os2ShmNodeList.
-**
-** Function os2ShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
-** statements. e.g.
-**
-** os2ShmEnterMutex()
-** assert( os2ShmMutexHeld() );
-** os2ShmLeaveMutex()
-*/
-static void os2ShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-static void os2ShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-#ifdef SQLITE_DEBUG
-static int os2ShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-int GetCurrentProcessId(void) {
- PPIB pib;
- DosGetInfoBlocks(NULL, &pib);
- return (int)pib->pib_ulpid;
-}
-#endif
-
-/*
-** Object used to represent a the shared memory area for a single log file.
-** When multiple threads all reference the same log-summary, each thread has
-** its own os2File object, but they all point to a single instance of this
-** object. In other words, each log-summary is opened only once per process.
-**
-** os2ShmMutexHeld() must be true when creating or destroying
-** this object or while reading or writing the following fields:
-**
-** nRef
-** pNext
-**
-** The following fields are read-only after the object is created:
-**
-** szRegion
-** hLockFile
-** shmBaseName
-**
-** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and
-** os2ShmMutexHeld() is true when reading or writing any other field
-** in this structure.
-**
-*/
-struct os2ShmNode {
- sqlite3_mutex *mutex; /* Mutex to access this object */
- os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */
-
- int szRegion; /* Size of shared-memory regions */
-
- int nRegion; /* Size of array apRegion */
- void **apRegion; /* Array of pointers to shared-memory regions */
-
- int nRef; /* Number of os2ShmLink objects pointing to this */
- os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */
-
- HFILE hLockFile; /* File used for inter-process memory locking */
- char shmBaseName[1]; /* Name of the memory object !!! must last !!! */
-};
-
-
-/*
-** Structure used internally by this VFS to record the state of an
-** open shared memory connection.
-**
-** The following fields are initialized when this object is created and
-** are read-only thereafter:
-**
-** os2Shm.pShmNode
-** os2Shm.id
-**
-** All other fields are read/write. The os2Shm.pShmNode->mutex must be held
-** while accessing any read/write fields.
-*/
-struct os2ShmLink {
- os2ShmNode *pShmNode; /* The underlying os2ShmNode object */
- os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */
- u32 sharedMask; /* Mask of shared locks held */
- u32 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection with its os2ShmNode */
-#endif
-};
-
-
-/*
-** A global list of all os2ShmNode objects.
-**
-** The os2ShmMutexHeld() must be true while reading or writing this list.
-*/
-static os2ShmNode *os2ShmNodeList = NULL;
-
-/*
-** Constants used for locking
-*/
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
-#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */
-#else
-#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
-#endif
-
-#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
-
-/*
-** Apply advisory locks for all n bytes beginning at ofst.
-*/
-#define _SHM_UNLCK 1 /* no lock */
-#define _SHM_RDLCK 2 /* shared lock, no wait */
-#define _SHM_WRLCK 3 /* exlusive lock, no wait */
-#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */
-static int os2ShmSystemLock(
- os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */
- int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */
- int ofst, /* Offset to first byte to be locked/unlocked */
- int nByte /* Number of bytes to lock or unlock */
-){
- APIRET rc;
- FILELOCK area;
- ULONG mode, timeout;
-
- /* Access to the os2ShmNode object is serialized by the caller */
- assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 );
-
- mode = 1; /* shared lock */
- timeout = 0; /* no wait */
- area.lOffset = ofst;
- area.lRange = nByte;
-
- switch( lockType ) {
- case _SHM_WRLCK_WAIT:
- timeout = (ULONG)-1; /* wait forever */
- case _SHM_WRLCK:
- mode = 0; /* exclusive lock */
- case _SHM_RDLCK:
- rc = DosSetFileLocks(pNode->hLockFile,
- NULL, &area, timeout, mode);
- break;
- /* case _SHM_UNLCK: */
- default:
- rc = DosSetFileLocks(pNode->hLockFile,
- &area, NULL, 0, 0);
- break;
- }
-
- OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",
- pNode->hLockFile,
- rc==SQLITE_OK ? "ok" : "failed",
- lockType==_SHM_UNLCK ? "Unlock" : "Lock",
- rc));
-
- ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName))
-
- return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY;
-}
-
-/*
-** Find an os2ShmNode in global list or allocate a new one, if not found.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static int os2OpenSharedMemory( os2File *fd, int szRegion ) {
- os2ShmLink *pLink;
- os2ShmNode *pNode;
- int cbShmName, rc = SQLITE_OK;
- char shmName[CCHMAXPATH + 30];
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- ULONG action;
-#endif
-
- /* We need some additional space at the end to append the region number */
- cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp );
- if( cbShmName >= CCHMAXPATH-8 )
- return SQLITE_IOERR_SHMOPEN;
-
- /* Replace colon in file name to form a valid shared memory name */
- shmName[10+1] = '!';
-
- /* Allocate link object (we free it later in case of failure) */
- pLink = sqlite3_malloc( sizeof(*pLink) );
- if( !pLink )
- return SQLITE_NOMEM;
-
- /* Access node list */
- os2ShmEnterMutex();
-
- /* Find node by it's shared memory base name */
- for( pNode = os2ShmNodeList;
- pNode && stricmp(shmName, pNode->shmBaseName) != 0;
- pNode = pNode->pNext ) ;
-
- /* Not found: allocate a new node */
- if( !pNode ) {
- pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName );
- if( pNode ) {
- memset(pNode, 0, sizeof(*pNode) );
- pNode->szRegion = szRegion;
- pNode->hLockFile = (HFILE)-1;
- strcpy(pNode->shmBaseName, shmName);
-
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) {
-#else
- sprintf(shmName, "%s-lck", fd->zFullPathCp);
- if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL,
- OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
- OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR,
- NULL) != 0 ) {
-#endif
- sqlite3_free(pNode);
- rc = SQLITE_IOERR;
- } else {
- pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( !pNode->mutex ) {
- sqlite3_free(pNode);
- rc = SQLITE_NOMEM;
- }
- }
- } else {
- rc = SQLITE_NOMEM;
- }
-
- if( rc == SQLITE_OK ) {
- pNode->pNext = os2ShmNodeList;
- os2ShmNodeList = pNode;
- } else {
- pNode = NULL;
- }
- } else if( pNode->szRegion != szRegion ) {
- rc = SQLITE_IOERR_SHMSIZE;
- pNode = NULL;
- }
-
- if( pNode ) {
- sqlite3_mutex_enter(pNode->mutex);
-
- memset(pLink, 0, sizeof(*pLink));
-
- pLink->pShmNode = pNode;
- pLink->pNext = pNode->pFirst;
- pNode->pFirst = pLink;
- pNode->nRef++;
-
- fd->pShmLink = pLink;
-
- sqlite3_mutex_leave(pNode->mutex);
-
- } else {
- /* Error occured. Free our link object. */
- sqlite3_free(pLink);
- }
-
- os2ShmLeaveMutex();
-
- ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp))
-
- return rc;
-}
-
-/*
-** Purge the os2ShmNodeList list of all entries with nRef==0.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static void os2PurgeShmNodes( int deleteFlag ) {
- os2ShmNode *pNode;
- os2ShmNode **ppNode;
-
- os2ShmEnterMutex();
-
- ppNode = &os2ShmNodeList;
-
- while( *ppNode ) {
- pNode = *ppNode;
-
- if( pNode->nRef == 0 ) {
- *ppNode = pNode->pNext;
-
- if( pNode->apRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- while( pNode->nRegion-- ) {
-#ifdef SQLITE_DEBUG
- int rc =
-#endif
- DosFreeMem(pNode->apRegion[pNode->nRegion]);
-
- OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
- (int)GetCurrentProcessId(), pNode->nRegion,
- rc == 0 ? "ok" : "failed"));
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- sqlite3_free(pNode->apRegion);
- }
-
- DosClose(pNode->hLockFile);
-
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( deleteFlag ) {
- char fileName[CCHMAXPATH];
- /* Skip "\\SHAREMEM\\" */
- sprintf(fileName, "%s-lck", pNode->shmBaseName + 10);
- /* restore colon */
- fileName[1] = ':';
-
- DosForceDelete(fileName);
- }
-#endif
-
- sqlite3_mutex_free(pNode->mutex);
-
- sqlite3_free(pNode);
-
- } else {
- ppNode = &pNode->pNext;
- }
- }
-
- os2ShmLeaveMutex();
-}
-
-/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file id. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
-** bytes in size.
-**
-** If an error occurs, an error code is returned and *pp is set to NULL.
-**
-** Otherwise, if the bExtend parameter is 0 and the requested shared-memory
-** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** bExtend is non-zero and the requested shared-memory region has not yet
-** been allocated, it is allocated by this function.
-**
-** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
-** memory and SQLITE_OK returned.
-*/
-static int os2ShmMap(
- sqlite3_file *id, /* Handle open on database file */
- int iRegion, /* Region to retrieve */
- int szRegion, /* Size of regions */
- int bExtend, /* True to extend block if necessary */
- void volatile **pp /* OUT: Mapped memory */
-){
- PVOID pvTemp;
- void **apRegion;
- os2ShmNode *pNode;
- int n, rc = SQLITE_OK;
- char shmName[CCHMAXPATH];
- os2File *pFile = (os2File*)id;
-
- *pp = NULL;
-
- if( !pFile->pShmLink )
- rc = os2OpenSharedMemory( pFile, szRegion );
-
- if( rc == SQLITE_OK ) {
- pNode = pFile->pShmLink->pShmNode ;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- assert( szRegion==pNode->szRegion );
-
- /* Unmapped region ? */
- if( iRegion >= pNode->nRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- apRegion = sqlite3_realloc(
- pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0]));
-
- if( apRegion ) {
- pNode->apRegion = apRegion;
-
- while( pNode->nRegion <= iRegion ) {
- sprintf(shmName, "%s-%u",
- pNode->shmBaseName, pNode->nRegion);
-
- if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName,
- PAG_READ | PAG_WRITE) != NO_ERROR ) {
- if( !bExtend )
- break;
-
- if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR &&
- DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) {
- rc = SQLITE_NOMEM;
- break;
- }
- }
-
- apRegion[pNode->nRegion++] = pvTemp;
- }
-
- /* zero out remaining entries */
- for( n = pNode->nRegion; n <= iRegion; n++ )
- pNode->apRegion[n] = NULL;
-
- /* Return this region (maybe zero) */
- *pp = pNode->apRegion[iRegion];
- } else {
- rc = SQLITE_NOMEM;
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- } else {
- /* Region has been mapped previously */
- *pp = pNode->apRegion[iRegion];
- }
-
- sqlite3_mutex_leave(pNode->mutex);
- }
-
- ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n",
- pFile->zFullPathCp, iRegion, szRegion, bExtend, rc))
-
- return rc;
-}
-
-/*
-** Close a connection to shared-memory. Delete the underlying
-** storage if deleteFlag is true.
-**
-** If there is no shared memory associated with the connection then this
-** routine is a harmless no-op.
-*/
-static int os2ShmUnmap(
- sqlite3_file *id, /* The underlying database file */
- int deleteFlag /* Delete shared-memory if true */
-){
- os2File *pFile = (os2File*)id;
- os2ShmLink *pLink = pFile->pShmLink;
-
- if( pLink ) {
- int nRef = -1;
- os2ShmLink **ppLink;
- os2ShmNode *pNode = pLink->pShmNode;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- for( ppLink = &pNode->pFirst;
- *ppLink && *ppLink != pLink;
- ppLink = &(*ppLink)->pNext ) ;
-
- assert(*ppLink);
-
- if( *ppLink ) {
- *ppLink = pLink->pNext;
- nRef = --pNode->nRef;
- } else {
- ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n",
- pNode->shmBaseName))
- }
-
- pFile->pShmLink = NULL;
- sqlite3_free(pLink);
-
- sqlite3_mutex_leave(pNode->mutex);
-
- if( nRef == 0 )
- os2PurgeShmNodes( deleteFlag );
- }
-
- return SQLITE_OK;
-}
-
-/*
-** Change the lock state for a shared-memory segment.
-**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
-** different here than in posix. In xShmLock(), one can go from unlocked
-** to shared and back or from unlocked to exclusive and back. But one may
-** not go from shared to exclusive or from exclusive to shared.
-*/
-static int os2ShmLock(
- sqlite3_file *id, /* Database file holding the shared memory */
- int ofst, /* First lock to acquire or release */
- int n, /* Number of locks to acquire or release */
- int flags /* What to do with the lock */
-){
- u32 mask; /* Mask of locks to take or release */
- int rc = SQLITE_OK; /* Result code */
- os2File *pFile = (os2File*)id;
- os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */
- os2ShmLink *pX; /* For looping over all siblings */
- os2ShmNode *pShmNode = p->pShmNode; /* Our node */
-
- assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
- assert( n>=1 );
- assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
- || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
- || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
- || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
- assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
-
- mask = (u32)((1U<<(ofst+n)) - (1U<<ofst));
- assert( n>1 || mask==(1<<ofst) );
-
-
- sqlite3_mutex_enter(pShmNode->mutex);
-
- if( flags & SQLITE_SHM_UNLOCK ){
- u32 allMask = 0; /* Mask of locks held by siblings */
-
- /* See if any siblings hold this same lock */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
- allMask |= pX->sharedMask;
- }
-
- /* Unlock the system-level locks */
- if( (mask & allMask)==0 ){
- rc = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
-
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- u32 allShared = 0; /* Union of locks held by connections other than "p" */
-
- /* Find out which shared locks are already held by sibling connections.
- ** If any sibling already holds an exclusive lock, go ahead and return
- ** SQLITE_BUSY.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- allShared |= pX->sharedMask;
- }
-
- /* Get shared locks at the system level, if necessary */
- if( rc==SQLITE_OK ){
- if( (allShared & mask)==0 ){
- rc = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
- }
-
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- }
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also mark the local connection as being locked.
- */
- if( rc==SQLITE_OK ){
- rc = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
- }
- }
- }
-
- sqlite3_mutex_leave(pShmNode->mutex);
-
- OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n",
- p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask,
- rc ? "failed" : "ok"));
-
- ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n",
- ofst, n, flags, rc))
-
- return rc;
-}
-
-/*
-** Implement a memory barrier or memory fence on shared memory.
-**
-** All loads and stores begun before the barrier must complete before
-** any load or store begun after the barrier.
-*/
-static void os2ShmBarrier(
- sqlite3_file *id /* Database file holding the shared memory */
-){
- UNUSED_PARAMETER(id);
- os2ShmEnterMutex();
- os2ShmLeaveMutex();
-}
-
-#else
-# define os2ShmMap 0
-# define os2ShmLock 0
-# define os2ShmBarrier 0
-# define os2ShmUnmap 0
-#endif /* #ifndef SQLITE_OMIT_WAL */
-
-
-/*
-** This vector defines all the methods that can operate on an
-** sqlite3_file for os2.
-*/
-static const sqlite3_io_methods os2IoMethod = {
- 2, /* iVersion */
- os2Close, /* xClose */
- os2Read, /* xRead */
- os2Write, /* xWrite */
- os2Truncate, /* xTruncate */
- os2Sync, /* xSync */
- os2FileSize, /* xFileSize */
- os2Lock, /* xLock */
- os2Unlock, /* xUnlock */
- os2CheckReservedLock, /* xCheckReservedLock */
- os2FileControl, /* xFileControl */
- os2SectorSize, /* xSectorSize */
- os2DeviceCharacteristics, /* xDeviceCharacteristics */
- os2ShmMap, /* xShmMap */
- os2ShmLock, /* xShmLock */
- os2ShmBarrier, /* xShmBarrier */
- os2ShmUnmap /* xShmUnmap */
-};
-
-
-/***************************************************************************
-** Here ends the I/O methods that form the sqlite3_io_methods object.
-**
-** The next block of code implements the VFS methods.
-****************************************************************************/
-
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
-*/
-static int getTempname(int nBuf, char *zBuf ){
- static const char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i, j;
- PSZ zTempPathCp;
- char zTempPath[CCHMAXPATH];
- ULONG ulDriveNum, ulDriveMap;
-
- /* It's odd to simulate an io-error here, but really this is just
- ** using the io-error infrastructure to test that SQLite handles this
- ** function failing.
- */
- SimulateIOError( return SQLITE_IOERR );
-
- if( sqlite3_temp_directory ) {
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory);
- } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) {
- char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp );
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF);
- free( zTempPathUTF );
- } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) {
- zTempPath[0] = (char)('A' + ulDriveNum - 1);
- zTempPath[1] = ':';
- zTempPath[2] = '\0';
- } else {
- zTempPath[0] = '\0';
- }
-
- /* Strip off a trailing slashes or backslashes, otherwise we would get *
- * multiple (back)slashes which causes DosOpen() to fail. *
- * Trailing spaces are not allowed, either. */
- j = sqlite3Strlen30(zTempPath);
- while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' ||
- zTempPath[j-1] == ' ' ) ){
- j--;
- }
- zTempPath[j] = '\0';
-
- /* We use 20 bytes to randomize the name */
- sqlite3_snprintf(nBuf-22, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
- j = sqlite3Strlen30(zBuf);
- sqlite3_randomness( 20, &zBuf[j] );
- for( i = 0; i < 20; i++, j++ ){
- zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
-
- OSTRACE(( "TEMP FILENAME: %s\n", zBuf ));
- return SQLITE_OK;
-}
-
-
-/*
-** Turn a relative pathname into a full pathname. Write the full
-** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname
-** bytes in size.
-*/
-static int os2FullPathname(
- sqlite3_vfs *pVfs, /* Pointer to vfs object */
- const char *zRelative, /* Possibly relative input path */
- int nFull, /* Size of output buffer in bytes */
- char *zFull /* Output buffer */
-){
- char *zRelativeCp = convertUtf8PathToCp( zRelative );
- char zFullCp[CCHMAXPATH] = "\0";
- char *zFullUTF;
- APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME,
- zFullCp, CCHMAXPATH );
- free( zRelativeCp );
- zFullUTF = convertCpPathToUtf8( zFullCp );
- sqlite3_snprintf( nFull, zFull, zFullUTF );
- free( zFullUTF );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-
-/*
-** Open a file.
-*/
-static int os2Open(
- sqlite3_vfs *pVfs, /* Not used */
- const char *zName, /* Name of the file (UTF-8) */
- sqlite3_file *id, /* Write the SQLite file handle here */
- int flags, /* Open mode flags */
- int *pOutFlags /* Status return flags */
-){
- HFILE h;
- ULONG ulOpenFlags = 0;
- ULONG ulOpenMode = 0;
- ULONG ulAction = 0;
- ULONG rc;
- os2File *pFile = (os2File*)id;
- const char *zUtf8Name = zName;
- char *zNameCp;
- char zTmpname[CCHMAXPATH];
-
- int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
- int isCreate = (flags & SQLITE_OPEN_CREATE);
- int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
-#ifndef NDEBUG
- int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
- int isReadonly = (flags & SQLITE_OPEN_READONLY);
- int eType = (flags & 0xFFFFFF00);
- int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
- || eType==SQLITE_OPEN_WAL
- ));
-#endif
-
- UNUSED_PARAMETER(pVfs);
- assert( id!=0 );
-
- /* Check the following statements are true:
- **
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
- ** (b) if CREATE is set, then READWRITE must also be set, and
- ** (c) if EXCLUSIVE is set, then CREATE must also be set.
- ** (d) if DELETEONCLOSE is set, then CREATE must also be set.
- */
- assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
- assert(isCreate==0 || isReadWrite);
- assert(isExclusive==0 || isCreate);
- assert(isDelete==0 || isCreate);
-
- /* The main DB, main journal, WAL file and master journal are never
- ** automatically deleted. Nor are they ever temporary files. */
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
-
- /* Assert that the upper layer has set one of the "file-type" flags. */
- assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
- || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
- );
-
- memset( pFile, 0, sizeof(*pFile) );
- pFile->h = (HFILE)-1;
-
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
- */
- if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- rc = getTempname(CCHMAXPATH, zTmpname);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- zUtf8Name = zTmpname;
- }
-
- if( isReadWrite ){
- ulOpenMode |= OPEN_ACCESS_READWRITE;
- }else{
- ulOpenMode |= OPEN_ACCESS_READONLY;
- }
-
- /* Open in random access mode for possibly better speed. Allow full
- ** sharing because file locks will provide exclusive access when needed.
- ** The handle should not be inherited by child processes and we don't
- ** want popups from the critical error handler.
- */
- ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR;
-
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
- ** as it is usually understood.
- */
- if( isExclusive ){
- /* Creates a new file, only if it does not already exist. */
- /* If the file exists, it fails. */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
- }else if( isCreate ){
- /* Open existing file, or create if it doesn't exist */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }else{
- /* Opens a file, only if it exists. */
- ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }
-
- zNameCp = convertUtf8PathToCp( zUtf8Name );
- rc = DosOpen( (PSZ)zNameCp,
- &h,
- &ulAction,
- 0L,
- FILE_NORMAL,
- ulOpenFlags,
- ulOpenMode,
- (PEAOP2)NULL );
- free( zNameCp );
-
- if( rc != NO_ERROR ){
- OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n",
- rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode ));
-
- if( isReadWrite ){
- return os2Open( pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
- pOutFlags );
- }else{
- return SQLITE_CANTOPEN;
- }
- }
-
- if( pOutFlags ){
- *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
- }
-
- os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname );
- pFile->zFullPathCp = convertUtf8PathToCp( zTmpname );
- pFile->pMethod = &os2IoMethod;
- pFile->flags = flags;
- pFile->h = h;
-
- OpenCounter(+1);
- OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags ));
- return SQLITE_OK;
-}
-
-/*
-** Delete the named file.
-*/
-static int os2Delete(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to delete */
- int syncDir /* Not used on os2 */
-){
- APIRET rc;
- char *zFilenameCp;
- SimulateIOError( return SQLITE_IOERR_DELETE );
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosDelete( (PSZ)zFilenameCp );
- free( zFilenameCp );
- OSTRACE(( "DELETE \"%s\"\n", zFilename ));
- return (rc == NO_ERROR ||
- rc == ERROR_FILE_NOT_FOUND ||
- rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE;
-}
-
-/*
-** Check the existance and status of a file.
-*/
-static int os2Access(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to check */
- int flags, /* Type of test to make on this file */
- int *pOut /* Write results here */
-){
- APIRET rc;
- FILESTATUS3 fsts3ConfigInfo;
- char *zFilenameCp;
-
- UNUSED_PARAMETER(pVfs);
- SimulateIOError( return SQLITE_IOERR_ACCESS; );
-
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD,
- &fsts3ConfigInfo, sizeof(FILESTATUS3) );
- free( zFilenameCp );
- OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n",
- fsts3ConfigInfo.attrFile, flags, rc ));
-
- switch( flags ){
- case SQLITE_ACCESS_EXISTS:
- /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
- ** as if it does not exist.
- */
- if( fsts3ConfigInfo.cbFile == 0 )
- rc = ERROR_FILE_NOT_FOUND;
- break;
- case SQLITE_ACCESS_READ:
- break;
- case SQLITE_ACCESS_READWRITE:
- if( fsts3ConfigInfo.attrFile & FILE_READONLY )
- rc = ERROR_ACCESS_DENIED;
- break;
- default:
- rc = ERROR_FILE_NOT_FOUND;
- assert( !"Invalid flags argument" );
- }
-
- *pOut = (rc == NO_ERROR);
- OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut ));
-
- return SQLITE_OK;
-}
-
-
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){
- HMODULE hmod;
- APIRET rc;
- char *zFilenameCp = convertUtf8PathToCp(zFilename);
- rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod);
- free(zFilenameCp);
- return rc != NO_ERROR ? 0 : (void*)hmod;
-}
-/*
-** A no-op since the error code is returned on the DosLoadModule call.
-** os2Dlopen returns zero if DosLoadModule is not successful.
-*/
-static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
-/* no-op */
-}
-static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
- PFN pfn;
- APIRET rc;
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn);
- if( rc != NO_ERROR ){
- /* if the symbol itself was not found, search again for the same
- * symbol with an extra underscore, that might be needed depending
- * on the calling convention */
- char _zSymbol[256] = "_";
- strncat(_zSymbol, zSymbol, 254);
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn);
- }
- return rc != NO_ERROR ? 0 : (void(*)(void))pfn;
-}
-static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
- DosFreeModule((HMODULE)pHandle);
-}
-#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
- #define os2DlOpen 0
- #define os2DlError 0
- #define os2DlSym 0
- #define os2DlClose 0
-#endif
-
-
-/*
-** Write up to nBuf bytes of randomness into zBuf.
-*/
-static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
- int n = 0;
-#if defined(SQLITE_TEST)
- n = nBuf;
- memset(zBuf, 0, nBuf);
-#else
- int i;
- PPIB ppib;
- PTIB ptib;
- DATETIME dt;
- static unsigned c = 0;
- /* Ordered by variation probability */
- static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW,
- QSV_MAXPRMEM, QSV_MAXSHMEM,
- QSV_TOTAVAILMEM, QSV_TOTRESMEM };
-
- /* 8 bytes; timezone and weekday don't increase the randomness much */
- if( (int)sizeof(dt)-3 <= nBuf - n ){
- c += 0x0100;
- DosGetDateTime(&dt);
- dt.year = (USHORT)((dt.year - 1900) | c);
- memcpy(&zBuf[n], &dt, sizeof(dt)-3);
- n += sizeof(dt)-3;
- }
-
- /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */
- if( (int)sizeof(ULONG) <= nBuf - n ){
- DosGetInfoBlocks(&ptib, &ppib);
- *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid,
- ptib->tib_ptib2->tib2_ultid);
- n += sizeof(ULONG);
- }
-
- /* Up to 6 * 4 bytes; variables depend on the system state */
- for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){
- DosQuerySysInfo(svIdx[i], svIdx[i],
- (PULONG)&zBuf[n], sizeof(ULONG));
- n += sizeof(ULONG);
- }
-#endif
-
- return n;
-}
-
-/*
-** Sleep for a little while. Return the amount of time slept.
-** The argument is the number of microseconds we want to sleep.
-** The return value is the number of microseconds of sleep actually
-** requested from the underlying operating system, a number which
-** might be greater than or equal to the argument, but not less
-** than the argument.
-*/
-static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){
- DosSleep( (microsec/1000) );
- return microsec;
-}
-
-/*
-** The following variable, if set to a non-zero value, becomes the result
-** returned from sqlite3OsCurrentTime(). This is used for testing.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_current_time = 0;
-#endif
-
-/*
-** Find the current time (in Universal Coordinated Time). Write into *piNow
-** the current time and date as a Julian Day number times 86_400_000. In
-** other words, write into *piNow the number of milliseconds since the Julian
-** epoch of noon in Greenwich on November 24, 4714 B.C according to the
-** proleptic Gregorian calendar.
-**
-** On success, return 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
-#ifdef SQLITE_TEST
- static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
-#endif
- int year, month, datepart, timepart;
-
- DATETIME dt;
- DosGetDateTime( &dt );
-
- year = dt.year;
- month = dt.month;
-
- /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
- ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c
- ** Calculate the Julian days
- */
- datepart = (int)dt.day - 32076 +
- 1461*(year + 4800 + (month - 14)/12)/4 +
- 367*(month - 2 - (month - 14)/12*12)/12 -
- 3*((year + 4900 + (month - 14)/12)/100)/4;
-
- /* Time in milliseconds, hours to noon added */
- timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 +
- ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000;
-
- *piNow = (sqlite3_int64)datepart*86400*1000 + timepart;
-
-#ifdef SQLITE_TEST
- if( sqlite3_current_time ){
- *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
- }
-#endif
-
- UNUSED_PARAMETER(pVfs);
- return 0;
-}
-
-/*
-** Find the current time (in Universal Coordinated Time). Write the
-** current time and date as a Julian Day number into *prNow and
-** return 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){
- int rc;
- sqlite3_int64 i;
- rc = os2CurrentTimeInt64(pVfs, &i);
- if( !rc ){
- *prNow = i/86400000.0;
- }
- return rc;
-}
-
-/*
-** The idea is that this function works like a combination of
-** GetLastError() and FormatMessage() on windows (or errno and
-** strerror_r() on unix). After an error is returned by an OS
-** function, SQLite calls this function with zBuf pointing to
-** a buffer of nBuf bytes. The OS layer should populate the
-** buffer with a nul-terminated UTF-8 encoded error message
-** describing the last IO error to have occurred within the calling
-** thread.
-**
-** If the error message is too large for the supplied buffer,
-** it should be truncated. The return value of xGetLastError
-** is zero if the error message fits in the buffer, or non-zero
-** otherwise (if the message was truncated). If non-zero is returned,
-** then it is not necessary to include the nul-terminator character
-** in the output buffer.
-**
-** Not supplying an error message will have no adverse effect
-** on SQLite. It is fine to have an implementation that never
-** returns an error message:
-**
-** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
-** assert(zBuf[0]=='\0');
-** return 0;
-** }
-**
-** However if an error message is supplied, it will be incorporated
-** by sqlite into the error message available to the user using
-** sqlite3_errmsg(), possibly making IO errors easier to debug.
-*/
-static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- assert(zBuf[0]=='\0');
- return 0;
-}
-
-/*
-** Initialize and deinitialize the operating system interface.
-*/
-SQLITE_API int sqlite3_os_init(void){
- static sqlite3_vfs os2Vfs = {
- 3, /* iVersion */
- sizeof(os2File), /* szOsFile */
- CCHMAXPATH, /* mxPathname */
- 0, /* pNext */
- "os2", /* zName */
- 0, /* pAppData */
-
- os2Open, /* xOpen */
- os2Delete, /* xDelete */
- os2Access, /* xAccess */
- os2FullPathname, /* xFullPathname */
- os2DlOpen, /* xDlOpen */
- os2DlError, /* xDlError */
- os2DlSym, /* xDlSym */
- os2DlClose, /* xDlClose */
- os2Randomness, /* xRandomness */
- os2Sleep, /* xSleep */
- os2CurrentTime, /* xCurrentTime */
- os2GetLastError, /* xGetLastError */
- os2CurrentTimeInt64, /* xCurrentTimeInt64 */
- 0, /* xSetSystemCall */
- 0, /* xGetSystemCall */
- 0 /* xNextSystemCall */
- };
- sqlite3_vfs_register(&os2Vfs, 1);
- initUconvObjects();
-/* sqlite3OSTrace = 1; */
- return SQLITE_OK;
-}
-SQLITE_API int sqlite3_os_end(void){
- freeUconvObjects();
- return SQLITE_OK;
-}
-
-#endif /* SQLITE_OS_OS2 */
-
-/************** End of os_os2.c **********************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
@@ -24347,45 +24683,20 @@ SQLITE_API int sqlite3_os_end(void){
#endif
/*
-** These #defines should enable >2GB file support on Posix if the
-** underlying operating system supports it. If the OS lacks
-** large file support, these should be no-ops.
-**
-** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
-** on the compiler command line. This is necessary if you are compiling
-** on a recent machine (ex: RedHat 7.2) but you want your code to work
-** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
-** without this option, LFS is enable. But LFS does not exist in the kernel
-** in RedHat 6.0, so the code won't work. Hence, for maximum binary
-** portability you should omit LFS.
-**
-** The previous paragraph was written in 2005. (This paragraph is written
-** on 2008-11-28.) These days, all Linux kernels support large files, so
-** you should probably leave LFS enabled. But some embedded platforms might
-** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
-*/
-#ifndef SQLITE_DISABLE_LFS
-# define _LARGE_FILE 1
-# ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
-# endif
-# define _LARGEFILE_SOURCE 1
-#endif
-
-/*
** standard include files.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+/* #include <time.h> */
#include <sys/time.h>
#include <errno.h>
-#ifndef SQLITE_OMIT_WAL
-#include <sys/mman.h>
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+# include <sys/mman.h>
#endif
-#if SQLITE_ENABLE_LOCKING_STYLE
+#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
# include <sys/ioctl.h>
# if OS_VXWORKS
# include <semaphore.h>
@@ -24414,6 +24725,7 @@ SQLITE_API int sqlite3_os_end(void){
** the SQLITE_UNIX_THREADS macro.
*/
#if SQLITE_THREADSAFE
+/* # include <pthread.h> */
# define SQLITE_UNIX_THREADS 1
#endif
@@ -24425,8 +24737,8 @@ SQLITE_API int sqlite3_os_end(void){
#endif
/*
- ** Default permissions when creating auto proxy dir
- */
+** Default permissions when creating auto proxy dir
+*/
#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
#endif
@@ -24467,17 +24779,28 @@ struct UnixUnusedFd {
typedef struct unixFile unixFile;
struct unixFile {
sqlite3_io_methods const *pMethod; /* Always the first entry */
+ sqlite3_vfs *pVfs; /* The VFS that created this unixFile */
unixInodeInfo *pInode; /* Info about locks on this inode */
int h; /* The file descriptor */
- int dirfd; /* File descriptor for the directory */
unsigned char eFileLock; /* The type of lock held on this fd */
- unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
+ unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
int lastErrno; /* The unix errno from last I/O error */
void *lockingContext; /* Locking style specific state */
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
const char *zPath; /* Name of the file */
unixShm *pShm; /* Shared memory segment information */
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
+#if SQLITE_MAX_MMAP_SIZE>0
+ int nFetchOut; /* Number of outstanding xFetch refs */
+ sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
+ sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */
+ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
+ void *pMapRegion; /* Memory mapped region */
+#endif
+#ifdef __QNXNTO__
+ int sectorSize; /* Device sector size */
+ int deviceCharacteristics; /* Precomputed device characteristics */
+#endif
#if SQLITE_ENABLE_LOCKING_STYLE
int openFlags; /* The flags specified at open() */
#endif
@@ -24485,10 +24808,9 @@ struct unixFile {
unsigned fsFlags; /* cached details from statfs() */
#endif
#if OS_VXWORKS
- int isDelete; /* Delete on close if true */
struct vxworksFileId *pId; /* Unique file ID */
#endif
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* The next group of variables are used to track whether or not the
** transaction counter in bytes 24-27 of database files are updated
** whenever any part of the database changes. An assertion fault will
@@ -24499,7 +24821,9 @@ struct unixFile {
unsigned char transCntrChng; /* True if the transaction counter changed */
unsigned char dbUpdate; /* True if any part of database file changed */
unsigned char inNormalWrite; /* True if in a normal write operation */
+
#endif
+
#ifdef SQLITE_TEST
/* In test mode, increase the size of this structure a bit so that
** it is larger than the struct CrashFile defined in test6.c.
@@ -24508,11 +24832,28 @@ struct unixFile {
#endif
};
+/* This variable holds the process id (pid) from when the xRandomness()
+** method was called. If xOpen() is called from a different process id,
+** indicating that a fork() has occurred, the PRNG will be reset.
+*/
+static int randomnessPid = 0;
+
/*
** Allowed values for the unixFile.ctrlFlags bitmask:
*/
-#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
-#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
+#define UNIXFILE_EXCL 0x01 /* Connections from one process only */
+#define UNIXFILE_RDONLY 0x02 /* Connection is read only */
+#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
+#ifndef SQLITE_DISABLE_DIRSYNC
+# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */
+#else
+# define UNIXFILE_DIRSYNC 0x00
+#endif
+#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
+#define UNIXFILE_DELETE 0x20 /* Delete on close */
+#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
+#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
+#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */
/*
** Include code that is common to all os_*.c files
@@ -24550,11 +24891,14 @@ struct unixFile {
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3OSTrace = 0;
-#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
#else
-#define OSTRACE(X)
+# define OSTRACE(X)
#endif
/*
@@ -24752,6 +25096,25 @@ SQLITE_API int sqlite3_open_file_count = 0;
#endif
/*
+** HAVE_MREMAP defaults to true on Linux and false everywhere else.
+*/
+#if !defined(HAVE_MREMAP)
+# if defined(__linux__) && defined(_GNU_SOURCE)
+# define HAVE_MREMAP 1
+# else
+# define HAVE_MREMAP 0
+# endif
+#endif
+
+/*
+** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek()
+** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined.
+*/
+#ifdef __ANDROID__
+# define lseek lseek64
+#endif
+
+/*
** Different Unix systems declare open() in different ways. Same use
** open(const char*,int,mode_t). Others use open(const char*,int,...).
** The difference is important when using a pointer to the function.
@@ -24764,13 +25127,30 @@ static int posixOpen(const char *zFile, int flags, int mode){
}
/*
+** On some systems, calls to fchown() will trigger a message in a security
+** log if they come from non-root processes. So avoid calling fchown() if
+** we are not running as root.
+*/
+static int posixFchown(int fd, uid_t uid, gid_t gid){
+#if OS_VXWORKS
+ return 0;
+#else
+ return geteuid() ? 0 : fchown(fd,uid,gid);
+#endif
+}
+
+/* Forward reference */
+static int openDirectory(const char*, int*);
+static int unixGetpagesize(void);
+
+/*
** Many system calls are accessed through pointer-to-functions so that
** they may be overridden at runtime to facilitate fault injection during
** testing and sandboxing. The following array holds the names and pointers
** to all overrideable system calls.
*/
static struct unix_syscall {
- const char *zName; /* Name of the sytem call */
+ const char *zName; /* Name of the system call */
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
sqlite3_syscall_ptr pDefault; /* Default value */
} aSyscall[] = {
@@ -24812,7 +25192,7 @@ static struct unix_syscall {
{ "read", (sqlite3_syscall_ptr)read, 0 },
#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
-#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
+#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
{ "pread", (sqlite3_syscall_ptr)pread, 0 },
#else
{ "pread", (sqlite3_syscall_ptr)0, 0 },
@@ -24829,7 +25209,7 @@ static struct unix_syscall {
{ "write", (sqlite3_syscall_ptr)write, 0 },
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
-#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
+#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
{ "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
#else
{ "pwrite", (sqlite3_syscall_ptr)0, 0 },
@@ -24845,11 +25225,7 @@ static struct unix_syscall {
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
aSyscall[13].pCurrent)
-#if SQLITE_ENABLE_LOCKING_STYLE
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
-#else
- { "fchmod", (sqlite3_syscall_ptr)0, 0 },
-#endif
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
@@ -24859,6 +25235,39 @@ static struct unix_syscall {
#endif
#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
+ { "unlink", (sqlite3_syscall_ptr)unlink, 0 },
+#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent)
+
+ { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
+#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent)
+
+ { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 },
+#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent)
+
+ { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
+#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
+
+ { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
+#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
+
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+ { "mmap", (sqlite3_syscall_ptr)mmap, 0 },
+#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent)
+
+ { "munmap", (sqlite3_syscall_ptr)munmap, 0 },
+#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent)
+
+#if HAVE_MREMAP
+ { "mremap", (sqlite3_syscall_ptr)mremap, 0 },
+#else
+ { "mremap", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
+ { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
+#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent)
+
+#endif
+
}; /* End of the overrideable system calls */
/*
@@ -24945,12 +25354,66 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
}
/*
-** Retry open() calls that fail due to EINTR
+** Do not accept any file descriptor less than this value, in order to avoid
+** opening database file using file descriptors that are commonly used for
+** standard input, output, and error.
*/
-static int robust_open(const char *z, int f, int m){
- int rc;
- do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR );
- return rc;
+#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
+# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
+#endif
+
+/*
+** Invoke open(). Do so multiple times, until it either succeeds or
+** fails for some reason other than EINTR.
+**
+** If the file creation mode "m" is 0 then set it to the default for
+** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally
+** 0644) as modified by the system umask. If m is not 0, then
+** make the file creation mode be exactly m ignoring the umask.
+**
+** The m parameter will be non-zero only when creating -wal, -journal,
+** and -shm files. We want those files to have *exactly* the same
+** permissions as their original database, unadulterated by the umask.
+** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a
+** transaction crashes and leaves behind hot journals, then any
+** process that is able to write to the database will also be able to
+** recover the hot journals.
+*/
+static int robust_open(const char *z, int f, mode_t m){
+ int fd;
+ mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
+ while(1){
+#if defined(O_CLOEXEC)
+ fd = osOpen(z,f|O_CLOEXEC,m2);
+#else
+ fd = osOpen(z,f,m2);
+#endif
+ if( fd<0 ){
+ if( errno==EINTR ) continue;
+ break;
+ }
+ if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
+ osClose(fd);
+ sqlite3_log(SQLITE_WARNING,
+ "attempt to open \"%s\" as file descriptor %d", z, fd);
+ fd = -1;
+ if( osOpen("/dev/null", f, m)<0 ) break;
+ }
+ if( fd>=0 ){
+ if( m!=0 ){
+ struct stat statbuf;
+ if( osFstat(fd, &statbuf)==0
+ && statbuf.st_size==0
+ && (statbuf.st_mode&0777)!=m
+ ){
+ osFchmod(fd, m);
+ }
+ }
+#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
+ osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+ }
+ return fd;
}
/*
@@ -24980,10 +25443,10 @@ static int unixMutexHeld(void) {
#endif
-#ifdef SQLITE_DEBUG
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
/*
** Helper function for printing out trace information from debugging
-** binaries. This returns the string represetation of the supplied
+** binaries. This returns the string representation of the supplied
** integer lock-type.
*/
static const char *azFileLock(int eFileLock){
@@ -25060,9 +25523,22 @@ static int lockTrace(int fd, int op, struct flock *p){
/*
** Retry ftruncate() calls that fail due to EINTR
+**
+** All calls to ftruncate() within this file should be made through this wrapper.
+** On the Android platform, bypassing the logic below could lead to a corrupt
+** database.
*/
static int robust_ftruncate(int h, sqlite3_int64 sz){
int rc;
+#ifdef __ANDROID__
+ /* On Android, ftruncate() always uses 32-bit offsets, even if
+ ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to
+ ** truncate a file to any size larger than 2GiB. Silently ignore any
+ ** such attempts. */
+ if( sz>(sqlite3_int64)0x7FFFFFFF ){
+ rc = SQLITE_OK;
+ }else
+#endif
do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR );
return rc;
}
@@ -25107,25 +25583,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
case EACCES:
/* EACCES is like EAGAIN during locking operations, but not any other time*/
if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
- (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
return SQLITE_BUSY;
}
/* else fall through */
case EPERM:
return SQLITE_PERM;
- /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
- ** this module never makes such a call. And the code in SQLite itself
- ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
- ** this case is also commented out. If the system does set errno to EDEADLK,
- ** the default SQLITE_IOERR_XXX code will be returned. */
-#if 0
- case EDEADLK:
- return SQLITE_IOERR_BLOCKED;
-#endif
-
#if EOPNOTSUPP!=ENOTSUP
case EOPNOTSUPP:
/* something went terribly awry, unless during file system support
@@ -25143,7 +25609,9 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
case ENODEV:
case ENXIO:
case ENOENT:
+#ifdef ESTALE /* ESTALE is not defined on Interix systems */
case ESTALE:
+#endif
case ENOSYS:
/* these should force the client to close the file and reconnect */
@@ -25153,7 +25621,6 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
}
-
/******************************************************************************
****************** Begin Unique File ID Utility Used By VxWorks ***************
**
@@ -25442,7 +25909,7 @@ static unixInodeInfo *inodeList = 0;
** The first argument passed to the macro should be the error code that
** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
** The two subsequent arguments should be the name of the OS function that
-** failed (e.g. "unlink", "open") and the the associated file-system path,
+** failed (e.g. "unlink", "open") and the associated file-system path,
** if any.
*/
#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__)
@@ -25465,7 +25932,7 @@ static int unixLogErrorAtLine(
zErr = aErr;
/* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
- ** assume that the system provides the the GNU version of strerror_r() that
+ ** assume that the system provides the GNU version of strerror_r() that
** returns a pointer to a buffer containing the error message. That pointer
** may point to aErr[], or it may point to some static storage somewhere.
** Otherwise, assume that the system provides the POSIX version of
@@ -25489,7 +25956,6 @@ static int unixLogErrorAtLine(
zErr = strerror(iErrno);
#endif
- assert( errcode!=SQLITE_OK );
if( zPath==0 ) zPath = "";
sqlite3_log(errcode,
"os_unix.c:%d: (%d) %s(%s) - %s",
@@ -25654,6 +26120,60 @@ static int findInodeInfo(
return SQLITE_OK;
}
+/*
+** Return TRUE if pFile has been renamed or unlinked since it was first opened.
+*/
+static int fileHasMoved(unixFile *pFile){
+#if OS_VXWORKS
+ return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId;
+#else
+ struct stat buf;
+ return pFile->pInode!=0 &&
+ (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
+#endif
+}
+
+
+/*
+** Check a unixFile that is a database. Verify the following:
+**
+** (1) There is exactly one hard link on the file
+** (2) The file is not a symbolic link
+** (3) The file has not been renamed or unlinked
+**
+** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right.
+*/
+static void verifyDbFile(unixFile *pFile){
+ struct stat buf;
+ int rc;
+ if( pFile->ctrlFlags & UNIXFILE_WARNED ){
+ /* One or more of the following warnings have already been issued. Do not
+ ** repeat them so as not to clutter the error log */
+ return;
+ }
+ rc = osFstat(pFile->h, &buf);
+ if( rc!=0 ){
+ sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
+ pFile->ctrlFlags |= UNIXFILE_WARNED;
+ return;
+ }
+ if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){
+ sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
+ pFile->ctrlFlags |= UNIXFILE_WARNED;
+ return;
+ }
+ if( buf.st_nlink>1 ){
+ sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath);
+ pFile->ctrlFlags |= UNIXFILE_WARNED;
+ return;
+ }
+ if( fileHasMoved(pFile) ){
+ sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
+ pFile->ctrlFlags |= UNIXFILE_WARNED;
+ return;
+ }
+}
+
/*
** This routine checks if there is a RESERVED lock held on the specified
@@ -25813,14 +26333,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){
*/
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
- unixInodeInfo *pInode = pFile->pInode;
+ unixInodeInfo *pInode;
struct flock lock;
int tErrno = 0;
assert( pFile );
OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h,
azFileLock(eFileLock), azFileLock(pFile->eFileLock),
- azFileLock(pInode->eFileLock), pInode->nShared , getpid()));
+ azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared , getpid()));
/* If there is already a lock of this type or more restrictive on the
** unixFile, do nothing. Don't use the end_lock: exit path, as
@@ -25961,7 +26481,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
}
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* Set up the transaction-counter change checking flags when
** transitioning from a SHARED to a RESERVED lock. The change
** from SHARED to RESERVED marks the beginning of a normal
@@ -26024,7 +26544,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
unixInodeInfo *pInode;
struct flock lock;
int rc = SQLITE_OK;
- int h;
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock,
@@ -26036,16 +26555,12 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
return SQLITE_OK;
}
unixEnterMutex();
- h = pFile->h;
pInode = pFile->pInode;
assert( pInode->nShared!=0 );
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
** transaction counter was updated if any part of the database
@@ -26054,11 +26569,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
** the file has changed and hence might not know to flush their
** cache. The use of a stale cache can lead to database corruption.
*/
-#if 0
- assert( pFile->inNormalWrite==0
- || pFile->dbUpdate==0
- || pFile->transCntrChng==1 );
-#endif
pFile->inNormalWrite = 0;
#endif
@@ -26160,14 +26670,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = lock.l_len = 0L;
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
if( unixFileLock(pFile, &lock)==0 ){
pInode->eFileLock = NO_LOCK;
}else{
rc = SQLITE_IOERR_UNLOCK;
- pFile->lastErrno = errno;
+ pFile->lastErrno = errno;
pInode->eFileLock = NO_LOCK;
pFile->eFileLock = NO_LOCK;
}
@@ -26183,7 +26690,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
closePendingFds(pFile);
}
}
-
+
end_unlock:
unixLeaveMutex();
if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
@@ -26198,9 +26705,17 @@ end_unlock:
** the requested locking level, this routine is a no-op.
*/
static int unixUnlock(sqlite3_file *id, int eFileLock){
+#if SQLITE_MAX_MMAP_SIZE>0
+ assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 );
+#endif
return posixUnlock(id, eFileLock, 0);
}
+#if SQLITE_MAX_MMAP_SIZE>0
+static int unixMapfile(unixFile *pFd, i64 nByte);
+static void unixUnmapfile(unixFile *pFd);
+#endif
+
/*
** This function performs the parts of the "close file" operation
** common to all locking schemes. It closes the directory and file
@@ -26213,23 +26728,29 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){
*/
static int closeUnixFile(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
- if( pFile->dirfd>=0 ){
- robust_close(pFile, pFile->dirfd, __LINE__);
- pFile->dirfd=-1;
- }
+#if SQLITE_MAX_MMAP_SIZE>0
+ unixUnmapfile(pFile);
+#endif
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
pFile->h = -1;
}
#if OS_VXWORKS
if( pFile->pId ){
- if( pFile->isDelete ){
- unlink(pFile->pId->zCanonicalName);
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){
+ osUnlink(pFile->pId->zCanonicalName);
}
vxworksReleaseFileId(pFile->pId);
pFile->pId = 0;
}
#endif
+#ifdef SQLITE_UNLINK_AFTER_CLOSE
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){
+ osUnlink(pFile->zPath);
+ sqlite3_free(*(char**)&pFile->zPath);
+ pFile->zPath = 0;
+ }
+#endif
OSTRACE(("CLOSE %-3d\n", pFile->h));
OpenCounter(-1);
sqlite3_free(pFile->pUnused);
@@ -26243,6 +26764,7 @@ static int closeUnixFile(sqlite3_file *id){
static int unixClose(sqlite3_file *id){
int rc = SQLITE_OK;
unixFile *pFile = (unixFile *)id;
+ verifyDbFile(pFile);
unixUnlock(id, NO_LOCK);
unixEnterMutex();
@@ -26311,9 +26833,9 @@ static int nolockClose(sqlite3_file *id) {
/******************************************************************************
************************* Begin dot-file Locking ******************************
**
-** The dotfile locking implementation uses the existance of separate lock
-** files in order to control access to the database. This works on just
-** about every filesystem imaginable. But there are serious downsides:
+** The dotfile locking implementation uses the existence of separate lock
+** files (really a directory) to control access to the database. This works
+** on just about every filesystem imaginable. But there are serious downsides:
**
** (1) There is zero concurrency. A single reader blocks all other
** connections from reading or writing the database.
@@ -26324,15 +26846,15 @@ static int nolockClose(sqlite3_file *id) {
** Nevertheless, a dotlock is an appropriate locking mode for use if no
** other locking strategy is available.
**
-** Dotfile locking works by creating a file in the same directory as the
-** database and with the same name but with a ".lock" extension added.
-** The existance of a lock file implies an EXCLUSIVE lock. All other lock
-** types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
+** Dotfile locking works by creating a subdirectory in the same directory as
+** the database and with the same name but with a ".lock" extension added.
+** The existence of a lock directory implies an EXCLUSIVE lock. All other
+** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
*/
/*
** The file suffix added to the data base filename in order to create the
-** lock file.
+** lock directory.
*/
#define DOTLOCK_SUFFIX ".lock"
@@ -26399,7 +26921,6 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
*/
static int dotlockLock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
- int fd;
char *zLockFile = (char *)pFile->lockingContext;
int rc = SQLITE_OK;
@@ -26419,9 +26940,9 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
}
/* grab an exclusive lock */
- fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
- if( fd<0 ){
- /* failed to open/create the file, someone else may have stolen the lock */
+ rc = osMkdir(zLockFile, 0777);
+ if( rc<0 ){
+ /* failed to open/create the lock directory */
int tErrno = errno;
if( EEXIST == tErrno ){
rc = SQLITE_BUSY;
@@ -26433,7 +26954,6 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
}
return rc;
}
- robust_close(pFile, fd, __LINE__);
/* got it, set the type and return ok */
pFile->eFileLock = eFileLock;
@@ -26452,10 +26972,11 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
char *zLockFile = (char *)pFile->lockingContext;
+ int rc;
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, getpid()));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -26473,9 +26994,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
/* To fully unlock the database, delete the lock file */
assert( eFileLock==NO_LOCK );
- if( unlink(zLockFile) ){
- int rc = 0;
+ rc = osRmdir(zLockFile);
+ if( rc<0 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
+ if( rc<0 ){
int tErrno = errno;
+ rc = 0;
if( ENOENT != tErrno ){
rc = SQLITE_IOERR_UNLOCK;
}
@@ -26492,13 +27015,13 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
** Close a file. Make sure the lock has been released before closing.
*/
static int dotlockClose(sqlite3_file *id) {
- int rc;
+ int rc = SQLITE_OK;
if( id ){
unixFile *pFile = (unixFile*)id;
dotlockUnlock(id, NO_LOCK);
sqlite3_free(pFile->lockingContext);
+ rc = closeUnixFile(id);
}
- rc = closeUnixFile(id);
return rc;
}
/****************** End of the dot-file lock implementation *******************
@@ -26702,10 +27225,12 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) {
** Close a file.
*/
static int flockClose(sqlite3_file *id) {
+ int rc = SQLITE_OK;
if( id ){
flockUnlock(id, NO_LOCK);
+ rc = closeUnixFile(id);
}
- return closeUnixFile(id);
+ return rc;
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */
@@ -26748,7 +27273,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
/* Otherwise see if some other process holds it. */
if( !reserved ){
sem_t *pSem = pFile->pInode->pSem;
- struct stat statBuf;
if( sem_trywait(pSem)==-1 ){
int tErrno = errno;
@@ -26801,7 +27325,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
*/
static int semLock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
- int fd;
sem_t *pSem = pFile->pInode->pSem;
int rc = SQLITE_OK;
@@ -26840,7 +27363,7 @@ static int semUnlock(sqlite3_file *id, int eFileLock) {
assert( pFile );
assert( pSem );
OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, getpid()));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -26979,11 +27502,12 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
+ afpLockingContext *context;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
- afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
+ context = (afpLockingContext *) pFile->lockingContext;
if( context->reserved ){
*pResOut = 1;
return SQLITE_OK;
@@ -27123,7 +27647,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){
** operating system calls for the specified lock.
*/
if( eFileLock==SHARED_LOCK ){
- int lrc1, lrc2, lrc1Errno;
+ int lrc1, lrc2, lrc1Errno = 0;
long lk, mask;
assert( pInode->nShared==0 );
@@ -27254,7 +27778,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
SimulateIOError( h=(-1) )
SimulateIOErrorBenign(0);
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
** transaction counter was updated if any part of the database
@@ -27402,7 +27926,7 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
** NB: If you define USE_PREAD or USE_PREAD64, then it might also
** be necessary to define _XOPEN_SOURCE to be 500. This varies from
** one system to another. Since SQLite does not define USE_PREAD
-** any any form by default, we will not attempt to define _XOPEN_SOURCE.
+** in any form by default, we will not attempt to define _XOPEN_SOURCE.
** See tickets #2741 and #2681.
**
** To avoid stomping the errno value on a failed read the lastErrno value
@@ -27410,35 +27934,51 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
*/
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
int got;
+ int prior = 0;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
TIMER_START;
+ assert( cnt==(cnt&0x1ffff) );
+ assert( id->h>2 );
+ cnt &= 0x1ffff;
+ do{
#if defined(USE_PREAD)
- do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
- SimulateIOError( got = -1 );
+ got = osPread(id->h, pBuf, cnt, offset);
+ SimulateIOError( got = -1 );
#elif defined(USE_PREAD64)
- do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR);
- SimulateIOError( got = -1 );
+ got = osPread64(id->h, pBuf, cnt, offset);
+ SimulateIOError( got = -1 );
#else
- newOffset = lseek(id->h, offset, SEEK_SET);
- SimulateIOError( newOffset-- );
- if( newOffset!=offset ){
- if( newOffset == -1 ){
- ((unixFile*)id)->lastErrno = errno;
- }else{
- ((unixFile*)id)->lastErrno = 0;
+ newOffset = lseek(id->h, offset, SEEK_SET);
+ SimulateIOError( newOffset-- );
+ if( newOffset!=offset ){
+ if( newOffset == -1 ){
+ ((unixFile*)id)->lastErrno = errno;
+ }else{
+ ((unixFile*)id)->lastErrno = 0;
+ }
+ return -1;
}
- return -1;
- }
- do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
+ got = osRead(id->h, pBuf, cnt);
#endif
+ if( got==cnt ) break;
+ if( got<0 ){
+ if( errno==EINTR ){ got = 1; continue; }
+ prior = 0;
+ ((unixFile*)id)->lastErrno = errno;
+ break;
+ }else if( got>0 ){
+ cnt -= got;
+ offset += got;
+ prior += got;
+ pBuf = (void*)(got + (char*)pBuf);
+ }
+ }while( got>0 );
TIMER_END;
- if( got<0 ){
- ((unixFile*)id)->lastErrno = errno;
- }
- OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
- return got;
+ OSTRACE(("READ %-3d %5d %7lld %llu\n",
+ id->h, got+prior, offset-prior, TIMER_ELAPSED));
+ return got+prior;
}
/*
@@ -27455,6 +27995,8 @@ static int unixRead(
unixFile *pFile = (unixFile *)id;
int got;
assert( id );
+ assert( offset>=0 );
+ assert( amt>0 );
/* If this is a database file (not a journal, master-journal or temp
** file), the bytes in the locking range should never be read or written. */
@@ -27465,6 +28007,23 @@ static int unixRead(
);
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this read request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
+ return SQLITE_OK;
+ }else{
+ int nCopy = pFile->mmapSize - offset;
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
+
got = seekAndRead(pFile, offset, pBuf, amt);
if( got==amt ){
return SQLITE_OK;
@@ -27480,42 +28039,60 @@ static int unixRead(
}
/*
-** Seek to the offset in id->offset then read cnt bytes into pBuf.
-** Return the number of bytes actually read. Update the offset.
-**
-** To avoid stomping the errno value on a failed write the lastErrno value
-** is set before returning.
+** Attempt to seek the file-descriptor passed as the first argument to
+** absolute offset iOff, then attempt to write nBuf bytes of data from
+** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
+** return the actual number of bytes written (which may be less than
+** nBuf).
*/
-static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
- int got;
-#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
- i64 newOffset;
-#endif
+static int seekAndWriteFd(
+ int fd, /* File descriptor to write to */
+ i64 iOff, /* File offset to begin writing at */
+ const void *pBuf, /* Copy data from this buffer to the file */
+ int nBuf, /* Size of buffer pBuf in bytes */
+ int *piErrno /* OUT: Error number if error occurs */
+){
+ int rc = 0; /* Value returned by system call */
+
+ assert( nBuf==(nBuf&0x1ffff) );
+ assert( fd>2 );
+ nBuf &= 0x1ffff;
TIMER_START;
+
#if defined(USE_PREAD)
- do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
+ do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR );
#elif defined(USE_PREAD64)
- do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
+ do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR);
#else
- newOffset = lseek(id->h, offset, SEEK_SET);
- SimulateIOError( newOffset-- );
- if( newOffset!=offset ){
- if( newOffset == -1 ){
- ((unixFile*)id)->lastErrno = errno;
- }else{
- ((unixFile*)id)->lastErrno = 0;
+ do{
+ i64 iSeek = lseek(fd, iOff, SEEK_SET);
+ SimulateIOError( iSeek-- );
+
+ if( iSeek!=iOff ){
+ if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0);
+ return -1;
}
- return -1;
- }
- do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
+ rc = osWrite(fd, pBuf, nBuf);
+ }while( rc<0 && errno==EINTR );
#endif
+
TIMER_END;
- if( got<0 ){
- ((unixFile*)id)->lastErrno = errno;
- }
+ OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED));
+
+ if( rc<0 && piErrno ) *piErrno = errno;
+ return rc;
+}
- OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
- return got;
+
+/*
+** Seek to the offset in id->offset then read cnt bytes into pBuf.
+** Return the number of bytes actually read. Update the offset.
+**
+** To avoid stomping the errno value on a failed write the lastErrno value
+** is set before returning.
+*/
+static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
+ return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno);
}
@@ -27543,7 +28120,7 @@ static int unixWrite(
);
#endif
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) then record the fact that the database
@@ -27565,6 +28142,23 @@ static int unixWrite(
}
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this write request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
+ return SQLITE_OK;
+ }else{
+ int nCopy = pFile->mmapSize - offset;
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
+
while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
amt -= wrote;
offset += wrote;
@@ -27597,11 +28191,11 @@ SQLITE_API int sqlite3_fullsync_count = 0;
/*
** We do not trust systems to provide a working fdatasync(). Some do.
-** Others do no. To be safe, we will stick with the (slower) fsync().
-** If you know that your system does support fdatasync() correctly,
+** Others do no. To be safe, we will stick with the (slightly slower)
+** fsync(). If you know that your system does support fdatasync() correctly,
** then simply compile with -Dfdatasync=fdatasync
*/
-#if !defined(fdatasync) && !defined(__linux__)
+#if !defined(fdatasync)
# define fdatasync fsync
#endif
@@ -27710,6 +28304,47 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
}
/*
+** Open a file descriptor to the directory containing file zFilename.
+** If successful, *pFd is set to the opened file descriptor and
+** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
+** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
+** value.
+**
+** The directory file descriptor is used for only one thing - to
+** fsync() a directory to make sure file creation and deletion events
+** are flushed to disk. Such fsyncs are not needed on newer
+** journaling filesystems, but are required on older filesystems.
+**
+** This routine can be overridden using the xSetSysCall interface.
+** The ability to override this routine was added in support of the
+** chromium sandbox. Opening a directory is a security risk (we are
+** told) so making it overrideable allows the chromium sandbox to
+** replace this routine with a harmless no-op. To make this routine
+** a no-op, replace it with a stub that returns SQLITE_OK but leaves
+** *pFd set to a negative number.
+**
+** If SQLITE_OK is returned, the caller is responsible for closing
+** the file descriptor *pFd using close().
+*/
+static int openDirectory(const char *zFilename, int *pFd){
+ int ii;
+ int fd = -1;
+ char zDirname[MAX_PATHNAME+1];
+
+ sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
+ for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
+ if( ii>0 ){
+ zDirname[ii] = '\0';
+ fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
+ if( fd>=0 ){
+ OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
+ }
+ }
+ *pFd = fd;
+ return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
+}
+
+/*
** Make sure all writes to a particular file are committed to disk.
**
** If dataOnly==0 then both the file itself and its metadata (file
@@ -27749,28 +28384,23 @@ static int unixSync(sqlite3_file *id, int flags){
pFile->lastErrno = errno;
return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
}
- if( pFile->dirfd>=0 ){
- OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd,
+
+ /* Also fsync the directory containing the file if the DIRSYNC flag
+ ** is set. This is a one-time occurrence. Many systems (examples: AIX)
+ ** are unable to fsync a directory, so ignore errors on the fsync.
+ */
+ if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){
+ int dirfd;
+ OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
HAVE_FULLFSYNC, isFullsync));
-#ifndef SQLITE_DISABLE_DIRSYNC
- /* The directory sync is only attempted if full_fsync is
- ** turned off or unavailable. If a full_fsync occurred above,
- ** then the directory sync is superfluous.
- */
- if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){
- /*
- ** We have received multiple reports of fsync() returning
- ** errors when applied to directories on certain file systems.
- ** A failed directory sync is not a big deal. So it seems
- ** better to ignore the error. Ticket #1657
- */
- /* pFile->lastErrno = errno; */
- /* return SQLITE_IOERR; */
+ rc = osOpenDirectory(pFile->zPath, &dirfd);
+ if( rc==SQLITE_OK && dirfd>=0 ){
+ full_fsync(dirfd, 0, 0);
+ robust_close(pFile, dirfd, __LINE__);
+ }else if( rc==SQLITE_CANTOPEN ){
+ rc = SQLITE_OK;
}
-#endif
- /* Only need to sync once, so close the directory when we are done */
- robust_close(pFile, pFile->dirfd, __LINE__);
- pFile->dirfd = -1;
+ pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
}
return rc;
}
@@ -27789,16 +28419,16 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
** actual file size after the operation may be larger than the requested
** size).
*/
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
- rc = robust_ftruncate(pFile->h, (off_t)nByte);
+ rc = robust_ftruncate(pFile->h, nByte);
if( rc ){
pFile->lastErrno = errno;
return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
}else{
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) and we truncate the file to zero length,
@@ -27811,6 +28441,16 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
}
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* If the file was just truncated to a size smaller than the currently
+ ** mapped region, reduce the effective mapping size as well. SQLite will
+ ** use read() and write() to access data beyond this point from now on.
+ */
+ if( nByte<pFile->mmapSize ){
+ pFile->mmapSize = nByte;
+ }
+#endif
+
return SQLITE_OK;
}
}
@@ -27852,14 +28492,12 @@ static int proxyFileControl(sqlite3_file*,int,void*);
/*
** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
-** file-control operation.
-**
-** If the user has configured a chunk-size for this file, it could be
-** that the file needs to be extended at this point. Otherwise, the
-** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
+** file-control operation. Enlarge the database to nBytes in size
+** (rounded up to the next chunk-size). If the database is already
+** nBytes or larger, this routine is a no-op.
*/
static int fcntlSizeHint(unixFile *pFile, i64 nByte){
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
i64 nSize; /* Required file size */
struct stat buf; /* Used to hold return values of fstat() */
@@ -27901,30 +28539,111 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
}
}
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){
+ int rc;
+ if( pFile->szChunk<=0 ){
+ if( robust_ftruncate(pFile->h, nByte) ){
+ pFile->lastErrno = errno;
+ return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
+ }
+ }
+
+ rc = unixMapfile(pFile, nByte);
+ return rc;
+ }
+#endif
+
return SQLITE_OK;
}
/*
+** If *pArg is initially negative then this is a query. Set *pArg to
+** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
+**
+** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
+*/
+static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
+ if( *pArg<0 ){
+ *pArg = (pFile->ctrlFlags & mask)!=0;
+ }else if( (*pArg)==0 ){
+ pFile->ctrlFlags &= ~mask;
+ }else{
+ pFile->ctrlFlags |= mask;
+ }
+}
+
+/* Forward declaration */
+static int unixGetTempname(int nBuf, char *zBuf);
+
+/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
+ unixFile *pFile = (unixFile*)id;
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((unixFile*)id)->eFileLock;
+ *(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
- *(int*)pArg = ((unixFile*)id)->lastErrno;
+ *(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
- ((unixFile*)id)->szChunk = *(int *)pArg;
+ pFile->szChunk = *(int *)pArg;
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
- return fcntlSizeHint((unixFile *)id, *(i64 *)pArg);
+ int rc;
+ SimulateIOErrorBenign(1);
+ rc = fcntlSizeHint(pFile, *(i64 *)pArg);
+ SimulateIOErrorBenign(0);
+ return rc;
}
-#ifndef NDEBUG
+ case SQLITE_FCNTL_PERSIST_WAL: {
+ unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
+ unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_VFSNAME: {
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_TEMPFILENAME: {
+ char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname );
+ if( zTFile ){
+ unixGetTempname(pFile->pVfs->mxPathname, zTFile);
+ *(char**)pArg = zTFile;
+ }
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_HAS_MOVED: {
+ *(int*)pArg = fileHasMoved(pFile);
+ return SQLITE_OK;
+ }
+#if SQLITE_MAX_MMAP_SIZE>0
+ case SQLITE_FCNTL_MMAP_SIZE: {
+ i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
+ if( newLimit>sqlite3GlobalConfig.mxMmap ){
+ newLimit = sqlite3GlobalConfig.mxMmap;
+ }
+ *(i64*)pArg = pFile->mmapSizeMax;
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
+ pFile->mmapSizeMax = newLimit;
+ if( pFile->mmapSize>0 ){
+ unixUnmapfile(pFile);
+ rc = unixMapfile(pFile, -1);
+ }
+ }
+ return rc;
+ }
+#endif
+#ifdef SQLITE_DEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
** it hence it is OK for the transaction change counter to be
@@ -27941,9 +28660,6 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return proxyFileControl(id,op,pArg);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
- case SQLITE_FCNTL_SYNC_OMITTED: {
- return SQLITE_OK; /* A no-op */
- }
}
return SQLITE_NOTFOUND;
}
@@ -27958,21 +28674,138 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
** a database and its journal file) that the sector size will be the
** same for both.
*/
+#ifndef __QNXNTO__
static int unixSectorSize(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return SQLITE_DEFAULT_SECTOR_SIZE;
}
+#endif
/*
-** Return the device characteristics for the file. This is always 0 for unix.
+** The following version of unixSectorSize() is optimized for QNX.
*/
-static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
- UNUSED_PARAMETER(NotUsed);
- return 0;
+#ifdef __QNXNTO__
+#include <sys/dcmd_blk.h>
+#include <sys/statvfs.h>
+static int unixSectorSize(sqlite3_file *id){
+ unixFile *pFile = (unixFile*)id;
+ if( pFile->sectorSize == 0 ){
+ struct statvfs fsInfo;
+
+ /* Set defaults for non-supported filesystems */
+ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
+ pFile->deviceCharacteristics = 0;
+ if( fstatvfs(pFile->h, &fsInfo) == -1 ) {
+ return pFile->sectorSize;
+ }
+
+ if( !strcmp(fsInfo.f_basetype, "tmp") ) {
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( strstr(fsInfo.f_basetype, "etfs") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ /* etfs cluster size writes are atomic */
+ (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) |
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ /* full bitset of atomics from max sector size and smaller */
+ ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( strstr(fsInfo.f_basetype, "dos") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ /* full bitset of atomics from max sector size and smaller */
+ ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else{
+ pFile->deviceCharacteristics =
+ SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ 0;
+ }
+ }
+ /* Last chance verification. If the sector size isn't a multiple of 512
+ ** then it isn't valid.*/
+ if( pFile->sectorSize % 512 != 0 ){
+ pFile->deviceCharacteristics = 0;
+ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
+ }
+ return pFile->sectorSize;
}
+#endif /* __QNXNTO__ */
-#ifndef SQLITE_OMIT_WAL
+/*
+** Return the device characteristics for the file.
+**
+** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
+** However, that choice is controversial since technically the underlying
+** file system does not always provide powersafe overwrites. (In other
+** words, after a power-loss event, parts of the file that were never
+** written might end up being altered.) However, non-PSOW behavior is very,
+** very rare. And asserting PSOW makes a large reduction in the amount
+** of required I/O for journaling, since a lot of padding is eliminated.
+** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
+** available to turn it off and URI query parameter available to turn it off.
+*/
+static int unixDeviceCharacteristics(sqlite3_file *id){
+ unixFile *p = (unixFile*)id;
+ int rc = 0;
+#ifdef __QNXNTO__
+ if( p->sectorSize==0 ) unixSectorSize(id);
+ rc = p->deviceCharacteristics;
+#endif
+ if( p->ctrlFlags & UNIXFILE_PSOW ){
+ rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
+ }
+ return rc;
+}
+
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+
+/*
+** Return the system page size.
+**
+** This function should not be called directly by other code in this file.
+** Instead, it should be called via macro osGetpagesize().
+*/
+static int unixGetpagesize(void){
+#if defined(_BSD_SOURCE)
+ return getpagesize();
+#else
+ return (int)sysconf(_SC_PAGESIZE);
+#endif
+}
+#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */
+
+#ifndef SQLITE_OMIT_WAL
/*
** Object used to represent an shared memory buffer.
@@ -28038,11 +28871,9 @@ struct unixShm {
unixShmNode *pShmNode; /* The underlying unixShmNode object */
unixShm *pNext; /* Next unixShm with the same unixShmNode */
u8 hasMutex; /* True if holding the unixShmNode mutex */
+ u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection within its unixShmNode */
-#endif
};
/*
@@ -28091,7 +28922,7 @@ static int unixShmSystemLock(
#ifdef SQLITE_DEBUG
{ u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = (1<<(ofst+n)) - (1<<ofst);
+ mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
OSTRACE(("unlock %d ok", ofst));
@@ -28125,6 +28956,22 @@ static int unixShmSystemLock(
return rc;
}
+/*
+** Return the minimum number of 32KB shm regions that should be mapped at
+** a time, assuming that each mapping must be an integer multiple of the
+** current system page-size.
+**
+** Usually, this is 1. The exception seems to be systems that are configured
+** to use 64KB pages - in this case each mapping must cover at least two
+** shm regions.
+*/
+static int unixShmRegionPerMap(void){
+ int shmsz = 32*1024; /* SHM region size */
+ int pgsz = osGetpagesize(); /* System page size */
+ assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */
+ if( pgsz<shmsz ) return 1;
+ return pgsz/shmsz;
+}
/*
** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
@@ -28136,12 +28983,13 @@ static void unixShmPurge(unixFile *pFd){
unixShmNode *p = pFd->pInode->pShmNode;
assert( unixMutexHeld() );
if( p && p->nRef==0 ){
+ int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
- if( p->mutex ) sqlite3_mutex_free(p->mutex);
- for(i=0; i<p->nRegion; i++){
+ sqlite3_mutex_free(p->mutex);
+ for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->h>=0 ){
- munmap(p->apRegion[i], p->szRegion);
+ osMunmap(p->apRegion[i], p->szRegion);
}else{
sqlite3_free(p->apRegion[i]);
}
@@ -28216,8 +29064,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
/* Call fstat() to figure out the permissions on the database file. If
** a new *-shm file is created, an attempt will be made to create it
- ** with the same permissions. The actual permissions the file is created
- ** with are subject to the current umask setting.
+ ** with the same permissions.
*/
if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){
rc = SQLITE_IOERR_FSTAT;
@@ -28225,16 +29072,16 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
}
#ifdef SQLITE_SHM_DIRECTORY
- nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30;
+ nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31;
#else
- nShmFilename = 5 + (int)strlen(pDbFd->zPath);
+ nShmFilename = 6 + (int)strlen(pDbFd->zPath);
#endif
pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
if( pShmNode==0 ){
rc = SQLITE_NOMEM;
goto shm_open_err;
}
- memset(pShmNode, 0, sizeof(*pShmNode));
+ memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
#ifdef SQLITE_SHM_DIRECTORY
sqlite3_snprintf(nShmFilename, zShmFilename,
@@ -28254,21 +29101,22 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
}
if( pInode->bProcessLock==0 ){
- pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT,
- (sStat.st_mode & 0777));
+ int openFlags = O_RDWR | O_CREAT;
+ if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
+ openFlags = O_RDONLY;
+ pShmNode->isReadonly = 1;
+ }
+ pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
if( pShmNode->h<0 ){
- const char *zRO;
- zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
- if( zRO && sqlite3GetBoolean(zRO) ){
- pShmNode->h = robust_open(zShmFilename, O_RDONLY,
- (sStat.st_mode & 0777));
- pShmNode->isReadonly = 1;
- }
- if( pShmNode->h<0 ){
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
- goto shm_open_err;
- }
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
+ goto shm_open_err;
}
+
+ /* If this process is running as root, make sure that the SHM file
+ ** is owned by the same user that owns the original database. Otherwise,
+ ** the original owner will not be able to connect.
+ */
+ osFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
/* Check to see if another process is holding the dead-man switch.
** If not, truncate the file to zero length.
@@ -28346,6 +29194,8 @@ static int unixShmMap(
unixShm *p;
unixShmNode *pShmNode;
int rc = SQLITE_OK;
+ int nShmPerMap = unixShmRegionPerMap();
+ int nReqRegion;
/* If the shared-memory file has not yet been opened, open it now. */
if( pDbFd->pShm==0 ){
@@ -28361,9 +29211,12 @@ static int unixShmMap(
assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
- if( pShmNode->nRegion<=iRegion ){
+ /* Minimum number of regions required to be mapped. */
+ nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
+
+ if( pShmNode->nRegion<nReqRegion ){
char **apNew; /* New apRegion[] array */
- int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
+ int nByte = nReqRegion*szRegion; /* Minimum required file size */
struct stat sStat; /* Used by fstat() */
pShmNode->szRegion = szRegion;
@@ -28381,34 +29234,52 @@ static int unixShmMap(
if( sStat.st_size<nByte ){
/* The requested memory region does not exist. If bExtend is set to
** false, exit early. *pp will be set to NULL and SQLITE_OK returned.
- **
- ** Alternatively, if bExtend is true, use ftruncate() to allocate
- ** the requested memory region.
*/
- if( !bExtend ) goto shmpage_out;
- if( robust_ftruncate(pShmNode->h, nByte) ){
- rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate",
- pShmNode->zFilename);
+ if( !bExtend ){
goto shmpage_out;
}
+
+ /* Alternatively, if bExtend is true, extend the file. Do this by
+ ** writing a single byte to the end of each (OS) page being
+ ** allocated or extended. Technically, we need only write to the
+ ** last page in order to extend the file. But writing to all new
+ ** pages forces the OS to allocate them immediately, which reduces
+ ** the chances of SIGBUS while accessing the mapped region later on.
+ */
+ else{
+ static const int pgsz = 4096;
+ int iPg;
+
+ /* Write to the last byte of each newly allocated or extended page */
+ assert( (nByte % pgsz)==0 );
+ for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
+ if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=1 ){
+ const char *zFile = pShmNode->zFilename;
+ rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
+ goto shmpage_out;
+ }
+ }
+ }
}
}
/* Map the requested memory region into this processes address space. */
apNew = (char **)sqlite3_realloc(
- pShmNode->apRegion, (iRegion+1)*sizeof(char *)
+ pShmNode->apRegion, nReqRegion*sizeof(char *)
);
if( !apNew ){
rc = SQLITE_IOERR_NOMEM;
goto shmpage_out;
}
pShmNode->apRegion = apNew;
- while(pShmNode->nRegion<=iRegion){
+ while( pShmNode->nRegion<nReqRegion ){
+ int nMap = szRegion*nShmPerMap;
+ int i;
void *pMem;
if( pShmNode->h>=0 ){
- pMem = mmap(0, szRegion,
+ pMem = osMmap(0, nMap,
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
- MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
+ MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
);
if( pMem==MAP_FAILED ){
rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
@@ -28422,8 +29293,11 @@ static int unixShmMap(
}
memset(pMem, 0, szRegion);
}
- pShmNode->apRegion[pShmNode->nRegion] = pMem;
- pShmNode->nRegion++;
+
+ for(i=0; i<nShmPerMap; i++){
+ pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i];
+ }
+ pShmNode->nRegion += nShmPerMap;
}
}
@@ -28607,7 +29481,7 @@ static int unixShmUnmap(
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
- if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename);
+ if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename);
unixShmPurge(pDbFd);
}
unixLeaveMutex();
@@ -28623,6 +29497,227 @@ static int unixShmUnmap(
# define unixShmUnmap 0
#endif /* #ifndef SQLITE_OMIT_WAL */
+#if SQLITE_MAX_MMAP_SIZE>0
+/*
+** If it is currently memory mapped, unmap file pFd.
+*/
+static void unixUnmapfile(unixFile *pFd){
+ assert( pFd->nFetchOut==0 );
+ if( pFd->pMapRegion ){
+ osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
+ pFd->pMapRegion = 0;
+ pFd->mmapSize = 0;
+ pFd->mmapSizeActual = 0;
+ }
+}
+
+/*
+** Attempt to set the size of the memory mapping maintained by file
+** descriptor pFd to nNew bytes. Any existing mapping is discarded.
+**
+** If successful, this function sets the following variables:
+**
+** unixFile.pMapRegion
+** unixFile.mmapSize
+** unixFile.mmapSizeActual
+**
+** If unsuccessful, an error message is logged via sqlite3_log() and
+** the three variables above are zeroed. In this case SQLite should
+** continue accessing the database using the xRead() and xWrite()
+** methods.
+*/
+static void unixRemapfile(
+ unixFile *pFd, /* File descriptor object */
+ i64 nNew /* Required mapping size */
+){
+ const char *zErr = "mmap";
+ int h = pFd->h; /* File descriptor open on db file */
+ u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */
+ i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */
+ u8 *pNew = 0; /* Location of new mapping */
+ int flags = PROT_READ; /* Flags to pass to mmap() */
+
+ assert( pFd->nFetchOut==0 );
+ assert( nNew>pFd->mmapSize );
+ assert( nNew<=pFd->mmapSizeMax );
+ assert( nNew>0 );
+ assert( pFd->mmapSizeActual>=pFd->mmapSize );
+ assert( MAP_FAILED!=0 );
+
+ if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
+
+ if( pOrig ){
+#if HAVE_MREMAP
+ i64 nReuse = pFd->mmapSize;
+#else
+ const int szSyspage = osGetpagesize();
+ i64 nReuse = (pFd->mmapSize & ~(szSyspage-1));
+#endif
+ u8 *pReq = &pOrig[nReuse];
+
+ /* Unmap any pages of the existing mapping that cannot be reused. */
+ if( nReuse!=nOrig ){
+ osMunmap(pReq, nOrig-nReuse);
+ }
+
+#if HAVE_MREMAP
+ pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE);
+ zErr = "mremap";
+#else
+ pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse);
+ if( pNew!=MAP_FAILED ){
+ if( pNew!=pReq ){
+ osMunmap(pNew, nNew - nReuse);
+ pNew = 0;
+ }else{
+ pNew = pOrig;
+ }
+ }
+#endif
+
+ /* The attempt to extend the existing mapping failed. Free it. */
+ if( pNew==MAP_FAILED || pNew==0 ){
+ osMunmap(pOrig, nReuse);
+ }
+ }
+
+ /* If pNew is still NULL, try to create an entirely new mapping. */
+ if( pNew==0 ){
+ pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0);
+ }
+
+ if( pNew==MAP_FAILED ){
+ pNew = 0;
+ nNew = 0;
+ unixLogError(SQLITE_OK, zErr, pFd->zPath);
+
+ /* If the mmap() above failed, assume that all subsequent mmap() calls
+ ** will probably fail too. Fall back to using xRead/xWrite exclusively
+ ** in this case. */
+ pFd->mmapSizeMax = 0;
+ }
+ pFd->pMapRegion = (void *)pNew;
+ pFd->mmapSize = pFd->mmapSizeActual = nNew;
+}
+
+/*
+** Memory map or remap the file opened by file-descriptor pFd (if the file
+** is already mapped, the existing mapping is replaced by the new). Or, if
+** there already exists a mapping for this file, and there are still
+** outstanding xFetch() references to it, this function is a no-op.
+**
+** If parameter nByte is non-negative, then it is the requested size of
+** the mapping to create. Otherwise, if nByte is less than zero, then the
+** requested size is the size of the file on disk. The actual size of the
+** created mapping is either the requested size or the value configured
+** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller.
+**
+** SQLITE_OK is returned if no error occurs (even if the mapping is not
+** recreated as a result of outstanding references) or an SQLite error
+** code otherwise.
+*/
+static int unixMapfile(unixFile *pFd, i64 nByte){
+ i64 nMap = nByte;
+ int rc;
+
+ assert( nMap>=0 || pFd->nFetchOut==0 );
+ if( pFd->nFetchOut>0 ) return SQLITE_OK;
+
+ if( nMap<0 ){
+ struct stat statbuf; /* Low-level file information */
+ rc = osFstat(pFd->h, &statbuf);
+ if( rc!=SQLITE_OK ){
+ return SQLITE_IOERR_FSTAT;
+ }
+ nMap = statbuf.st_size;
+ }
+ if( nMap>pFd->mmapSizeMax ){
+ nMap = pFd->mmapSizeMax;
+ }
+
+ if( nMap!=pFd->mmapSize ){
+ if( nMap>0 ){
+ unixRemapfile(pFd, nMap);
+ }else{
+ unixUnmapfile(pFd);
+ }
+ }
+
+ return SQLITE_OK;
+}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
+
+/*
+** If possible, return a pointer to a mapping of file fd starting at offset
+** iOff. The mapping must be valid for at least nAmt bytes.
+**
+** If such a pointer can be obtained, store it in *pp and return SQLITE_OK.
+** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK.
+** Finally, if an error does occur, return an SQLite error code. The final
+** value of *pp is undefined in this case.
+**
+** If this function does return a pointer, the caller must eventually
+** release the reference by calling unixUnfetch().
+*/
+static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
+#if SQLITE_MAX_MMAP_SIZE>0
+ unixFile *pFd = (unixFile *)fd; /* The underlying database file */
+#endif
+ *pp = 0;
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFd->mmapSizeMax>0 ){
+ if( pFd->pMapRegion==0 ){
+ int rc = unixMapfile(pFd, -1);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ if( pFd->mmapSize >= iOff+nAmt ){
+ *pp = &((u8 *)pFd->pMapRegion)[iOff];
+ pFd->nFetchOut++;
+ }
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** If the third argument is non-NULL, then this function releases a
+** reference obtained by an earlier call to unixFetch(). The second
+** argument passed to this function must be the same as the corresponding
+** argument that was passed to the unixFetch() invocation.
+**
+** Or, if the third argument is NULL, then this function is being called
+** to inform the VFS layer that, according to POSIX, any existing mapping
+** may now be invalid and should be unmapped.
+*/
+static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
+#if SQLITE_MAX_MMAP_SIZE>0
+ unixFile *pFd = (unixFile *)fd; /* The underlying database file */
+ UNUSED_PARAMETER(iOff);
+
+ /* If p==0 (unmap the entire file) then there must be no outstanding
+ ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
+ ** then there must be at least one outstanding. */
+ assert( (p==0)==(pFd->nFetchOut==0) );
+
+ /* If p!=0, it must match the iOff value. */
+ assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
+
+ if( p ){
+ pFd->nFetchOut--;
+ }else{
+ unixUnmapfile(pFd);
+ }
+
+ assert( pFd->nFetchOut>=0 );
+#else
+ UNUSED_PARAMETER(fd);
+ UNUSED_PARAMETER(p);
+ UNUSED_PARAMETER(iOff);
+#endif
+ return SQLITE_OK;
+}
+
/*
** Here ends the implementation of all sqlite3_file methods.
**
@@ -28642,7 +29737,7 @@ static int unixShmUnmap(
** looks at the filesystem type and tries to guess the best locking
** strategy from that.
**
-** For finder-funtion F, two objects are created:
+** For finder-function F, two objects are created:
**
** (1) The real finder-function named "FImpt()".
**
@@ -28663,7 +29758,7 @@ static int unixShmUnmap(
** * An I/O method finder function called FINDER that returns a pointer
** to the METHOD object in the previous bullet.
*/
-#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \
+#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK, SHMMAP) \
static const sqlite3_io_methods METHOD = { \
VERSION, /* iVersion */ \
CLOSE, /* xClose */ \
@@ -28678,10 +29773,12 @@ static const sqlite3_io_methods METHOD = { \
unixFileControl, /* xFileControl */ \
unixSectorSize, /* xSectorSize */ \
unixDeviceCharacteristics, /* xDeviceCapabilities */ \
- unixShmMap, /* xShmMap */ \
+ SHMMAP, /* xShmMap */ \
unixShmLock, /* xShmLock */ \
unixShmBarrier, /* xShmBarrier */ \
- unixShmUnmap /* xShmUnmap */ \
+ unixShmUnmap, /* xShmUnmap */ \
+ unixFetch, /* xFetch */ \
+ unixUnfetch, /* xUnfetch */ \
}; \
static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \
UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \
@@ -28698,20 +29795,22 @@ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \
IOMETHODS(
posixIoFinder, /* Finder function name */
posixIoMethods, /* sqlite3_io_methods object name */
- 2, /* shared memory is enabled */
+ 3, /* shared memory and mmap are enabled */
unixClose, /* xClose method */
unixLock, /* xLock method */
unixUnlock, /* xUnlock method */
- unixCheckReservedLock /* xCheckReservedLock method */
+ unixCheckReservedLock, /* xCheckReservedLock method */
+ unixShmMap /* xShmMap method */
)
IOMETHODS(
nolockIoFinder, /* Finder function name */
nolockIoMethods, /* sqlite3_io_methods object name */
- 1, /* shared memory is disabled */
+ 3, /* shared memory is disabled */
nolockClose, /* xClose method */
nolockLock, /* xLock method */
nolockUnlock, /* xUnlock method */
- nolockCheckReservedLock /* xCheckReservedLock method */
+ nolockCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
IOMETHODS(
dotlockIoFinder, /* Finder function name */
@@ -28720,7 +29819,8 @@ IOMETHODS(
dotlockClose, /* xClose method */
dotlockLock, /* xLock method */
dotlockUnlock, /* xUnlock method */
- dotlockCheckReservedLock /* xCheckReservedLock method */
+ dotlockCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
@@ -28731,7 +29831,8 @@ IOMETHODS(
flockClose, /* xClose method */
flockLock, /* xLock method */
flockUnlock, /* xUnlock method */
- flockCheckReservedLock /* xCheckReservedLock method */
+ flockCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28743,7 +29844,8 @@ IOMETHODS(
semClose, /* xClose method */
semLock, /* xLock method */
semUnlock, /* xUnlock method */
- semCheckReservedLock /* xCheckReservedLock method */
+ semCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28755,7 +29857,8 @@ IOMETHODS(
afpClose, /* xClose method */
afpLock, /* xLock method */
afpUnlock, /* xUnlock method */
- afpCheckReservedLock /* xCheckReservedLock method */
+ afpCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28780,7 +29883,8 @@ IOMETHODS(
proxyClose, /* xClose method */
proxyLock, /* xLock method */
proxyUnlock, /* xUnlock method */
- proxyCheckReservedLock /* xCheckReservedLock method */
+ proxyCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28793,7 +29897,8 @@ IOMETHODS(
unixClose, /* xClose method */
unixLock, /* xLock method */
nfsUnlock, /* xUnlock method */
- unixCheckReservedLock /* xCheckReservedLock method */
+ unixCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28902,7 +30007,7 @@ static const sqlite3_io_methods
#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */
/*
-** An abstract type for a pointer to a IO method finder function:
+** An abstract type for a pointer to an IO method finder function:
*/
typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
@@ -28920,12 +30025,9 @@ typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
static int fillInUnixFile(
sqlite3_vfs *pVfs, /* Pointer to vfs object */
int h, /* Open file descriptor of file being opened */
- int dirfd, /* Directory file descriptor */
sqlite3_file *pId, /* Write to the unixFile structure here */
const char *zFilename, /* Name of the file being opened */
- int noLock, /* Omit locking if true */
- int isDelete, /* Delete on close if true */
- int isReadOnly /* True if the file is opened read-only */
+ int ctrlFlags /* Zero or more UNIXFILE_* values */
){
const sqlite3_io_methods *pLockingStyle;
unixFile *pNew = (unixFile *)pId;
@@ -28933,11 +30035,6 @@ static int fillInUnixFile(
assert( pNew->pInode==NULL );
- /* Parameter isDelete is only used on vxworks. Express this explicitly
- ** here to prevent compiler warnings about unused parameters.
- */
- UNUSED_PARAMETER(isDelete);
-
/* Usually the path zFilename should not be a relative pathname. The
** exception is when opening the proxy "conch" file in builds that
** include the special Apple locking styles.
@@ -28949,28 +30046,34 @@ static int fillInUnixFile(
assert( zFilename==0 || zFilename[0]=='/' );
#endif
+ /* No locking occurs in temporary files */
+ assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 );
+
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
pNew->h = h;
- pNew->dirfd = dirfd;
+ pNew->pVfs = pVfs;
pNew->zPath = zFilename;
- if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
- pNew->ctrlFlags = UNIXFILE_EXCL;
- }else{
- pNew->ctrlFlags = 0;
+ pNew->ctrlFlags = (u8)ctrlFlags;
+#if SQLITE_MAX_MMAP_SIZE>0
+ pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap;
+#endif
+ if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
+ "psow", SQLITE_POWERSAFE_OVERWRITE) ){
+ pNew->ctrlFlags |= UNIXFILE_PSOW;
}
- if( isReadOnly ){
- pNew->ctrlFlags |= UNIXFILE_RDONLY;
+ if( strcmp(pVfs->zName,"unix-excl")==0 ){
+ pNew->ctrlFlags |= UNIXFILE_EXCL;
}
#if OS_VXWORKS
pNew->pId = vxworksFindFileId(zFilename);
if( pNew->pId==0 ){
- noLock = 1;
+ ctrlFlags |= UNIXFILE_NOLOCK;
rc = SQLITE_NOMEM;
}
#endif
- if( noLock ){
+ if( ctrlFlags & UNIXFILE_NOLOCK ){
pLockingStyle = &nolockIoMethods;
}else{
pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew);
@@ -28990,7 +30093,7 @@ static int fillInUnixFile(
unixEnterMutex();
rc = findInodeInfo(pNew, &pNew->pInode);
if( rc!=SQLITE_OK ){
- /* If an error occured in findInodeInfo(), close the file descriptor
+ /* If an error occurred in findInodeInfo(), close the file descriptor
** immediately, before releasing the mutex. findInodeInfo() may fail
** in two scenarios:
**
@@ -29048,6 +30151,7 @@ static int fillInUnixFile(
*/
char *zLockFile;
int nFilename;
+ assert( zFilename!=0 );
nFilename = (int)strlen(zFilename) + 6;
zLockFile = (char *)sqlite3_malloc(nFilename);
if( zLockFile==0 ){
@@ -29087,53 +30191,21 @@ static int fillInUnixFile(
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);
h = -1;
- unlink(zFilename);
- isDelete = 0;
+ osUnlink(zFilename);
+ pNew->ctrlFlags |= UNIXFILE_DELETE;
}
- pNew->isDelete = isDelete;
#endif
if( rc!=SQLITE_OK ){
- if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__);
if( h>=0 ) robust_close(pNew, h, __LINE__);
}else{
pNew->pMethod = pLockingStyle;
OpenCounter(+1);
+ verifyDbFile(pNew);
}
return rc;
}
/*
-** Open a file descriptor to the directory containing file zFilename.
-** If successful, *pFd is set to the opened file descriptor and
-** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM
-** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined
-** value.
-**
-** If SQLITE_OK is returned, the caller is responsible for closing
-** the file descriptor *pFd using close().
-*/
-static int openDirectory(const char *zFilename, int *pFd){
- int ii;
- int fd = -1;
- char zDirname[MAX_PATHNAME+1];
-
- sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
- for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
- if( ii>0 ){
- zDirname[ii] = '\0';
- fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
- if( fd>=0 ){
-#ifdef FD_CLOEXEC
- osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
-#endif
- OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
- }
- }
- *pFd = fd;
- return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
-}
-
-/*
** Return the name of a directory in which to put temporary files.
** If no suitable temporary file directory can be found, return NULL.
*/
@@ -29141,6 +30213,7 @@ static const char *unixTempFileDir(void){
static const char *azDirs[] = {
0,
0,
+ 0,
"/var/tmp",
"/usr/tmp",
"/tmp",
@@ -29151,7 +30224,8 @@ static const char *unixTempFileDir(void){
const char *zDir = 0;
azDirs[0] = sqlite3_temp_directory;
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
if( zDir==0 ) continue;
if( osStat(zDir, &buf) ) continue;
@@ -29187,18 +30261,19 @@ static int unixGetTempname(int nBuf, char *zBuf){
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
- if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){
+ if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){
return SQLITE_ERROR;
}
do{
- sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
+ sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
j = (int)strlen(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
+ zBuf[j+1] = 0;
}while( osAccess(zBuf,0)==0 );
return SQLITE_OK;
}
@@ -29246,8 +30321,8 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
** descriptor on the same path, fail, and return an error to SQLite.
**
** Even if a subsequent open() call does succeed, the consequences of
- ** not searching for a resusable file descriptor are not dire. */
- if( 0==stat(zPath, &sStat) ){
+ ** not searching for a reusable file descriptor are not dire. */
+ if( 0==osStat(zPath, &sStat) ){
unixInodeInfo *pInode;
unixEnterMutex();
@@ -29277,12 +30352,10 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
** written to *pMode. If an IO error occurs, an SQLite error code is
** returned and the value of *pMode is not modified.
**
-** If the file being opened is a temporary file, it is always created with
-** the octal permissions 0600 (read/writable by owner only). If the file
-** is a database or master journal file, it is created with the permissions
-** mask SQLITE_DEFAULT_FILE_PERMISSIONS.
-**
-** Finally, if the file being opened is a WAL or regular journal file, then
+** In most cases, this routine sets *pMode to 0, which will become
+** an indication to robust_open() to create the file using
+** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask.
+** But if the file being opened is a WAL or regular journal file, then
** this function queries the file-system for the permissions on the
** corresponding database file and sets *pMode to this value. Whenever
** possible, WAL and journal files are created using the same permissions
@@ -29296,10 +30369,14 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */
int flags, /* Flags passed as 4th argument to xOpen() */
- mode_t *pMode /* OUT: Permissions to open file with */
+ mode_t *pMode, /* OUT: Permissions to open file with */
+ uid_t *pUid, /* OUT: uid to set on the file */
+ gid_t *pGid /* OUT: gid to set on the file */
){
int rc = SQLITE_OK; /* Return Code */
- *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
+ *pMode = 0;
+ *pUid = 0;
+ *pGid = 0;
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
char zDb[MAX_PATHNAME+1]; /* Database file path */
int nDb; /* Number of valid bytes in zDb */
@@ -29314,17 +30391,27 @@ static int findCreateFileMode(
** "<path to db>-journalNN"
** "<path to db>-walNN"
**
- ** where NN is a 4 digit decimal number. The NN naming schemes are
+ ** where NN is a decimal number. The NN naming schemes are
** used by the test_multiplex.c module.
*/
nDb = sqlite3Strlen30(zPath) - 1;
- while( nDb>0 && zPath[nDb]!='-' ) nDb--;
- if( nDb==0 ) return SQLITE_OK;
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--;
+ if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
+#else
+ while( zPath[nDb]!='-' ){
+ assert( nDb>0 );
+ assert( zPath[nDb]!='\n' );
+ nDb--;
+ }
+#endif
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
- if( 0==stat(zDb, &sStat) ){
+ if( 0==osStat(zDb, &sStat) ){
*pMode = sStat.st_mode & 0777;
+ *pUid = sStat.st_uid;
+ *pGid = sStat.st_gid;
}else{
rc = SQLITE_IOERR_FSTAT;
}
@@ -29365,11 +30452,11 @@ static int unixOpen(
){
unixFile *p = (unixFile *)pFile;
int fd = -1; /* File descriptor returned by open() */
- int dirfd = -1; /* Directory file descriptor */
int openFlags = 0; /* Flags to pass to open() */
int eType = flags&0xFFFFFF00; /* Type of file to open */
int noLock; /* True to omit locking primitives */
int rc = SQLITE_OK; /* Function Return Code */
+ int ctrlFlags = 0; /* UNIXFILE_* flags */
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
@@ -29379,12 +30466,15 @@ static int unixOpen(
#if SQLITE_ENABLE_LOCKING_STYLE
int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY);
#endif
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
+ struct statfs fsInfo;
+#endif
/* If creating a master or main-file journal, this function will open
** a file-descriptor on the directory too. The first time unixSync()
** is called the directory file descriptor will be fsync()ed and close()d.
*/
- int isOpenDirectory = (isCreate && (
+ int syncDir = (isCreate && (
eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
@@ -29393,7 +30483,7 @@ static int unixOpen(
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
- char zTmpname[MAX_PATHNAME+1];
+ char zTmpname[MAX_PATHNAME+2];
const char *zName = zPath;
/* Check the following statements are true:
@@ -29422,6 +30512,16 @@ static int unixOpen(
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
+ /* Detect a pid change and reset the PRNG. There is a race condition
+ ** here such that two or more threads all trying to open databases at
+ ** the same instant might all reset the PRNG. But multiple resets
+ ** are harmless.
+ */
+ if( randomnessPid!=getpid() ){
+ randomnessPid = getpid();
+ sqlite3_randomness(0,0);
+ }
+
memset(p, 0, sizeof(unixFile));
if( eType==SQLITE_OPEN_MAIN_DB ){
@@ -29436,14 +30536,24 @@ static int unixOpen(
}
}
p->pUnused = pUnused;
+
+ /* Database filenames are double-zero terminated if they are not
+ ** URIs with parameters. Hence, they can always be passed into
+ ** sqlite3_uri_parameter(). */
+ assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 );
+
}else if( !zName ){
/* If zName is NULL, the upper layer is requesting a temp file. */
- assert(isDelete && !isOpenDirectory);
- rc = unixGetTempname(MAX_PATHNAME+1, zTmpname);
+ assert(isDelete && !syncDir);
+ rc = unixGetTempname(MAX_PATHNAME+2, zTmpname);
if( rc!=SQLITE_OK ){
return rc;
}
zName = zTmpname;
+
+ /* Generated temporary filenames are always double-zero terminated
+ ** for use by sqlite3_uri_parameter(). */
+ assert( zName[strlen(zName)+1]==0 );
}
/* Determine the value of the flags parameter passed to POSIX function
@@ -29458,7 +30568,9 @@ static int unixOpen(
if( fd<0 ){
mode_t openMode; /* Permissions to create file with */
- rc = findCreateFileMode(zName, flags, &openMode);
+ uid_t uid; /* Userid for the file */
+ gid_t gid; /* Groupid for the file */
+ rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid);
if( rc!=SQLITE_OK ){
assert( !p->pUnused );
assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
@@ -29479,6 +30591,14 @@ static int unixOpen(
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
goto open_finished;
}
+
+ /* If this process is running as root and if creating a new rollback
+ ** journal or WAL file, set the ownership of the journal or WAL to be
+ ** the same as the original database.
+ */
+ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
+ osFchown(fd, uid, gid);
+ }
}
assert( fd>=0 );
if( pOutFlags ){
@@ -29493,8 +30613,14 @@ static int unixOpen(
if( isDelete ){
#if OS_VXWORKS
zPath = zName;
+#elif defined(SQLITE_UNLINK_AFTER_CLOSE)
+ zPath = sqlite3_mprintf("%s", zName);
+ if( zPath==0 ){
+ robust_close(p, fd, __LINE__);
+ return SQLITE_NOMEM;
+ }
#else
- unlink(zName);
+ osUnlink(zName);
#endif
}
#if SQLITE_ENABLE_LOCKING_STYLE
@@ -29503,31 +30629,12 @@ static int unixOpen(
}
#endif
- if( isOpenDirectory ){
- rc = openDirectory(zPath, &dirfd);
- if( rc!=SQLITE_OK ){
- /* It is safe to close fd at this point, because it is guaranteed not
- ** to be open on a database file. If it were open on a database file,
- ** it would not be safe to close as this would release any locks held
- ** on the file by this process. */
- assert( eType!=SQLITE_OPEN_MAIN_DB );
- robust_close(p, fd, __LINE__);
- goto open_finished;
- }
- }
-
-#ifdef FD_CLOEXEC
- osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
-#endif
-
noLock = eType!=SQLITE_OPEN_MAIN_DB;
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
- struct statfs fsInfo;
if( fstatfs(fd, &fsInfo) == -1 ){
((unixFile*)pFile)->lastErrno = errno;
- if( dirfd>=0 ) robust_close(p, dirfd, __LINE__);
robust_close(p, fd, __LINE__);
return SQLITE_IOERR_ACCESS;
}
@@ -29535,7 +30642,14 @@ static int unixOpen(
((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
}
#endif
-
+
+ /* Set up appropriate ctrlFlags */
+ if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
+ if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
+ if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
+ if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
+ if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
+
#if SQLITE_ENABLE_LOCKING_STYLE
#if SQLITE_PREFER_PROXY_LOCKING
isAutoProxy = 1;
@@ -29549,7 +30663,6 @@ static int unixOpen(
if( envforce!=NULL ){
useProxy = atoi(envforce)>0;
}else{
- struct statfs fsInfo;
if( statfs(zPath, &fsInfo) == -1 ){
/* In theory, the close(fd) call is sub-optimal. If the file opened
** with fd is a database file, and there are other connections open
@@ -29559,9 +30672,6 @@ static int unixOpen(
** not while other file descriptors opened by the same process on
** the same file are working. */
p->lastErrno = errno;
- if( dirfd>=0 ){
- robust_close(p, dirfd, __LINE__);
- }
robust_close(p, fd, __LINE__);
rc = SQLITE_IOERR_ACCESS;
goto open_finished;
@@ -29569,8 +30679,7 @@ static int unixOpen(
useProxy = !(fsInfo.f_flags&MNT_LOCAL);
}
if( useProxy ){
- rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
- isDelete, isReadonly);
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
if( rc==SQLITE_OK ){
rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
if( rc!=SQLITE_OK ){
@@ -29587,8 +30696,8 @@ static int unixOpen(
}
#endif
- rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock,
- isDelete, isReadonly);
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
+
open_finished:
if( rc!=SQLITE_OK ){
sqlite3_free(p->pUnused);
@@ -29609,13 +30718,22 @@ static int unixDelete(
int rc = SQLITE_OK;
UNUSED_PARAMETER(NotUsed);
SimulateIOError(return SQLITE_IOERR_DELETE);
- if( unlink(zPath)==(-1) && errno!=ENOENT ){
- return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
+ if( osUnlink(zPath)==(-1) ){
+ if( errno==ENOENT
+#if OS_VXWORKS
+ || osAccess(zPath,0)!=0
+#endif
+ ){
+ rc = SQLITE_IOERR_DELETE_NOENT;
+ }else{
+ rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
+ }
+ return rc;
}
#ifndef SQLITE_DISABLE_DIRSYNC
- if( dirSync ){
+ if( (dirSync & 1)!=0 ){
int fd;
- rc = openDirectory(zPath, &fd);
+ rc = osOpenDirectory(zPath, &fd);
if( rc==SQLITE_OK ){
#if OS_VXWORKS
if( fsync(fd)==-1 )
@@ -29626,6 +30744,8 @@ static int unixDelete(
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
}
robust_close(0, fd, __LINE__);
+ }else if( rc==SQLITE_CANTOPEN ){
+ rc = SQLITE_OK;
}
}
#endif
@@ -29633,7 +30753,7 @@ static int unixDelete(
}
/*
-** Test the existance of or access permissions of file zPath. The
+** Test the existence of or access permissions of file zPath. The
** test performed depends on the value of flags:
**
** SQLITE_ACCESS_EXISTS: Return 1 if the file exists
@@ -29668,7 +30788,7 @@ static int unixAccess(
*pResOut = (osAccess(zPath, amode)==0);
if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
struct stat buf;
- if( 0==stat(zPath, &buf) && buf.st_size==0 ){
+ if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
*pResOut = 0;
}
}
@@ -29799,20 +30919,20 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
** tests repeatable.
*/
memset(zBuf, 0, nBuf);
+ randomnessPid = getpid();
#if !defined(SQLITE_TEST)
{
- int pid, fd;
+ int fd, got;
fd = robust_open("/dev/urandom", O_RDONLY, 0);
if( fd<0 ){
time_t t;
time(&t);
memcpy(zBuf, &t, sizeof(t));
- pid = getpid();
- memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
- assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
- nBuf = sizeof(t) + sizeof(pid);
+ memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
+ assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
+ nBuf = sizeof(t) + sizeof(randomnessPid);
}else{
- do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR );
+ do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
robust_close(0, fd, __LINE__);
}
}
@@ -29866,10 +30986,12 @@ SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
-** On success, return 0. Return 1 if the time and date cannot be found.
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
+** cannot be found.
*/
static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
+ int rc = SQLITE_OK;
#if defined(NO_GETTOD)
time_t t;
time(&t);
@@ -29880,8 +31002,11 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000;
#else
struct timeval sNow;
- gettimeofday(&sNow, 0);
- *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
+ if( gettimeofday(&sNow, 0)==0 ){
+ *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
+ }else{
+ rc = SQLITE_ERROR;
+ }
#endif
#ifdef SQLITE_TEST
@@ -29890,7 +31015,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
}
#endif
UNUSED_PARAMETER(NotUsed);
- return 0;
+ return rc;
}
/*
@@ -29899,11 +31024,12 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
** return 0. Return 1 if the time and date cannot be found.
*/
static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
- sqlite3_int64 i;
+ sqlite3_int64 i = 0;
+ int rc;
UNUSED_PARAMETER(NotUsed);
- unixCurrentTimeInt64(0, &i);
+ rc = unixCurrentTimeInt64(0, &i);
*prNow = i/86400000.0;
- return 0;
+ return rc;
}
/*
@@ -29949,7 +31075,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** address in the shared range is taken for a SHARED lock, the entire
** shared range is taken for an EXCLUSIVE lock):
**
-** PENDING_BYTE 0x40000000
+** PENDING_BYTE 0x40000000
** RESERVED_BYTE 0x40000001
** SHARED_RANGE 0x40000002 -> 0x40000200
**
@@ -30018,7 +31144,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** proxy path against the values stored in the conch. The conch file is
** stored in the same directory as the database file and the file name
** is patterned after the database file name as ".<databasename>-conch".
-** If the conch file does not exist, or it's contents do not match the
+** If the conch file does not exist, or its contents do not match the
** host ID and/or proxy path, then the lock is escalated to an exclusive
** lock and the conch file contents is updated with the host ID and proxy
** path and the lock is downgraded to a shared lock again. If the conch
@@ -30070,7 +31196,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will
** force proxy locking to be used for every database file opened, and 0
** will force automatic proxy locking to be disabled for all database
-** files (explicity calling the SQLITE_SET_LOCKPROXYFILE pragma or
+** files (explicitly calling the SQLITE_SET_LOCKPROXYFILE pragma or
** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING).
*/
@@ -30156,7 +31282,7 @@ static int proxyCreateLockPath(const char *lockPath){
if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
|| (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
buf[i]='\0';
- if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
+ if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
int err=errno;
if( err!=EEXIST ) {
OSTRACE(("CREATELOCKPATH FAILED creating %s, "
@@ -30187,7 +31313,6 @@ static int proxyCreateUnixFile(
int islockfile /* if non zero missing dirs will be created */
) {
int fd = -1;
- int dirfd = -1;
unixFile *pNew;
int rc = SQLITE_OK;
int openFlags = O_RDWR | O_CREAT;
@@ -30211,17 +31336,17 @@ static int proxyCreateUnixFile(
}
}
if( fd<0 ){
- fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(path, openFlags, 0);
terrno = errno;
if( fd<0 && errno==ENOENT && islockfile ){
if( proxyCreateLockPath(path) == SQLITE_OK ){
- fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(path, openFlags, 0);
}
}
}
if( fd<0 ){
openFlags = O_RDONLY;
- fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(path, openFlags, 0);
terrno = errno;
}
if( fd<0 ){
@@ -30252,7 +31377,7 @@ static int proxyCreateUnixFile(
pUnused->flags = openFlags;
pNew->pUnused = pUnused;
- rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0);
+ rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0);
if( rc==SQLITE_OK ){
*ppFile = pNew;
return SQLITE_OK;
@@ -30292,6 +31417,8 @@ static int proxyGetHostID(unsigned char *pHostID, int *pError){
return SQLITE_IOERR;
}
}
+#else
+ UNUSED_PARAMETER(pError);
#endif
#ifdef SQLITE_TEST
/* simulate multiple hosts by creating unique hostid file paths */
@@ -30343,8 +31470,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
goto end_breaklock;
}
/* write it out to the temporary break file */
- fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL),
- SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
if( fd<0 ){
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
goto end_breaklock;
@@ -30366,7 +31492,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
end_breaklock:
if( rc ){
if( fd>=0 ){
- unlink(tPath);
+ osUnlink(tPath);
robust_close(pFile, fd, __LINE__);
}
fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg);
@@ -30384,6 +31510,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
int nTries = 0;
struct timespec conchModTime;
+ memset(&conchModTime, 0, sizeof(conchModTime));
do {
rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
nTries ++;
@@ -30615,12 +31742,12 @@ static int proxyTakeConch(unixFile *pFile){
end_takeconch:
OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
if( rc==SQLITE_OK && pFile->openFlags ){
+ int fd;
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
}
pFile->h = -1;
- int fd = robust_open(pCtx->dbPath, pFile->openFlags,
- SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(pCtx->dbPath, pFile->openFlags, 0);
OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
if( fd>=0 ){
pFile->h = fd;
@@ -31189,7 +32316,7 @@ SQLITE_API int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==16 );
+ assert( ArraySize(aSyscall)==25 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
@@ -31225,49 +32352,9 @@ SQLITE_API int sqlite3_os_end(void){
**
******************************************************************************
**
-** This file contains code that is specific to windows.
+** This file contains code that is specific to Windows.
*/
-#if SQLITE_OS_WIN /* This file is used for windows only */
-
-
-/*
-** A Note About Memory Allocation:
-**
-** This driver uses malloc()/free() directly rather than going through
-** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
-** are designed for use on embedded systems where memory is scarce and
-** malloc failures happen frequently. Win32 does not typically run on
-** embedded systems, and when it does the developers normally have bigger
-** problems to worry about than running out of memory. So there is not
-** a compelling need to use the wrappers.
-**
-** But there is a good reason to not use the wrappers. If we use the
-** wrappers then we will get simulated malloc() failures within this
-** driver. And that causes all kinds of problems for our tests. We
-** could enhance SQLite to deal with simulated malloc failures within
-** the OS driver, but the code to deal with those failure would not
-** be exercised on Linux (which does not need to malloc() in the driver)
-** and so we would have difficulty writing coverage tests for that
-** code. Better to leave the code out, we think.
-**
-** The point of this discussion is as follows: When creating a new
-** OS layer for an embedded system, if you use this file as an example,
-** avoid the use of malloc()/free(). Those routines work ok on windows
-** desktops but not so well in embedded systems.
-*/
-
-#include <winbase.h>
-
-#ifdef __CYGWIN__
-# include <sys/cygwin.h>
-#endif
-
-/*
-** Macros used to determine whether or not to use threads.
-*/
-#if defined(THREADSAFE) && THREADSAFE
-# define SQLITE_W32_THREADS 1
-#endif
+#if SQLITE_OS_WIN /* This file is used for Windows only */
/*
** Include code that is common to all os_*.c files
@@ -31305,11 +32392,14 @@ SQLITE_API int sqlite3_os_end(void){
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3OSTrace = 0;
-#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
#else
-#define OSTRACE(X)
+# define OSTRACE(X)
#endif
/*
@@ -31480,24 +32570,200 @@ SQLITE_API int sqlite3_open_file_count = 0;
/************** Continuing where we left off in os_win.c *********************/
/*
-** Some microsoft compilers lack this definition.
+** Include the header file for the Windows VFS.
*/
-#ifndef INVALID_FILE_ATTRIBUTES
-# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+
+/*
+** Compiling and using WAL mode requires several APIs that are only
+** available in Windows platforms based on the NT kernel.
+*/
+#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL)
+# error "WAL mode requires support from the Windows NT kernel, compile\
+ with SQLITE_OMIT_WAL."
#endif
/*
-** Determine if we are dealing with WindowsCE - which has a much
-** reduced API.
+** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
+** based on the sub-platform)?
*/
-#if SQLITE_OS_WINCE
-# define AreFileApisANSI() 1
-# define FormatMessageW(a,b,c,d,e,f,g) 0
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
+# define SQLITE_WIN32_HAS_ANSI
#endif
-/* Forward references */
+/*
+** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
+** based on the sub-platform)?
+*/
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \
+ !defined(SQLITE_WIN32_NO_WIDE)
+# define SQLITE_WIN32_HAS_WIDE
+#endif
+
+/*
+** Make sure at least one set of Win32 APIs is available.
+*/
+#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE)
+# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\
+ must be defined."
+#endif
+
+/*
+** Define the required Windows SDK version constants if they are not
+** already available.
+*/
+#ifndef NTDDI_WIN8
+# define NTDDI_WIN8 0x06020000
+#endif
+
+#ifndef NTDDI_WINBLUE
+# define NTDDI_WINBLUE 0x06030000
+#endif
+
+/*
+** Check to see if the GetVersionEx[AW] functions are deprecated on the
+** target system. GetVersionEx was first deprecated in Win8.1.
+*/
+#ifndef SQLITE_WIN32_GETVERSIONEX
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE
+# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */
+# else
+# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */
+# endif
+#endif
+
+/*
+** This constant should already be defined (in the "WinDef.h" SDK file).
+*/
+#ifndef MAX_PATH
+# define MAX_PATH (260)
+#endif
+
+/*
+** Maximum pathname length (in chars) for Win32. This should normally be
+** MAX_PATH.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_CHARS
+# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH)
+#endif
+
+/*
+** This constant should already be defined (in the "WinNT.h" SDK file).
+*/
+#ifndef UNICODE_STRING_MAX_CHARS
+# define UNICODE_STRING_MAX_CHARS (32767)
+#endif
+
+/*
+** Maximum pathname length (in chars) for WinNT. This should normally be
+** UNICODE_STRING_MAX_CHARS.
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_CHARS
+# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in
+** characters, so we allocate 4 bytes per character assuming worst-case of
+** 4-bytes-per-character for UTF8.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_BYTES
+# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for WinNT. This should normally be
+** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR).
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_BYTES
+# define SQLITE_WINNT_MAX_PATH_BYTES \
+ (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS)
+#endif
+
+/*
+** Maximum error message length (in chars) for WinRT.
+*/
+#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS
+# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024)
+#endif
+
+/*
+** Returns non-zero if the character should be treated as a directory
+** separator.
+*/
+#ifndef winIsDirSep
+# define winIsDirSep(a) (((a) == '/') || ((a) == '\\'))
+#endif
+
+/*
+** This macro is used when a local variable is set to a value that is
+** [sometimes] not used by the code (e.g. via conditional compilation).
+*/
+#ifndef UNUSED_VARIABLE_VALUE
+# define UNUSED_VARIABLE_VALUE(x) (void)(x)
+#endif
+
+/*
+** Returns the character that should be used as the directory separator.
+*/
+#ifndef winGetDirSep
+# define winGetDirSep() '\\'
+#endif
+
+/*
+** Do we need to manually define the Win32 file mapping APIs for use with WAL
+** mode (e.g. these APIs are available in the Windows CE SDK; however, they
+** are not present in the header file)?
+*/
+#if SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL)
+/*
+** Two of the file mapping APIs are different under WinRT. Figure out which
+** set we need.
+*/
+#if SQLITE_OS_WINRT
+WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \
+ LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR);
+
+WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T);
+#else
+#if defined(SQLITE_WIN32_HAS_ANSI)
+WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \
+ DWORD, DWORD, DWORD, LPCSTR);
+#endif /* defined(SQLITE_WIN32_HAS_ANSI) */
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \
+ DWORD, DWORD, DWORD, LPCWSTR);
+#endif /* defined(SQLITE_WIN32_HAS_WIDE) */
+
+WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
+#endif /* SQLITE_OS_WINRT */
+
+/*
+** This file mapping API is common to both Win32 and WinRT.
+*/
+WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
+#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */
+
+/*
+** Some Microsoft compilers lack this definition.
+*/
+#ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+
+#ifndef FILE_FLAG_MASK
+# define FILE_FLAG_MASK (0xFF3C0000)
+#endif
+
+#ifndef FILE_ATTRIBUTE_MASK
+# define FILE_ATTRIBUTE_MASK (0x0003FFF7)
+#endif
+
+#ifndef SQLITE_OMIT_WAL
+/* Forward references to structures used for WAL */
typedef struct winShm winShm; /* A connection to shared-memory */
typedef struct winShmNode winShmNode; /* A region of shared-memory */
+#endif
/*
** WinCE lacks native support for file locking so we have to fake it
@@ -31521,47 +32787,1065 @@ struct winFile {
const sqlite3_io_methods *pMethod; /*** Must be first ***/
sqlite3_vfs *pVfs; /* The VFS used to open this file */
HANDLE h; /* Handle for accessing the file */
- unsigned char locktype; /* Type of lock currently held on this file */
+ u8 locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
+ u8 ctrlFlags; /* Flags. See WINFILE_* below */
DWORD lastErrno; /* The Windows errno from the last I/O error */
- DWORD sectorSize; /* Sector size of the device file is on */
+#ifndef SQLITE_OMIT_WAL
winShm *pShm; /* Instance of shared memory on this file */
+#endif
const char *zPath; /* Full pathname of this file */
int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
#if SQLITE_OS_WINCE
- WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
- HANDLE hMutex; /* Mutex used to control access to shared lock */
+ LPWSTR zDeleteOnClose; /* Name of file to delete when closing */
+ HANDLE hMutex; /* Mutex used to control access to shared lock */
HANDLE hShared; /* Shared memory segment used for locking */
winceLock local; /* Locks obtained by this instance of winFile */
winceLock *shared; /* Global shared lock memory for the file */
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ int nFetchOut; /* Number of outstanding xFetch references */
+ HANDLE hMap; /* Handle for accessing memory mapping */
+ void *pMapRegion; /* Area memory mapped */
+ sqlite3_int64 mmapSize; /* Usable size of mapped region */
+ sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */
+ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
+#endif
};
+/*
+** Allowed values for winFile.ctrlFlags
+*/
+#define WINFILE_RDONLY 0x02 /* Connection is read only */
+#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
+#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
+
+/*
+ * The size of the buffer used by sqlite3_win32_write_debug().
+ */
+#ifndef SQLITE_WIN32_DBG_BUF_SIZE
+# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD)))
+#endif
+
+/*
+ * The value used with sqlite3_win32_set_directory() to specify that
+ * the data directory should be changed.
+ */
+#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
+# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
+#endif
+
+/*
+ * The value used with sqlite3_win32_set_directory() to specify that
+ * the temporary directory should be changed.
+ */
+#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
+# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
+#endif
+
+/*
+ * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
+ * various Win32 API heap functions instead of our own.
+ */
+#ifdef SQLITE_WIN32_MALLOC
+
+/*
+ * If this is non-zero, an isolated heap will be created by the native Win32
+ * allocator subsystem; otherwise, the default process heap will be used. This
+ * setting has no effect when compiling for WinRT. By default, this is enabled
+ * and an isolated heap will be created to store all allocated data.
+ *
+ ******************************************************************************
+ * WARNING: It is important to note that when this setting is non-zero and the
+ * winMemShutdown function is called (e.g. by the sqlite3_shutdown
+ * function), all data that was allocated using the isolated heap will
+ * be freed immediately and any attempt to access any of that freed
+ * data will almost certainly result in an immediate access violation.
+ ******************************************************************************
+ */
+#ifndef SQLITE_WIN32_HEAP_CREATE
+# define SQLITE_WIN32_HEAP_CREATE (TRUE)
+#endif
+
+/*
+ * The initial size of the Win32-specific heap. This value may be zero.
+ */
+#ifndef SQLITE_WIN32_HEAP_INIT_SIZE
+# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \
+ (SQLITE_DEFAULT_PAGE_SIZE) + 4194304)
+#endif
+
+/*
+ * The maximum size of the Win32-specific heap. This value may be zero.
+ */
+#ifndef SQLITE_WIN32_HEAP_MAX_SIZE
+# define SQLITE_WIN32_HEAP_MAX_SIZE (0)
+#endif
/*
-** Forward prototypes.
+ * The extra flags to use in calls to the Win32 heap APIs. This value may be
+ * zero for the default behavior.
+ */
+#ifndef SQLITE_WIN32_HEAP_FLAGS
+# define SQLITE_WIN32_HEAP_FLAGS (0)
+#endif
+
+
+/*
+** The winMemData structure stores information required by the Win32-specific
+** sqlite3_mem_methods implementation.
*/
-static int getSectorSize(
- sqlite3_vfs *pVfs,
- const char *zRelative /* UTF-8 file name */
-);
+typedef struct winMemData winMemData;
+struct winMemData {
+#ifndef NDEBUG
+ u32 magic1; /* Magic number to detect structure corruption. */
+#endif
+ HANDLE hHeap; /* The handle to our heap. */
+ BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */
+#ifndef NDEBUG
+ u32 magic2; /* Magic number to detect structure corruption. */
+#endif
+};
+
+#ifndef NDEBUG
+#define WINMEM_MAGIC1 0x42b2830b
+#define WINMEM_MAGIC2 0xbd4d7cf4
+#endif
+
+static struct winMemData win_mem_data = {
+#ifndef NDEBUG
+ WINMEM_MAGIC1,
+#endif
+ NULL, FALSE
+#ifndef NDEBUG
+ ,WINMEM_MAGIC2
+#endif
+};
+
+#ifndef NDEBUG
+#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 )
+#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 )
+#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2();
+#else
+#define winMemAssertMagic()
+#endif
+
+#define winMemGetDataPtr() &win_mem_data
+#define winMemGetHeap() win_mem_data.hHeap
+#define winMemGetOwned() win_mem_data.bOwned
+
+static void *winMemMalloc(int nBytes);
+static void winMemFree(void *pPrior);
+static void *winMemRealloc(void *pPrior, int nBytes);
+static int winMemSize(void *p);
+static int winMemRoundup(int n);
+static int winMemInit(void *pAppData);
+static void winMemShutdown(void *pAppData);
+
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void);
+#endif /* SQLITE_WIN32_MALLOC */
/*
** The following variable is (normally) set once and never changes
-** thereafter. It records whether the operating system is Win95
+** thereafter. It records whether the operating system is Win9x
** or WinNT.
**
** 0: Operating system unknown.
-** 1: Operating system is Win95.
+** 1: Operating system is Win9x.
** 2: Operating system is WinNT.
**
** In order to facilitate testing on a WinNT system, the test fixture
** can manually set this value to 1 to emulate Win98 behavior.
*/
#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_os_type = 0;
+SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
+#else
+static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
+#endif
+
+#ifndef SYSCALL
+# define SYSCALL sqlite3_syscall_ptr
+#endif
+
+/*
+** This function is not available on Windows CE or WinRT.
+ */
+
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
+# define osAreFileApisANSI() 1
+#endif
+
+/*
+** Many system calls are accessed through pointer-to-functions so that
+** they may be overridden at runtime to facilitate fault injection during
+** testing and sandboxing. The following array holds the names and pointers
+** to all overrideable system calls.
+*/
+static struct win_syscall {
+ const char *zName; /* Name of the system call */
+ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
+ sqlite3_syscall_ptr pDefault; /* Default value */
+} aSyscall[] = {
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 },
+#else
+ { "AreFileApisANSI", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osAreFileApisANSI
+#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent)
+#endif
+
+#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CharLowerW", (SYSCALL)CharLowerW, 0 },
+#else
+ { "CharLowerW", (SYSCALL)0, 0 },
+#endif
+
+#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent)
+
+#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CharUpperW", (SYSCALL)CharUpperW, 0 },
+#else
+ { "CharUpperW", (SYSCALL)0, 0 },
+#endif
+
+#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent)
+
+ { "CloseHandle", (SYSCALL)CloseHandle, 0 },
+
+#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "CreateFileA", (SYSCALL)CreateFileA, 0 },
+#else
+ { "CreateFileA", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \
+ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CreateFileW", (SYSCALL)CreateFileW, 0 },
+#else
+ { "CreateFileW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
+ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
+
+#if (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
+ !defined(SQLITE_OMIT_WAL))
+ { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 },
+#else
+ { "CreateFileMappingA", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
+ DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent)
+
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ !defined(SQLITE_OMIT_WAL))
+ { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 },
+#else
+ { "CreateFileMappingW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
+ DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CreateMutexW", (SYSCALL)CreateMutexW, 0 },
+#else
+ { "CreateMutexW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \
+ LPCWSTR))aSyscall[8].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "DeleteFileA", (SYSCALL)DeleteFileA, 0 },
+#else
+ { "DeleteFileA", (SYSCALL)0, 0 },
+#endif
+
+#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "DeleteFileW", (SYSCALL)DeleteFileW, 0 },
+#else
+ { "DeleteFileW", (SYSCALL)0, 0 },
+#endif
+
+#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent)
+
+#if SQLITE_OS_WINCE
+ { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
+#else
+ { "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
+#endif
+
+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
+ LPFILETIME))aSyscall[11].pCurrent)
+
+#if SQLITE_OS_WINCE
+ { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
+#else
+ { "FileTimeToSystemTime", (SYSCALL)0, 0 },
+#endif
+
+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
+ LPSYSTEMTIME))aSyscall[12].pCurrent)
+
+ { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
+
+#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "FormatMessageA", (SYSCALL)FormatMessageA, 0 },
+#else
+ { "FormatMessageA", (SYSCALL)0, 0 },
+#endif
+
+#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \
+ DWORD,va_list*))aSyscall[14].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "FormatMessageW", (SYSCALL)FormatMessageW, 0 },
+#else
+ { "FormatMessageW", (SYSCALL)0, 0 },
+#endif
+
+#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \
+ DWORD,va_list*))aSyscall[15].pCurrent)
+
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "FreeLibrary", (SYSCALL)FreeLibrary, 0 },
+#else
+ { "FreeLibrary", (SYSCALL)0, 0 },
+#endif
+
+#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent)
+
+ { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 },
+
+#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent)
+
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 },
+#else
+ { "GetDiskFreeSpaceA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \
+ LPDWORD))aSyscall[18].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 },
+#else
+ { "GetDiskFreeSpaceW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \
+ LPDWORD))aSyscall[19].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 },
+#else
+ { "GetFileAttributesA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 },
+#else
+ { "GetFileAttributesW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 },
+#else
+ { "GetFileAttributesExW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \
+ LPVOID))aSyscall[22].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "GetFileSize", (SYSCALL)GetFileSize, 0 },
+#else
+ { "GetFileSize", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent)
+
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 },
+#else
+ { "GetFullPathNameA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \
+ LPSTR*))aSyscall[24].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 },
+#else
+ { "GetFullPathNameW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
+ LPWSTR*))aSyscall[25].pCurrent)
+
+ { "GetLastError", (SYSCALL)GetLastError, 0 },
+
+#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
+
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
+#if SQLITE_OS_WINCE
+ /* The GetProcAddressA() routine is only available on Windows CE. */
+ { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 },
+#else
+ /* All other Windows platforms expect GetProcAddress() to take
+ ** an ANSI string regardless of the _UNICODE setting */
+ { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 },
+#endif
+#else
+ { "GetProcAddressA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \
+ LPCSTR))aSyscall[27].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 },
+#else
+ { "GetSystemInfo", (SYSCALL)0, 0 },
+#endif
+
+#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent)
+
+ { "GetSystemTime", (SYSCALL)GetSystemTime, 0 },
+
+#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent)
+
+#if !SQLITE_OS_WINCE
+ { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 },
+#else
+ { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 },
+#endif
+
+#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \
+ LPFILETIME))aSyscall[30].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetTempPathA", (SYSCALL)GetTempPathA, 0 },
+#else
+ { "GetTempPathA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetTempPathW", (SYSCALL)GetTempPathW, 0 },
+#else
+ { "GetTempPathW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "GetTickCount", (SYSCALL)GetTickCount, 0 },
+#else
+ { "GetTickCount", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_GETVERSIONEX) && \
+ SQLITE_WIN32_GETVERSIONEX
+ { "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
+#else
+ { "GetVersionExA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetVersionExA ((BOOL(WINAPI*)( \
+ LPOSVERSIONINFOA))aSyscall[34].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+ { "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
+#else
+ { "GetVersionExW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetVersionExW ((BOOL(WINAPI*)( \
+ LPOSVERSIONINFOW))aSyscall[35].pCurrent)
+
+ { "HeapAlloc", (SYSCALL)HeapAlloc, 0 },
+
+#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
+ SIZE_T))aSyscall[36].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "HeapCreate", (SYSCALL)HeapCreate, 0 },
+#else
+ { "HeapCreate", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
+ SIZE_T))aSyscall[37].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "HeapDestroy", (SYSCALL)HeapDestroy, 0 },
+#else
+ { "HeapDestroy", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent)
+
+ { "HeapFree", (SYSCALL)HeapFree, 0 },
+
+#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent)
+
+ { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 },
+
+#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \
+ SIZE_T))aSyscall[40].pCurrent)
+
+ { "HeapSize", (SYSCALL)HeapSize, 0 },
+
+#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
+ LPCVOID))aSyscall[41].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "HeapValidate", (SYSCALL)HeapValidate, 0 },
+#else
+ { "HeapValidate", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
+ LPCVOID))aSyscall[42].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "HeapCompact", (SYSCALL)HeapCompact, 0 },
+#else
+ { "HeapCompact", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
+#else
+ { "LoadLibraryA", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 },
+#else
+ { "LoadLibraryW", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "LocalFree", (SYSCALL)LocalFree, 0 },
+#else
+ { "LocalFree", (SYSCALL)0, 0 },
+#endif
+
+#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "LockFile", (SYSCALL)LockFile, 0 },
+#else
+ { "LockFile", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osLockFile
+#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ DWORD))aSyscall[47].pCurrent)
+#endif
+
+#if !SQLITE_OS_WINCE
+ { "LockFileEx", (SYSCALL)LockFileEx, 0 },
+#else
+ { "LockFileEx", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osLockFileEx
+#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
+ LPOVERLAPPED))aSyscall[48].pCurrent)
+#endif
+
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL))
+ { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 },
+#else
+ { "MapViewOfFile", (SYSCALL)0, 0 },
+#endif
+
+#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ SIZE_T))aSyscall[49].pCurrent)
+
+ { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
+
+#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
+ int))aSyscall[50].pCurrent)
+
+ { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
+
+#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
+ LARGE_INTEGER*))aSyscall[51].pCurrent)
+
+ { "ReadFile", (SYSCALL)ReadFile, 0 },
+
+#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
+ LPOVERLAPPED))aSyscall[52].pCurrent)
+
+ { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
+
+#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
+#else
+ { "SetFilePointer", (SYSCALL)0, 0 },
+#endif
+
+#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
+ DWORD))aSyscall[54].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "Sleep", (SYSCALL)Sleep, 0 },
+#else
+ { "Sleep", (SYSCALL)0, 0 },
+#endif
+
+#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
+
+ { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
+
+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
+ LPFILETIME))aSyscall[56].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "UnlockFile", (SYSCALL)UnlockFile, 0 },
+#else
+ { "UnlockFile", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osUnlockFile
+#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ DWORD))aSyscall[57].pCurrent)
+#endif
+
+#if !SQLITE_OS_WINCE
+ { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 },
+#else
+ { "UnlockFileEx", (SYSCALL)0, 0 },
+#endif
+
+#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ LPOVERLAPPED))aSyscall[58].pCurrent)
+
+#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL)
+ { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
+#else
+ { "UnmapViewOfFile", (SYSCALL)0, 0 },
+#endif
+
+#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent)
+
+ { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
+
+#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
+ LPCSTR,LPBOOL))aSyscall[60].pCurrent)
+
+ { "WriteFile", (SYSCALL)WriteFile, 0 },
+
+#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
+ LPOVERLAPPED))aSyscall[61].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "CreateEventExW", (SYSCALL)CreateEventExW, 0 },
+#else
+ { "CreateEventExW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
+ DWORD,DWORD))aSyscall[62].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
+#else
+ { "WaitForSingleObject", (SYSCALL)0, 0 },
+#endif
+
+#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
+ DWORD))aSyscall[63].pCurrent)
+
+#if !SQLITE_OS_WINCE
+ { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
+#else
+ { "WaitForSingleObjectEx", (SYSCALL)0, 0 },
+#endif
+
+#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
+ BOOL))aSyscall[64].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 },
+#else
+ { "SetFilePointerEx", (SYSCALL)0, 0 },
+#endif
+
+#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
+ PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
+#else
+ { "GetFileInformationByHandleEx", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
+ FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent)
+
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
+ { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
+#else
+ { "MapViewOfFileFromApp", (SYSCALL)0, 0 },
+#endif
+
+#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
+ SIZE_T))aSyscall[67].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "CreateFile2", (SYSCALL)CreateFile2, 0 },
+#else
+ { "CreateFile2", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
+ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent)
+
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
+#else
+ { "LoadPackagedLibrary", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
+ DWORD))aSyscall[69].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
+#else
+ { "GetTickCount64", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
+#else
+ { "GetNativeSystemInfo", (SYSCALL)0, 0 },
+#endif
+
+#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
+ LPSYSTEM_INFO))aSyscall[71].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
+#else
+ { "OutputDebugStringA", (SYSCALL)0, 0 },
+#endif
+
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
+#else
+ { "OutputDebugStringW", (SYSCALL)0, 0 },
+#endif
+
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent)
+
+ { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
+
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent)
+
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
+ { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
+#else
+ { "CreateFileMappingFromApp", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
+ LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent)
+
+/*
+** NOTE: On some sub-platforms, the InterlockedCompareExchange "function"
+** is really just a macro that uses a compiler intrinsic (e.g. x64).
+** So do not try to make this is into a redefinable interface.
+*/
+#if defined(InterlockedCompareExchange)
+ { "InterlockedCompareExchange", (SYSCALL)0, 0 },
+
+#define osInterlockedCompareExchange InterlockedCompareExchange
+#else
+ { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 },
+
+#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \
+ SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent)
+#endif /* defined(InterlockedCompareExchange) */
+
+}; /* End of the overrideable system calls */
+
+/*
+** This is the xSetSystemCall() method of sqlite3_vfs for all of the
+** "win32" VFSes. Return SQLITE_OK opon successfully updating the
+** system call pointer, or SQLITE_NOTFOUND if there is no configurable
+** system call named zName.
+*/
+static int winSetSystemCall(
+ sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */
+ const char *zName, /* Name of system call to override */
+ sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */
+){
+ unsigned int i;
+ int rc = SQLITE_NOTFOUND;
+
+ UNUSED_PARAMETER(pNotUsed);
+ if( zName==0 ){
+ /* If no zName is given, restore all system calls to their default
+ ** settings and return NULL
+ */
+ rc = SQLITE_OK;
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
+ if( aSyscall[i].pDefault ){
+ aSyscall[i].pCurrent = aSyscall[i].pDefault;
+ }
+ }
+ }else{
+ /* If zName is specified, operate on only the one system call
+ ** specified.
+ */
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ){
+ if( aSyscall[i].pDefault==0 ){
+ aSyscall[i].pDefault = aSyscall[i].pCurrent;
+ }
+ rc = SQLITE_OK;
+ if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
+ aSyscall[i].pCurrent = pNewFunc;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Return the value of a system call. Return NULL if zName is not a
+** recognized system call name. NULL is also returned if the system call
+** is currently undefined.
+*/
+static sqlite3_syscall_ptr winGetSystemCall(
+ sqlite3_vfs *pNotUsed,
+ const char *zName
+){
+ unsigned int i;
+
+ UNUSED_PARAMETER(pNotUsed);
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent;
+ }
+ return 0;
+}
+
+/*
+** Return the name of the first system call after zName. If zName==NULL
+** then return the name of the first system call. Return NULL if zName
+** is the last system call or if zName is not the name of a valid
+** system call.
+*/
+static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){
+ int i = -1;
+
+ UNUSED_PARAMETER(p);
+ if( zName ){
+ for(i=0; i<ArraySize(aSyscall)-1; i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ) break;
+ }
+ }
+ for(i++; i<ArraySize(aSyscall); i++){
+ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
+ }
+ return 0;
+}
+
+#ifdef SQLITE_WIN32_MALLOC
+/*
+** If a Win32 native heap has been configured, this function will attempt to
+** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one
+** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The
+** "pnLargest" argument, if non-zero, will be used to return the size of the
+** largest committed free block in the heap, in bytes.
+*/
+SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){
+ int rc = SQLITE_OK;
+ UINT nLargest = 0;
+ HANDLE hHeap;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){
+ DWORD lastErrno = osGetLastError();
+ if( lastErrno==NO_ERROR ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p",
+ (void*)hHeap);
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p",
+ osGetLastError(), (void*)hHeap);
+ rc = SQLITE_ERROR;
+ }
+ }
+#else
+ sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p",
+ (void*)hHeap);
+ rc = SQLITE_NOTFOUND;
+#endif
+ if( pnLargest ) *pnLargest = nLargest;
+ return rc;
+}
+
+/*
+** If a Win32 native heap has been configured, this function will attempt to
+** destroy and recreate it. If the Win32 native heap is not isolated and/or
+** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will
+** be returned and no changes will be made to the Win32 native heap.
+*/
+SQLITE_API int sqlite3_win32_reset_heap(){
+ int rc;
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
+ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
+ sqlite3_mutex_enter(pMaster);
+ sqlite3_mutex_enter(pMem);
+ winMemAssertMagic();
+ if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){
+ /*
+ ** At this point, there should be no outstanding memory allocations on
+ ** the heap. Also, since both the master and memsys locks are currently
+ ** being held by us, no other function (i.e. from another thread) should
+ ** be able to even access the heap. Attempt to destroy and recreate our
+ ** isolated Win32 native heap now.
+ */
+ assert( winMemGetHeap()!=NULL );
+ assert( winMemGetOwned() );
+ assert( sqlite3_memory_used()==0 );
+ winMemShutdown(winMemGetDataPtr());
+ assert( winMemGetHeap()==NULL );
+ assert( !winMemGetOwned() );
+ assert( sqlite3_memory_used()==0 );
+ rc = winMemInit(winMemGetDataPtr());
+ assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL );
+ assert( rc!=SQLITE_OK || winMemGetOwned() );
+ assert( rc!=SQLITE_OK || sqlite3_memory_used()==0 );
+ }else{
+ /*
+ ** The Win32 native heap cannot be modified because it may be in use.
+ */
+ rc = SQLITE_BUSY;
+ }
+ sqlite3_mutex_leave(pMem);
+ sqlite3_mutex_leave(pMaster);
+ return rc;
+}
+#endif /* SQLITE_WIN32_MALLOC */
+
+/*
+** This function outputs the specified (ANSI) string to the Win32 debugger
+** (if available).
+*/
+
+SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){
+ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE];
+ int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */
+ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */
+ assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE );
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ if( nMin>0 ){
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ memcpy(zDbgBuf, zBuf, nMin);
+ osOutputDebugStringA(zDbgBuf);
+ }else{
+ osOutputDebugStringA(zBuf);
+ }
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ if ( osMultiByteToWideChar(
+ osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf,
+ nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){
+ return;
+ }
+ osOutputDebugStringW((LPCWSTR)zDbgBuf);
#else
-static int sqlite3_os_type = 0;
+ if( nMin>0 ){
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ memcpy(zDbgBuf, zBuf, nMin);
+ fprintf(stderr, "%s", zDbgBuf);
+ }else{
+ fprintf(stderr, "%s", zBuf);
+ }
+#endif
+}
+
+/*
+** The following routine suspends the current thread for at least ms
+** milliseconds. This is equivalent to the Win32 Sleep() interface.
+*/
+#if SQLITE_OS_WINRT
+static HANDLE sleepObj = NULL;
+#endif
+
+SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
+#if SQLITE_OS_WINRT
+ if ( sleepObj==NULL ){
+ sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET,
+ SYNCHRONIZE);
+ }
+ assert( sleepObj!=NULL );
+ osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
+#else
+ osSleep(milliseconds);
+#endif
+}
+
+#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
+ SQLITE_THREADSAFE>0
+SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
+ DWORD rc;
+ while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
+ TRUE))==WAIT_IO_COMPLETION ){}
+ return rc;
+}
#endif
/*
@@ -31575,110 +33859,366 @@ static int sqlite3_os_type = 0;
** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API.
*/
-#if SQLITE_OS_WINCE
-# define isNT() (1)
+
+#if !defined(SQLITE_WIN32_GETVERSIONEX) || !SQLITE_WIN32_GETVERSIONEX
+# define osIsNT() (1)
+#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
+# define osIsNT() (1)
+#elif !defined(SQLITE_WIN32_HAS_WIDE)
+# define osIsNT() (0)
#else
- static int isNT(void){
- if( sqlite3_os_type==0 ){
- OSVERSIONINFO sInfo;
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
- GetVersionEx(&sInfo);
- sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
+# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt())
+#endif
+
+/*
+** This function determines if the machine is running a version of Windows
+** based on the NT kernel.
+*/
+SQLITE_API int sqlite3_win32_is_nt(void){
+#if SQLITE_OS_WINRT
+ /*
+ ** NOTE: The WinRT sub-platform is always assumed to be based on the NT
+ ** kernel.
+ */
+ return 1;
+#elif defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+ if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ OSVERSIONINFOA sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExA(&sInfo);
+ osInterlockedCompareExchange(&sqlite3_os_type,
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ OSVERSIONINFOW sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExW(&sInfo);
+ osInterlockedCompareExchange(&sqlite3_os_type,
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
+#endif
+ }
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+#elif SQLITE_TEST
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+#else
+ /*
+ ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
+ ** deprecated are always assumed to be based on the NT kernel.
+ */
+ return 1;
+#endif
+}
+
+#ifdef SQLITE_WIN32_MALLOC
+/*
+** Allocate nBytes of memory.
+*/
+static void *winMemMalloc(int nBytes){
+ HANDLE hHeap;
+ void *p;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ assert( nBytes>=0 );
+ p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
+ if( !p ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p",
+ nBytes, osGetLastError(), (void*)hHeap);
+ }
+ return p;
+}
+
+/*
+** Free memory.
+*/
+static void winMemFree(void *pPrior){
+ HANDLE hHeap;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+#endif
+ if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
+ if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p",
+ pPrior, osGetLastError(), (void*)hHeap);
+ }
+}
+
+/*
+** Change the size of an existing memory allocation
+*/
+static void *winMemRealloc(void *pPrior, int nBytes){
+ HANDLE hHeap;
+ void *p;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+#endif
+ assert( nBytes>=0 );
+ if( !pPrior ){
+ p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
+ }else{
+ p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
+ }
+ if( !p ){
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p",
+ pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(),
+ (void*)hHeap);
+ }
+ return p;
+}
+
+/*
+** Return the size of an outstanding allocation, in bytes.
+*/
+static int winMemSize(void *p){
+ HANDLE hHeap;
+ SIZE_T n;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) );
+#endif
+ if( !p ) return 0;
+ n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
+ if( n==(SIZE_T)-1 ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p",
+ p, osGetLastError(), (void*)hHeap);
+ return 0;
+ }
+ return (int)n;
+}
+
+/*
+** Round up a request size to the next valid allocation size.
+*/
+static int winMemRoundup(int n){
+ return n;
+}
+
+/*
+** Initialize this module.
+*/
+static int winMemInit(void *pAppData){
+ winMemData *pWinMemData = (winMemData *)pAppData;
+
+ if( !pWinMemData ) return SQLITE_ERROR;
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 );
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 );
+
+#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
+ if( !pWinMemData->hHeap ){
+ DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE;
+ DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap;
+ if( dwMaximumSize==0 ){
+ dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE;
+ }else if( dwInitialSize>dwMaximumSize ){
+ dwInitialSize = dwMaximumSize;
+ }
+ pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS,
+ dwInitialSize, dwMaximumSize);
+ if( !pWinMemData->hHeap ){
+ sqlite3_log(SQLITE_NOMEM,
+ "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu",
+ osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize,
+ dwMaximumSize);
+ return SQLITE_NOMEM;
}
- return sqlite3_os_type==2;
+ pWinMemData->bOwned = TRUE;
+ assert( pWinMemData->bOwned );
}
-#endif /* SQLITE_OS_WINCE */
+#else
+ pWinMemData->hHeap = osGetProcessHeap();
+ if( !pWinMemData->hHeap ){
+ sqlite3_log(SQLITE_NOMEM,
+ "failed to GetProcessHeap (%lu)", osGetLastError());
+ return SQLITE_NOMEM;
+ }
+ pWinMemData->bOwned = FALSE;
+ assert( !pWinMemData->bOwned );
+#endif
+ assert( pWinMemData->hHeap!=0 );
+ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** Deinitialize this module.
+*/
+static void winMemShutdown(void *pAppData){
+ winMemData *pWinMemData = (winMemData *)pAppData;
+
+ if( !pWinMemData ) return;
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 );
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 );
+
+ if( pWinMemData->hHeap ){
+ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+ if( pWinMemData->bOwned ){
+ if( !osHeapDestroy(pWinMemData->hHeap) ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p",
+ osGetLastError(), (void*)pWinMemData->hHeap);
+ }
+ pWinMemData->bOwned = FALSE;
+ }
+ pWinMemData->hHeap = NULL;
+ }
+}
/*
-** Convert a UTF-8 string to microsoft unicode (UTF-16?).
+** Populate the low-level memory allocation function pointers in
+** sqlite3GlobalConfig.m with pointers to the routines in this file. The
+** arguments specify the block of memory to manage.
+**
+** This routine is only called by sqlite3_config(), and therefore
+** is not required to be threadsafe (it is not).
+*/
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void){
+ static const sqlite3_mem_methods winMemMethods = {
+ winMemMalloc,
+ winMemFree,
+ winMemRealloc,
+ winMemSize,
+ winMemRoundup,
+ winMemInit,
+ winMemShutdown,
+ &win_mem_data
+ };
+ return &winMemMethods;
+}
+
+SQLITE_PRIVATE void sqlite3MemSetDefault(void){
+ sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
+}
+#endif /* SQLITE_WIN32_MALLOC */
+
+/*
+** Convert a UTF-8 string to Microsoft Unicode (UTF-16?).
**
** Space to hold the returned string is obtained from malloc.
*/
-static WCHAR *utf8ToUnicode(const char *zFilename){
+static LPWSTR winUtf8ToUnicode(const char *zFilename){
int nChar;
- WCHAR *zWideFilename;
+ LPWSTR zWideFilename;
- nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
- zWideFilename = malloc( nChar*sizeof(zWideFilename[0]) );
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
+ zWideFilename = sqlite3MallocZero( nChar*sizeof(zWideFilename[0]) );
if( zWideFilename==0 ){
return 0;
}
- nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename,
+ nChar);
if( nChar==0 ){
- free(zWideFilename);
+ sqlite3_free(zWideFilename);
zWideFilename = 0;
}
return zWideFilename;
}
/*
-** Convert microsoft unicode to UTF-8. Space to hold the returned string is
-** obtained from malloc().
+** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is
+** obtained from sqlite3_malloc().
*/
-static char *unicodeToUtf8(const WCHAR *zWideFilename){
+static char *winUnicodeToUtf8(LPCWSTR zWideFilename){
int nByte;
char *zFilename;
- nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
- zFilename = malloc( nByte );
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
+ zFilename = sqlite3MallocZero( nByte );
if( zFilename==0 ){
return 0;
}
- nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
- 0, 0);
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
+ 0, 0);
if( nByte == 0 ){
- free(zFilename);
+ sqlite3_free(zFilename);
zFilename = 0;
}
return zFilename;
}
/*
-** Convert an ansi string to microsoft unicode, based on the
+** Convert an ANSI string to Microsoft Unicode, based on the
** current codepage settings for file apis.
-**
+**
** Space to hold the returned string is obtained
-** from malloc.
+** from sqlite3_malloc.
*/
-static WCHAR *mbcsToUnicode(const char *zFilename){
+static LPWSTR winMbcsToUnicode(const char *zFilename){
int nByte;
- WCHAR *zMbcsFilename;
- int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ LPWSTR zMbcsFilename;
+ int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
- nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR);
- zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) );
+ nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL,
+ 0)*sizeof(WCHAR);
+ if( nByte==0 ){
+ return 0;
+ }
+ zMbcsFilename = sqlite3MallocZero( nByte*sizeof(zMbcsFilename[0]) );
if( zMbcsFilename==0 ){
return 0;
}
- nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
+ nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename,
+ nByte);
if( nByte==0 ){
- free(zMbcsFilename);
+ sqlite3_free(zMbcsFilename);
zMbcsFilename = 0;
}
return zMbcsFilename;
}
/*
-** Convert microsoft unicode to multibyte character string, based on the
-** user's Ansi codepage.
+** Convert Microsoft Unicode to multi-byte character string, based on the
+** user's ANSI codepage.
**
** Space to hold the returned string is obtained from
-** malloc().
+** sqlite3_malloc().
*/
-static char *unicodeToMbcs(const WCHAR *zWideFilename){
+static char *winUnicodeToMbcs(LPCWSTR zWideFilename){
int nByte;
char *zFilename;
- int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
- nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
- zFilename = malloc( nByte );
+ nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
+ zFilename = sqlite3MallocZero( nByte );
if( zFilename==0 ){
return 0;
}
- nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte,
- 0, 0);
+ nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename,
+ nByte, 0, 0);
if( nByte == 0 ){
- free(zFilename);
+ sqlite3_free(zFilename);
zFilename = 0;
}
return zFilename;
@@ -31686,97 +34226,151 @@ static char *unicodeToMbcs(const WCHAR *zWideFilename){
/*
** Convert multibyte character string to UTF-8. Space to hold the
-** returned string is obtained from malloc().
+** returned string is obtained from sqlite3_malloc().
*/
SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
char *zFilenameUtf8;
- WCHAR *zTmpWide;
+ LPWSTR zTmpWide;
- zTmpWide = mbcsToUnicode(zFilename);
+ zTmpWide = winMbcsToUnicode(zFilename);
if( zTmpWide==0 ){
return 0;
}
- zFilenameUtf8 = unicodeToUtf8(zTmpWide);
- free(zTmpWide);
+ zFilenameUtf8 = winUnicodeToUtf8(zTmpWide);
+ sqlite3_free(zTmpWide);
return zFilenameUtf8;
}
/*
-** Convert UTF-8 to multibyte character string. Space to hold the
-** returned string is obtained from malloc().
+** Convert UTF-8 to multibyte character string. Space to hold the
+** returned string is obtained from sqlite3_malloc().
*/
SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
char *zFilenameMbcs;
- WCHAR *zTmpWide;
+ LPWSTR zTmpWide;
- zTmpWide = utf8ToUnicode(zFilename);
+ zTmpWide = winUtf8ToUnicode(zFilename);
if( zTmpWide==0 ){
return 0;
}
- zFilenameMbcs = unicodeToMbcs(zTmpWide);
- free(zTmpWide);
+ zFilenameMbcs = winUnicodeToMbcs(zTmpWide);
+ sqlite3_free(zTmpWide);
return zFilenameMbcs;
}
+/*
+** This function sets the data directory or the temporary directory based on
+** the provided arguments. The type argument must be 1 in order to set the
+** data directory or 2 in order to set the temporary directory. The zValue
+** argument is the name of the directory to use. The return value will be
+** SQLITE_OK if successful.
+*/
+SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
+ char **ppDirectory = 0;
+#ifndef SQLITE_OMIT_AUTOINIT
+ int rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+ if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
+ ppDirectory = &sqlite3_data_directory;
+ }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
+ ppDirectory = &sqlite3_temp_directory;
+ }
+ assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE
+ || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE
+ );
+ assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
+ if( ppDirectory ){
+ char *zValueUtf8 = 0;
+ if( zValue && zValue[0] ){
+ zValueUtf8 = winUnicodeToUtf8(zValue);
+ if ( zValueUtf8==0 ){
+ return SQLITE_NOMEM;
+ }
+ }
+ sqlite3_free(*ppDirectory);
+ *ppDirectory = zValueUtf8;
+ return SQLITE_OK;
+ }
+ return SQLITE_ERROR;
+}
/*
-** The return value of getLastErrorMsg
+** The return value of winGetLastErrorMsg
** is zero if the error message fits in the buffer, or non-zero
** otherwise (if the message was truncated).
*/
-static int getLastErrorMsg(int nBuf, char *zBuf){
+static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
/* FormatMessage returns 0 on failure. Otherwise it
** returns the number of TCHARs written to the output
** buffer, excluding the terminating null char.
*/
- DWORD error = GetLastError();
DWORD dwLen = 0;
char *zOut = 0;
- if( isNT() ){
- WCHAR *zTempWide = NULL;
- dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPWSTR) &zTempWide,
- 0,
- 0);
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1];
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ zTempWide,
+ SQLITE_WIN32_MAX_ERRMSG_CHARS,
+ 0);
+#else
+ LPWSTR zTempWide = NULL;
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ (LPWSTR) &zTempWide,
+ 0,
+ 0);
+#endif
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
- zOut = unicodeToUtf8(zTempWide);
+ sqlite3BeginBenignMalloc();
+ zOut = winUnicodeToUtf8(zTempWide);
+ sqlite3EndBenignMalloc();
+#if !SQLITE_OS_WINRT
/* free the system buffer allocated by FormatMessage */
- LocalFree(zTempWide);
+ osLocalFree(zTempWide);
+#endif
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zTemp = NULL;
- dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPSTR) &zTemp,
- 0,
- 0);
+ dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ (LPSTR) &zTemp,
+ 0,
+ 0);
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
+ sqlite3BeginBenignMalloc();
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
+ sqlite3EndBenignMalloc();
/* free the system buffer allocated by FormatMessage */
- LocalFree(zTemp);
+ osLocalFree(zTemp);
}
-#endif
}
+#endif
if( 0 == dwLen ){
- sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno);
}else{
/* copy a maximum of nBuf chars to output buffer */
sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
/* free the UTF8 buffer */
- free(zOut);
+ sqlite3_free(zOut);
}
return 0;
}
@@ -31788,47 +34382,133 @@ static int getLastErrorMsg(int nBuf, char *zBuf){
**
** This routine is invoked after an error occurs in an OS function.
** It logs a message using sqlite3_log() containing the current value of
-** error code and, if possible, the human-readable equivalent from
+** error code and, if possible, the human-readable equivalent from
** FormatMessage.
**
** The first argument passed to the macro should be the error code that
-** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
** The two subsequent arguments should be the name of the OS function that
-** failed and the the associated file-system path, if any.
+** failed and the associated file-system path, if any.
*/
-#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__)
+#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__)
static int winLogErrorAtLine(
int errcode, /* SQLite error code */
+ DWORD lastErrno, /* Win32 last error */
const char *zFunc, /* Name of OS function that failed */
const char *zPath, /* File path associated with error */
int iLine /* Source line number where error occurred */
){
char zMsg[500]; /* Human readable error text */
int i; /* Loop counter */
- DWORD iErrno = GetLastError(); /* Error code */
zMsg[0] = 0;
- getLastErrorMsg(sizeof(zMsg), zMsg);
+ winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
assert( errcode!=SQLITE_OK );
if( zPath==0 ) zPath = "";
for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
zMsg[i] = 0;
sqlite3_log(errcode,
- "os_win.c:%d: (%d) %s(%s) - %s",
- iLine, iErrno, zFunc, zPath, zMsg
+ "os_win.c:%d: (%lu) %s(%s) - %s",
+ iLine, lastErrno, zFunc, zPath, zMsg
);
return errcode;
}
+/*
+** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
+** will be retried following a locking error - probably caused by
+** antivirus software. Also the initial delay before the first retry.
+** The delay increases linearly with each retry.
+*/
+#ifndef SQLITE_WIN32_IOERR_RETRY
+# define SQLITE_WIN32_IOERR_RETRY 10
+#endif
+#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
+# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
+#endif
+static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
+static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+
+/*
+** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
+** error code obtained via GetLastError() is eligible to be retried. It
+** must accept the error code DWORD as its only argument and should return
+** non-zero if the error code is transient in nature and the operation
+** responsible for generating the original error might succeed upon being
+** retried. The argument to this macro should be a variable.
+**
+** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it
+** is defined, it will be consulted only when the macro "winIoerrCanRetry1"
+** returns zero. The "winIoerrCanRetry2" macro is completely optional and
+** may be used to include additional error codes in the set that should
+** result in the failing I/O operation being retried by the caller. If
+** defined, the "winIoerrCanRetry2" macro must exhibit external semantics
+** identical to those of the "winIoerrCanRetry1" macro.
+*/
+#if !defined(winIoerrCanRetry1)
+#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \
+ ((a)==ERROR_SHARING_VIOLATION) || \
+ ((a)==ERROR_LOCK_VIOLATION) || \
+ ((a)==ERROR_DEV_NOT_EXIST) || \
+ ((a)==ERROR_NETNAME_DELETED) || \
+ ((a)==ERROR_SEM_TIMEOUT) || \
+ ((a)==ERROR_NETWORK_UNREACHABLE))
+#endif
+
+/*
+** If a ReadFile() or WriteFile() error occurs, invoke this routine
+** to see if it should be retried. Return TRUE to retry. Return FALSE
+** to give up with an error.
+*/
+static int winRetryIoerr(int *pnRetry, DWORD *pError){
+ DWORD e = osGetLastError();
+ if( *pnRetry>=winIoerrRetry ){
+ if( pError ){
+ *pError = e;
+ }
+ return 0;
+ }
+ if( winIoerrCanRetry1(e) ){
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
+ ++*pnRetry;
+ return 1;
+ }
+#if defined(winIoerrCanRetry2)
+ else if( winIoerrCanRetry2(e) ){
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
+ ++*pnRetry;
+ return 1;
+ }
+#endif
+ if( pError ){
+ *pError = e;
+ }
+ return 0;
+}
+
+/*
+** Log a I/O error retry episode.
+*/
+static void winLogIoerr(int nRetry){
+ if( nRetry ){
+ sqlite3_log(SQLITE_IOERR,
+ "delayed %dms for lock/sharing conflict",
+ winIoerrRetryDelay*nRetry*(nRetry+1)/2
+ );
+ }
+}
+
#if SQLITE_OS_WINCE
/*************************************************************************
** This section contains code for WinCE only.
*/
+#if !defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API
/*
-** WindowsCE does not have a localtime() function. So create a
-** substitute.
+** The MSVC CRT on Windows CE may not have a localtime() function. So
+** create a substitute.
*/
+/* #include <time.h> */
struct tm *__cdecl localtime(const time_t *t)
{
static struct tm y;
@@ -31839,8 +34519,8 @@ struct tm *__cdecl localtime(const time_t *t)
t64 = (t64 + 11644473600)*10000000;
uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
uTm.dwHighDateTime= (DWORD)(t64 >> 32);
- FileTimeToLocalFileTime(&uTm,&lTm);
- FileTimeToSystemTime(&lTm,&pTm);
+ osFileTimeToLocalFileTime(&uTm,&lTm);
+ osFileTimeToSystemTime(&lTm,&pTm);
y.tm_year = pTm.wYear - 1900;
y.tm_mon = pTm.wMonth - 1;
y.tm_wday = pTm.wDayOfWeek;
@@ -31850,13 +34530,7 @@ struct tm *__cdecl localtime(const time_t *t)
y.tm_sec = pTm.wSecond;
return &y;
}
-
-/* This will never be called, but defined to make the code compile */
-#define GetTempPathA(a,b)
-
-#define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e)
-#define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e)
-#define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f)
+#endif
#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)]
@@ -31866,7 +34540,7 @@ struct tm *__cdecl localtime(const time_t *t)
static void winceMutexAcquire(HANDLE h){
DWORD dwErr;
do {
- dwErr = WaitForSingleObject(h, INFINITE);
+ dwErr = osWaitForSingleObject(h, INFINITE);
} while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED);
}
/*
@@ -31878,78 +34552,95 @@ static void winceMutexAcquire(HANDLE h){
** Create the mutex and shared memory used for locking in the file
** descriptor pFile
*/
-static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
- WCHAR *zTok;
- WCHAR *zName = utf8ToUnicode(zFilename);
+static int winceCreateLock(const char *zFilename, winFile *pFile){
+ LPWSTR zTok;
+ LPWSTR zName;
+ DWORD lastErrno;
+ BOOL bLogged = FALSE;
BOOL bInit = TRUE;
+ zName = winUtf8ToUnicode(zFilename);
+ if( zName==0 ){
+ /* out of memory */
+ return SQLITE_IOERR_NOMEM;
+ }
+
/* Initialize the local lockdata */
- ZeroMemory(&pFile->local, sizeof(pFile->local));
+ memset(&pFile->local, 0, sizeof(pFile->local));
/* Replace the backslashes from the filename and lowercase it
** to derive a mutex name. */
- zTok = CharLowerW(zName);
+ zTok = osCharLowerW(zName);
for (;*zTok;zTok++){
if (*zTok == '\\') *zTok = '_';
}
/* Create/open the named mutex */
- pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
+ pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
if (!pFile->hMutex){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename);
- free(zName);
- return FALSE;
+ pFile->lastErrno = osGetLastError();
+ sqlite3_free(zName);
+ return winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock1", zFilename);
}
/* Acquire the mutex before continuing */
winceMutexAcquire(pFile->hMutex);
-
- /* Since the names of named mutexes, semaphores, file mappings etc are
+
+ /* Since the names of named mutexes, semaphores, file mappings etc are
** case-sensitive, take advantage of that by uppercasing the mutex name
** and using that as the shared filemapping name.
*/
- CharUpperW(zName);
- pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
- PAGE_READWRITE, 0, sizeof(winceLock),
- zName);
+ osCharUpperW(zName);
+ pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
+ PAGE_READWRITE, 0, sizeof(winceLock),
+ zName);
- /* Set a flag that indicates we're the first to create the memory so it
+ /* Set a flag that indicates we're the first to create the memory so it
** must be zero-initialized */
- if (GetLastError() == ERROR_ALREADY_EXISTS){
+ lastErrno = osGetLastError();
+ if (lastErrno == ERROR_ALREADY_EXISTS){
bInit = FALSE;
}
- free(zName);
+ sqlite3_free(zName);
/* If we succeeded in making the shared memory handle, map it. */
- if (pFile->hShared){
- pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared,
+ if( pFile->hShared ){
+ pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared,
FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock));
/* If mapping failed, close the shared memory handle and erase it */
- if (!pFile->shared){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename);
- CloseHandle(pFile->hShared);
+ if( !pFile->shared ){
+ pFile->lastErrno = osGetLastError();
+ winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock2", zFilename);
+ bLogged = TRUE;
+ osCloseHandle(pFile->hShared);
pFile->hShared = NULL;
}
}
/* If shared memory could not be created, then close the mutex and fail */
- if (pFile->hShared == NULL){
+ if( pFile->hShared==NULL ){
+ if( !bLogged ){
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock3", zFilename);
+ bLogged = TRUE;
+ }
winceMutexRelease(pFile->hMutex);
- CloseHandle(pFile->hMutex);
+ osCloseHandle(pFile->hMutex);
pFile->hMutex = NULL;
- return FALSE;
+ return SQLITE_IOERR;
}
-
+
/* Initialize the shared memory if we're supposed to */
- if (bInit) {
- ZeroMemory(pFile->shared, sizeof(winceLock));
+ if( bInit ){
+ memset(pFile->shared, 0, sizeof(winceLock));
}
winceMutexRelease(pFile->hMutex);
- return TRUE;
+ return SQLITE_OK;
}
/*
@@ -31976,21 +34667,21 @@ static void winceDestroyLock(winFile *pFile){
}
/* De-reference and close our copy of the shared memory handle */
- UnmapViewOfFile(pFile->shared);
- CloseHandle(pFile->hShared);
+ osUnmapViewOfFile(pFile->shared);
+ osCloseHandle(pFile->hShared);
/* Done with the mutex */
- winceMutexRelease(pFile->hMutex);
- CloseHandle(pFile->hMutex);
+ winceMutexRelease(pFile->hMutex);
+ osCloseHandle(pFile->hMutex);
pFile->hMutex = NULL;
}
}
-/*
-** An implementation of the LockFile() API of windows for wince
+/*
+** An implementation of the LockFile() API of Windows for CE
*/
static BOOL winceLockFile(
- HANDLE *phFile,
+ LPHANDLE phFile,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToLockLow,
@@ -32028,7 +34719,8 @@ static BOOL winceLockFile(
}
/* Want a pending lock? */
- else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)PENDING_BYTE
+ && nNumberOfBytesToLockLow == 1){
/* If no pending lock has been acquired, then acquire it */
if (pFile->shared->bPending == 0) {
pFile->shared->bPending = TRUE;
@@ -32038,7 +34730,8 @@ static BOOL winceLockFile(
}
/* Want a reserved lock? */
- else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE
+ && nNumberOfBytesToLockLow == 1){
if (pFile->shared->bReserved == 0) {
pFile->shared->bReserved = TRUE;
pFile->local.bReserved = TRUE;
@@ -32051,10 +34744,10 @@ static BOOL winceLockFile(
}
/*
-** An implementation of the UnlockFile API of windows for wince
+** An implementation of the UnlockFile API of Windows for CE
*/
static BOOL winceUnlockFile(
- HANDLE *phFile,
+ LPHANDLE phFile,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToUnlockLow,
@@ -32081,7 +34774,8 @@ static BOOL winceUnlockFile(
/* Did we just have a reader lock? */
else if (pFile->local.nReaders){
- assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1);
+ assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE
+ || nNumberOfBytesToUnlockLow == 1);
pFile->local.nReaders --;
if (pFile->local.nReaders == 0)
{
@@ -32092,7 +34786,8 @@ static BOOL winceUnlockFile(
}
/* Releasing a pending lock */
- else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)PENDING_BYTE
+ && nNumberOfBytesToUnlockLow == 1){
if (pFile->local.bPending){
pFile->local.bPending = FALSE;
pFile->shared->bPending = FALSE;
@@ -32100,7 +34795,8 @@ static BOOL winceUnlockFile(
}
}
/* Releasing a reserved lock */
- else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE
+ && nNumberOfBytesToUnlockLow == 1){
if (pFile->local.bReserved) {
pFile->local.bReserved = FALSE;
pFile->shared->bReserved = FALSE;
@@ -32111,34 +34807,73 @@ static BOOL winceUnlockFile(
winceMutexRelease(pFile->hMutex);
return bReturn;
}
+/*
+** End of the special code for wince
+*****************************************************************************/
+#endif /* SQLITE_OS_WINCE */
/*
-** An implementation of the LockFileEx() API of windows for wince
+** Lock a file region.
*/
-static BOOL winceLockFileEx(
- HANDLE *phFile,
- DWORD dwFlags,
- DWORD dwReserved,
- DWORD nNumberOfBytesToLockLow,
- DWORD nNumberOfBytesToLockHigh,
- LPOVERLAPPED lpOverlapped
+static BOOL winLockFile(
+ LPHANDLE phFile,
+ DWORD flags,
+ DWORD offsetLow,
+ DWORD offsetHigh,
+ DWORD numBytesLow,
+ DWORD numBytesHigh
){
- UNUSED_PARAMETER(dwReserved);
- UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
-
- /* If the caller wants a shared read lock, forward this call
- ** to winceLockFile */
- if (lpOverlapped->Offset == (DWORD)SHARED_FIRST &&
- dwFlags == 1 &&
- nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){
- return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0);
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API LockFile.
+ */
+ return winceLockFile(phFile, offsetLow, offsetHigh,
+ numBytesLow, numBytesHigh);
+#else
+ if( osIsNT() ){
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offsetLow;
+ ovlp.OffsetHigh = offsetHigh;
+ return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
+ }else{
+ return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
+ numBytesHigh);
}
- return FALSE;
+#endif
}
+
/*
-** End of the special code for wince
-*****************************************************************************/
-#endif /* SQLITE_OS_WINCE */
+** Unlock a file region.
+ */
+static BOOL winUnlockFile(
+ LPHANDLE phFile,
+ DWORD offsetLow,
+ DWORD offsetHigh,
+ DWORD numBytesLow,
+ DWORD numBytesHigh
+){
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API UnlockFile.
+ */
+ return winceUnlockFile(phFile, offsetLow, offsetHigh,
+ numBytesLow, numBytesHigh);
+#else
+ if( osIsNT() ){
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offsetLow;
+ ovlp.OffsetHigh = offsetHigh;
+ return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
+ }else{
+ return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
+ numBytesHigh);
+ }
+#endif
+}
/*****************************************************************************
** The next group of routines implement the I/O methods specified
@@ -32146,47 +34881,84 @@ static BOOL winceLockFileEx(
******************************************************************************/
/*
-** Some microsoft compilers lack this definition.
+** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
/*
-** Move the current position of the file handle passed as the first
-** argument to offset iOffset within the file. If successful, return 0.
+** Move the current position of the file handle passed as the first
+** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.
*/
-static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
+#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
+ DWORD lastErrno; /* Value returned by GetLastError() */
+
+ OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
- /* API oddity: If successful, SetFilePointer() returns a dword
+ /* API oddity: If successful, SetFilePointer() returns a dword
** containing the lower 32-bits of the new file-offset. Or, if it fails,
- ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
- ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
- ** whether an error has actually occured, it is also necessary to call
+ ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
+ ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
+ ** whether an error has actually occurred, it is also necessary to call
** GetLastError().
*/
- dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
- if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath);
+ dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+
+ if( (dwRet==INVALID_SET_FILE_POINTER
+ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
+ "winSeekFile", pFile->zPath);
+ OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
+ OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
return 0;
+#else
+ /*
+ ** Same as above, except that this implementation works for WinRT.
+ */
+
+ LARGE_INTEGER x; /* The new offset */
+ BOOL bRet; /* Value returned by SetFilePointerEx() */
+
+ x.QuadPart = iOffset;
+ bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
+
+ if(!bRet){
+ pFile->lastErrno = osGetLastError();
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
+ "winSeekFile", pFile->zPath);
+ OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
+ return 1;
+ }
+
+ OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
+ return 0;
+#endif
}
+#if SQLITE_MAX_MMAP_SIZE>0
+/* Forward references to VFS helper methods used for memory mapped files */
+static int winMapfile(winFile*, sqlite3_int64);
+static int winUnmapfile(winFile*);
+#endif
+
/*
** Close a file.
**
** It is reported that an attempt to close a handle might sometimes
-** fail. This is a very unreasonable result, but windows is notorious
+** fail. This is a very unreasonable result, but Windows is notorious
** for being unreasonable so I do not doubt that it might happen. If
** the close fails, we pause for 100 milliseconds and try again. As
** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
@@ -32198,31 +34970,43 @@ static int winClose(sqlite3_file *id){
winFile *pFile = (winFile*)id;
assert( id!=0 );
+#ifndef SQLITE_OMIT_WAL
assert( pFile->pShm==0 );
- OSTRACE(("CLOSE %d\n", pFile->h));
+#endif
+ assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE );
+ OSTRACE(("CLOSE file=%p\n", pFile->h));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ winUnmapfile(pFile);
+#endif
+
do{
- rc = CloseHandle(pFile->h);
+ rc = osCloseHandle(pFile->h);
/* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */
- }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
+ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) );
#if SQLITE_OS_WINCE
#define WINCE_DELETION_ATTEMPTS 3
winceDestroyLock(pFile);
if( pFile->zDeleteOnClose ){
int cnt = 0;
while(
- DeleteFileW(pFile->zDeleteOnClose)==0
- && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
+ osDeleteFileW(pFile->zDeleteOnClose)==0
+ && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
&& cnt++ < WINCE_DELETION_ATTEMPTS
){
- Sleep(100); /* Wait a little before trying again */
+ sqlite3_win32_sleep(100); /* Wait a little before trying again */
}
- free(pFile->zDeleteOnClose);
+ sqlite3_free(pFile->zDeleteOnClose);
}
#endif
- OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
+ if( rc ){
+ pFile->h = NULL;
+ }
OpenCounter(-1);
+ OSTRACE(("CLOSE file=%p, rc=%s\n", pFile->h, rc ? "ok" : "failed"));
return rc ? SQLITE_OK
- : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath);
+ : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
+ "winClose", pFile->zPath);
}
/*
@@ -32236,26 +35020,67 @@ static int winRead(
int amt, /* Number of bytes to read */
sqlite3_int64 offset /* Begin reading at this offset */
){
+#if !SQLITE_OS_WINCE
+ OVERLAPPED overlapped; /* The offset for ReadFile. */
+#endif
winFile *pFile = (winFile*)id; /* file handle */
DWORD nRead; /* Number of bytes actually read from file */
+ int nRetry = 0; /* Number of retrys */
assert( id!=0 );
+ assert( amt>0 );
+ assert( offset>=0 );
SimulateIOError(return SQLITE_IOERR_READ);
- OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
+ OSTRACE(("READ file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n",
+ pFile->h, pBuf, amt, offset, pFile->locktype));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this read request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
+ OSTRACE(("READ-MMAP file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }else{
+ int nCopy = (int)(pFile->mmapSize - offset);
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
- if( seekWinFile(pFile, offset) ){
+#if SQLITE_OS_WINCE
+ if( winSeekFile(pFile, offset) ){
+ OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h));
return SQLITE_FULL;
}
- if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
- pFile->lastErrno = GetLastError();
- return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath);
- }
+ while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
+#else
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = (LONG)(offset & 0xffffffff);
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+ while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) &&
+ osGetLastError()!=ERROR_HANDLE_EOF ){
+#endif
+ DWORD lastErrno;
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
+ pFile->lastErrno = lastErrno;
+ OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h));
+ return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
+ "winRead", pFile->zPath);
+ }
+ winLogIoerr(nRetry);
if( nRead<(DWORD)amt ){
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
+ OSTRACE(("READ file=%p, rc=SQLITE_IOERR_SHORT_READ\n", pFile->h));
return SQLITE_IOERR_SHORT_READ;
}
+ OSTRACE(("READ file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
@@ -32269,28 +35094,80 @@ static int winWrite(
int amt, /* Number of bytes to write */
sqlite3_int64 offset /* Offset into the file to begin writing at */
){
- int rc; /* True if error has occured, else false */
+ int rc = 0; /* True if error has occurred, else false */
winFile *pFile = (winFile*)id; /* File handle */
+ int nRetry = 0; /* Number of retries */
assert( amt>0 );
assert( pFile );
SimulateIOError(return SQLITE_IOERR_WRITE);
SimulateDiskfullError(return SQLITE_FULL);
- OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
+ OSTRACE(("WRITE file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n",
+ pFile->h, pBuf, amt, offset, pFile->locktype));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this write request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
+ OSTRACE(("WRITE-MMAP file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }else{
+ int nCopy = (int)(pFile->mmapSize - offset);
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
- rc = seekWinFile(pFile, offset);
+#if SQLITE_OS_WINCE
+ rc = winSeekFile(pFile, offset);
if( rc==0 ){
+#else
+ {
+#endif
+#if !SQLITE_OS_WINCE
+ OVERLAPPED overlapped; /* The offset for WriteFile. */
+#endif
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */
+ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
+
+#if !SQLITE_OS_WINCE
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = (LONG)(offset & 0xffffffff);
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+#endif
- while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){
+ while( nRem>0 ){
+#if SQLITE_OS_WINCE
+ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
+#else
+ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
+#endif
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
+ break;
+ }
+ assert( nWrite==0 || nWrite<=(DWORD)nRem );
+ if( nWrite==0 || nWrite>(DWORD)nRem ){
+ lastErrno = osGetLastError();
+ break;
+ }
+#if !SQLITE_OS_WINCE
+ offset += nWrite;
+ overlapped.Offset = (LONG)(offset & 0xffffffff);
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+#endif
aRem += nWrite;
nRem -= nWrite;
}
if( nRem>0 ){
- pFile->lastErrno = GetLastError();
+ pFile->lastErrno = lastErrno;
rc = 1;
}
}
@@ -32298,10 +35175,17 @@ static int winWrite(
if( rc ){
if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|| ( pFile->lastErrno==ERROR_DISK_FULL )){
- return SQLITE_FULL;
+ OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h));
+ return winLogError(SQLITE_FULL, pFile->lastErrno,
+ "winWrite1", pFile->zPath);
}
- return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath);
+ OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h));
+ return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
+ "winWrite2", pFile->zPath);
+ }else{
+ winLogIoerr(nRetry);
}
+ OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
@@ -32311,30 +35195,44 @@ static int winWrite(
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
winFile *pFile = (winFile*)id; /* File handle object */
int rc = SQLITE_OK; /* Return code for this function */
+ DWORD lastErrno;
assert( pFile );
-
- OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
+ OSTRACE(("TRUNCATE file=%p, size=%lld, lock=%d\n",
+ pFile->h, nByte, pFile->locktype));
/* If the user has configured a chunk-size for this file, truncate the
** file so that it consists of an integer number of chunks (i.e. the
** actual file size after the operation may be larger than the requested
** size).
*/
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
- if( seekWinFile(pFile, nByte) ){
- rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath);
- }else if( 0==SetEndOfFile(pFile->h) ){
- pFile->lastErrno = GetLastError();
- rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath);
+ if( winSeekFile(pFile, nByte) ){
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
+ "winTruncate1", pFile->zPath);
+ }else if( 0==osSetEndOfFile(pFile->h) &&
+ ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){
+ pFile->lastErrno = lastErrno;
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
+ "winTruncate2", pFile->zPath);
}
- OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* If the file was truncated to a size smaller than the currently
+ ** mapped region, reduce the effective mapping size as well. SQLite will
+ ** use read() and write() to access data beyond this point from now on.
+ */
+ if( pFile->pMapRegion && nByte<pFile->mmapSize ){
+ pFile->mmapSize = nByte;
+ }
+#endif
+
+ OSTRACE(("TRUNCATE file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
return rc;
}
@@ -32351,9 +35249,19 @@ SQLITE_API int sqlite3_fullsync_count = 0;
** Make sure all writes to a particular file are committed to disk.
*/
static int winSync(sqlite3_file *id, int flags){
-#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG)
- winFile *pFile = (winFile*)id;
+#ifndef SQLITE_NO_SYNC
+ /*
+ ** Used only when SQLITE_NO_SYNC is not defined.
+ */
BOOL rc;
+#endif
+#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \
+ (defined(SQLITE_TEST) && defined(SQLITE_DEBUG))
+ /*
+ ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or
+ ** OSTRACE() macros.
+ */
+ winFile *pFile = (winFile*)id;
#else
UNUSED_PARAMETER(id);
#endif
@@ -32364,13 +35272,14 @@ static int winSync(sqlite3_file *id, int flags){
|| (flags&0x0F)==SQLITE_SYNC_FULL
);
- OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype));
-
/* Unix cannot, but some systems may return SQLITE_FULL from here. This
** line is to test that doing so does not cause any problems.
*/
SimulateDiskfullError( return SQLITE_FULL );
+ OSTRACE(("SYNC file=%p, flags=%x, lock=%d\n",
+ pFile->h, flags, pFile->locktype));
+
#ifndef SQLITE_TEST
UNUSED_PARAMETER(flags);
#else
@@ -32384,15 +35293,19 @@ static int winSync(sqlite3_file *id, int flags){
** no-op
*/
#ifdef SQLITE_NO_SYNC
+ OSTRACE(("SYNC-NOP file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
#else
- rc = FlushFileBuffers(pFile->h);
+ rc = osFlushFileBuffers(pFile->h);
SimulateIOError( rc=FALSE );
if( rc ){
+ OSTRACE(("SYNC file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}else{
- pFile->lastErrno = GetLastError();
- return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath);
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h));
+ return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
+ "winSync", pFile->zPath);
}
#endif
}
@@ -32401,22 +35314,45 @@ static int winSync(sqlite3_file *id, int flags){
** Determine the current size of a file in bytes
*/
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
- DWORD upperBits;
- DWORD lowerBits;
winFile *pFile = (winFile*)id;
- DWORD error;
+ int rc = SQLITE_OK;
assert( id!=0 );
+ assert( pSize!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT);
- lowerBits = GetFileSize(pFile->h, &upperBits);
- if( (lowerBits == INVALID_FILE_SIZE)
- && ((error = GetLastError()) != NO_ERROR) )
+ OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize));
+
+#if SQLITE_OS_WINRT
+ {
+ FILE_STANDARD_INFO info;
+ if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo,
+ &info, sizeof(info)) ){
+ *pSize = info.EndOfFile.QuadPart;
+ }else{
+ pFile->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
+ "winFileSize", pFile->zPath);
+ }
+ }
+#else
{
- pFile->lastErrno = error;
- return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath);
+ DWORD upperBits;
+ DWORD lowerBits;
+ DWORD lastErrno;
+
+ lowerBits = osGetFileSize(pFile->h, &upperBits);
+ *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
+ if( (lowerBits == INVALID_FILE_SIZE)
+ && ((lastErrno = osGetLastError())!=NO_ERROR) ){
+ pFile->lastErrno = lastErrno;
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
+ "winFileSize", pFile->zPath);
+ }
}
- *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
- return SQLITE_OK;
+#endif
+ OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n",
+ pFile->h, pSize, *pSize, sqlite3ErrName(rc)));
+ return rc;
}
/*
@@ -32426,55 +35362,88 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
# define LOCKFILE_FAIL_IMMEDIATELY 1
#endif
+#ifndef LOCKFILE_EXCLUSIVE_LOCK
+# define LOCKFILE_EXCLUSIVE_LOCK 2
+#endif
+
+/*
+** Historically, SQLite has used both the LockFile and LockFileEx functions.
+** When the LockFile function was used, it was always expected to fail
+** immediately if the lock could not be obtained. Also, it always expected to
+** obtain an exclusive lock. These flags are used with the LockFileEx function
+** and reflect those expectations; therefore, they should not be changed.
+*/
+#ifndef SQLITE_LOCKFILE_FLAGS
+# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \
+ LOCKFILE_EXCLUSIVE_LOCK)
+#endif
+
+/*
+** Currently, SQLite never calls the LockFileEx function without wanting the
+** call to fail immediately if the lock cannot be obtained.
+*/
+#ifndef SQLITE_LOCKFILEEX_FLAGS
+# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY)
+#endif
+
/*
** Acquire a reader lock.
** Different API routines are called depending on whether or not this
-** is Win95 or WinNT.
+** is Win9x or WinNT.
*/
-static int getReadLock(winFile *pFile){
+static int winGetReadLock(winFile *pFile){
int res;
- if( isNT() ){
- OVERLAPPED ovlp;
- ovlp.Offset = SHARED_FIRST;
- ovlp.OffsetHigh = 0;
- ovlp.hEvent = 0;
- res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,
- 0, SHARED_SIZE, 0, &ovlp);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
+ if( osIsNT() ){
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API LockFileEx.
+ */
+ res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
+#else
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
+ SHARED_SIZE, 0);
+#endif
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
int lk;
sqlite3_randomness(sizeof(lk), &lk);
pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
- res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
-#endif
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
+#endif
if( res == 0 ){
- pFile->lastErrno = GetLastError();
+ pFile->lastErrno = osGetLastError();
/* No need to log a failure to lock */
}
+ OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
/*
** Undo a readlock
*/
-static int unlockReadLock(winFile *pFile){
+static int winUnlockReadLock(winFile *pFile){
int res;
- if( isNT() ){
- res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
-#endif
+ DWORD lastErrno;
+ OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
+ if( osIsNT() ){
+ res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
- if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath);
+#endif
+ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
+ "winUnlockReadLock", pFile->zPath);
}
+ OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
@@ -32506,21 +35475,22 @@ static int unlockReadLock(winFile *pFile){
*/
static int winLock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK; /* Return code from subroutines */
- int res = 1; /* Result of a windows lock call */
+ int res = 1; /* Result of a Windows lock call */
int newLocktype; /* Set pFile->locktype to this value before exiting */
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
winFile *pFile = (winFile*)id;
- DWORD error = NO_ERROR;
+ DWORD lastErrno = NO_ERROR;
assert( id!=0 );
- OSTRACE(("LOCK %d %d was %d(%d)\n",
- pFile->h, locktype, pFile->locktype, pFile->sharedLockByte));
+ OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n",
+ pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
/* If there is already a lock of this type or more restrictive on the
** OsFile, do nothing. Don't use the end_lock: exit path, as
** sqlite3OsEnterMutex() hasn't been called yet.
*/
if( pFile->locktype>=locktype ){
+ OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
@@ -32540,16 +35510,29 @@ static int winLock(sqlite3_file *id, int locktype){
&& (pFile->locktype==RESERVED_LOCK))
){
int cnt = 3;
- while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
- /* Try 3 times to get the pending lock. The pending lock might be
- ** held by another reader process who will release it momentarily.
+ while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ PENDING_BYTE, 0, 1, 0))==0 ){
+ /* Try 3 times to get the pending lock. This is needed to work
+ ** around problems caused by indexing and/or anti-virus software on
+ ** Windows systems.
+ ** If you are using this code as a model for alternative VFSes, do not
+ ** copy this retry logic. It is a hack intended for Windows only.
*/
- OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt));
- Sleep(1);
+ lastErrno = osGetLastError();
+ OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
+ pFile->h, cnt, res));
+ if( lastErrno==ERROR_INVALID_HANDLE ){
+ pFile->lastErrno = lastErrno;
+ rc = SQLITE_IOERR_LOCK;
+ OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
+ pFile->h, cnt, sqlite3ErrName(rc)));
+ return rc;
+ }
+ if( cnt ) sqlite3_win32_sleep(1);
}
gotPendingLock = res;
if( !res ){
- error = GetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -32557,11 +35540,11 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
+ res = winGetReadLock(pFile);
if( res ){
newLocktype = SHARED_LOCK;
}else{
- error = GetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -32569,11 +35552,11 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==RESERVED_LOCK && res ){
assert( pFile->locktype==SHARED_LOCK );
- res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0);
if( res ){
newLocktype = RESERVED_LOCK;
}else{
- error = GetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -32588,15 +35571,14 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
- OSTRACE(("unreadlock = %d\n", res));
- res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ res = winUnlockReadLock(pFile);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
+ SHARED_SIZE, 0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
- error = GetLastError();
- OSTRACE(("error-code = %d\n", error));
- getReadLock(pFile);
+ lastErrno = osGetLastError();
+ winGetReadLock(pFile);
}
}
@@ -32604,7 +35586,7 @@ static int winLock(sqlite3_file *id, int locktype){
** release it now.
*/
if( gotPendingLock && locktype==SHARED_LOCK ){
- UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
}
/* Update the state of the lock has held in the file descriptor then
@@ -32613,12 +35595,14 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
rc = SQLITE_OK;
}else{
- OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h,
- locktype, newLocktype));
- pFile->lastErrno = error;
+ pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
+ OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
+ pFile->h, locktype, newLocktype));
}
pFile->locktype = (u8)newLocktype;
+ OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n",
+ pFile->h, pFile->locktype, sqlite3ErrName(rc)));
return rc;
}
@@ -32628,24 +35612,27 @@ static int winLock(sqlite3_file *id, int locktype){
** non-zero, otherwise zero.
*/
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
- int rc;
+ int res;
winFile *pFile = (winFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
+ OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut));
assert( id!=0 );
if( pFile->locktype>=RESERVED_LOCK ){
- rc = 1;
- OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc));
+ res = 1;
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
}else{
- rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
- if( rc ){
- UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
+ if( res ){
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
- rc = !rc;
- OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc));
+ res = !res;
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res));
}
- *pResOut = rc;
+ *pResOut = res;
+ OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
+ pFile->h, pResOut, *pResOut));
return SQLITE_OK;
}
@@ -32666,58 +35653,165 @@ static int winUnlock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK;
assert( pFile!=0 );
assert( locktype<=SHARED_LOCK );
- OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype,
- pFile->locktype, pFile->sharedLockByte));
+ OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
+ pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
- UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
- if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
+ winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
- rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath);
+ rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
+ "winUnlock", pFile->zPath);
}
}
if( type>=RESERVED_LOCK ){
- UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- unlockReadLock(pFile);
+ winUnlockReadLock(pFile);
}
if( type>=PENDING_LOCK ){
- UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
}
pFile->locktype = (u8)locktype;
+ OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n",
+ pFile->h, pFile->locktype, sqlite3ErrName(rc)));
return rc;
}
/*
+** If *pArg is initially negative then this is a query. Set *pArg to
+** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
+**
+** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
+*/
+static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
+ if( *pArg<0 ){
+ *pArg = (pFile->ctrlFlags & mask)!=0;
+ }else if( (*pArg)==0 ){
+ pFile->ctrlFlags &= ~mask;
+ }else{
+ pFile->ctrlFlags |= mask;
+ }
+}
+
+/* Forward references to VFS helper methods used for temporary files */
+static int winGetTempname(sqlite3_vfs *, char **);
+static int winIsDir(const void *);
+static BOOL winIsDriveLetterAndColon(const char *);
+
+/*
** Control and query of the open file handle.
*/
static int winFileControl(sqlite3_file *id, int op, void *pArg){
+ winFile *pFile = (winFile*)id;
+ OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg));
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((winFile*)id)->locktype;
+ *(int*)pArg = pFile->locktype;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_LAST_ERRNO: {
- *(int*)pArg = (int)((winFile*)id)->lastErrno;
+ *(int*)pArg = (int)pFile->lastErrno;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
- ((winFile*)id)->szChunk = *(int *)pArg;
+ pFile->szChunk = *(int *)pArg;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
- sqlite3_int64 sz = *(sqlite3_int64*)pArg;
- SimulateIOErrorBenign(1);
- winTruncate(id, sz);
- SimulateIOErrorBenign(0);
+ if( pFile->szChunk>0 ){
+ sqlite3_int64 oldSz;
+ int rc = winFileSize(id, &oldSz);
+ if( rc==SQLITE_OK ){
+ sqlite3_int64 newSz = *(sqlite3_int64*)pArg;
+ if( newSz>oldSz ){
+ SimulateIOErrorBenign(1);
+ rc = winTruncate(id, newSz);
+ SimulateIOErrorBenign(0);
+ }
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
+ }
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_PERSIST_WAL: {
+ winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg);
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
- case SQLITE_FCNTL_SYNC_OMITTED: {
+ case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
+ winModeBit(pFile, WINFILE_PSOW, (int*)pArg);
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
+ case SQLITE_FCNTL_VFSNAME: {
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_WIN32_AV_RETRY: {
+ int *a = (int*)pArg;
+ if( a[0]>0 ){
+ winIoerrRetry = a[0];
+ }else{
+ a[0] = winIoerrRetry;
+ }
+ if( a[1]>0 ){
+ winIoerrRetryDelay = a[1];
+ }else{
+ a[1] = winIoerrRetryDelay;
+ }
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
+#ifdef SQLITE_TEST
+ case SQLITE_FCNTL_WIN32_SET_HANDLE: {
+ LPHANDLE phFile = (LPHANDLE)pArg;
+ HANDLE hOldFile = pFile->h;
+ pFile->h = *phFile;
+ *phFile = hOldFile;
+ OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
+ hOldFile, pFile->h));
+ return SQLITE_OK;
+ }
+#endif
+ case SQLITE_FCNTL_TEMPFILENAME: {
+ char *zTFile = 0;
+ int rc = winGetTempname(pFile->pVfs, &zTFile);
+ if( rc==SQLITE_OK ){
+ *(char**)pArg = zTFile;
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
+ }
+#if SQLITE_MAX_MMAP_SIZE>0
+ case SQLITE_FCNTL_MMAP_SIZE: {
+ i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
+ if( newLimit>sqlite3GlobalConfig.mxMmap ){
+ newLimit = sqlite3GlobalConfig.mxMmap;
+ }
+ *(i64*)pArg = pFile->mmapSizeMax;
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
+ pFile->mmapSizeMax = newLimit;
+ if( pFile->mmapSize>0 ){
+ winUnmapfile(pFile);
+ rc = winMapfile(pFile, -1);
+ }
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
+ }
+#endif
}
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
return SQLITE_NOTFOUND;
}
@@ -32732,35 +35826,36 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
** same for both.
*/
static int winSectorSize(sqlite3_file *id){
- assert( id!=0 );
- return (int)(((winFile*)id)->sectorSize);
+ (void)id;
+ return SQLITE_DEFAULT_SECTOR_SIZE;
}
/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
+ winFile *p = (winFile*)id;
+ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
+ ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
-#ifndef SQLITE_OMIT_WAL
-
-/*
+/*
** Windows will only let you create file view mappings
** on allocation size granularity boundaries.
** During sqlite3_os_init() we do a GetSystemInfo()
** to get the granularity size.
*/
-SYSTEM_INFO winSysInfo;
+static SYSTEM_INFO winSysInfo;
+
+#ifndef SQLITE_OMIT_WAL
/*
** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect the winLockInfo objects used by
+** global mutex is used to protect the winLockInfo objects used by
** this file, all of which may be shared by multiple threads.
**
-** Function winShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
+** Function winShmMutexHeld() is used to assert() that the global mutex
+** is held when required. This function is only used as part of assert()
** statements. e.g.
**
** winShmEnterMutex()
@@ -32773,7 +35868,7 @@ static void winShmEnterMutex(void){
static void winShmLeaveMutex(void){
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
-#ifdef SQLITE_DEBUG
+#ifndef NDEBUG
static int winShmMutexHeld(void) {
return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
@@ -32790,10 +35885,10 @@ static int winShmMutexHeld(void) {
** this object or while reading or writing the following fields:
**
** nRef
-** pNext
+** pNext
**
** The following fields are read-only after the object is created:
-**
+**
** fid
** zFilename
**
@@ -32872,39 +35967,34 @@ static int winShmSystemLock(
int ofst, /* Offset to first byte to be locked/unlocked */
int nByte /* Number of bytes to lock or unlock */
){
- OVERLAPPED ovlp;
- DWORD dwFlags;
int rc = 0; /* Result code form Lock/UnlockFileEx() */
/* Access to the winShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
- /* Initialize the locking parameters */
- dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
- if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
-
- memset(&ovlp, 0, sizeof(OVERLAPPED));
- ovlp.Offset = ofst;
+ OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
+ pFile->hFile.h, lockType, ofst, nByte));
/* Release/Acquire the system-level lock */
if( lockType==_SHM_UNLCK ){
- rc = UnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp);
+ rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
}else{
- rc = LockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp);
+ /* Initialize the locking parameters */
+ DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
+ if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
+ rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
}
-
+
if( rc!= 0 ){
rc = SQLITE_OK;
}else{
- pFile->lastErrno = GetLastError();
+ pFile->lastErrno = osGetLastError();
rc = SQLITE_BUSY;
}
- OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",
- pFile->hFile.h,
- rc==SQLITE_OK ? "ok" : "failed",
- lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx",
- pFile->lastErrno));
+ OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
+ pFile->hFile.h, (lockType == _SHM_UNLCK) ? "winUnlockFile" :
+ "winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
return rc;
}
@@ -32922,31 +36012,34 @@ static int winDelete(sqlite3_vfs *,const char*,int);
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
winShmNode **pp;
winShmNode *p;
- BOOL bRc;
assert( winShmMutexHeld() );
+ OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
+ osGetCurrentProcessId(), deleteFlag));
pp = &winShmNodeList;
while( (p = *pp)!=0 ){
if( p->nRef==0 ){
int i;
- if( p->mutex ) sqlite3_mutex_free(p->mutex);
+ if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
for(i=0; i<p->nRegion; i++){
- bRc = UnmapViewOfFile(p->aRegion[i].pMap);
- OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
- (int)GetCurrentProcessId(), i,
- bRc ? "ok" : "failed"));
- bRc = CloseHandle(p->aRegion[i].hMap);
- OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n",
- (int)GetCurrentProcessId(), i,
- bRc ? "ok" : "failed"));
- }
- if( p->hFile.h != INVALID_HANDLE_VALUE ){
+ BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
+ OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
+ bRc = osCloseHandle(p->aRegion[i].hMap);
+ OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
+ }
+ if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
SimulateIOErrorBenign(1);
winClose((sqlite3_file *)&p->hFile);
SimulateIOErrorBenign(0);
}
if( deleteFlag ){
SimulateIOErrorBenign(1);
+ sqlite3BeginBenignMalloc();
winDelete(pVfs, p->zFilename, 0);
+ sqlite3EndBenignMalloc();
SimulateIOErrorBenign(0);
}
*pp = p->pNext;
@@ -32977,19 +36070,17 @@ static int winOpenSharedMemory(winFile *pDbFd){
/* Allocate space for the new sqlite3_shm object. Also speculatively
** allocate space for a new winShmNode and filename.
*/
- p = sqlite3_malloc( sizeof(*p) );
- if( p==0 ) return SQLITE_NOMEM;
- memset(p, 0, sizeof(*p));
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p==0 ) return SQLITE_IOERR_NOMEM;
nName = sqlite3Strlen30(pDbFd->zPath);
- pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 15 );
+ pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 );
if( pNew==0 ){
sqlite3_free(p);
- return SQLITE_NOMEM;
+ return SQLITE_IOERR_NOMEM;
}
- memset(pNew, 0, sizeof(*pNew));
pNew->zFilename = (char*)&pNew[1];
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
- sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
+ sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
/* Look to see if there is an existing winShmNode that can be used.
** If no matching winShmNode currently exists, create a new one.
@@ -33012,27 +36103,27 @@ static int winOpenSharedMemory(winFile *pDbFd){
pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->mutex==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_IOERR_NOMEM;
goto shm_open_err;
}
rc = winOpen(pDbFd->pVfs,
pShmNode->zFilename, /* Name of the file (UTF-8) */
(sqlite3_file*)&pShmNode->hFile, /* File handle here */
- SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
+ SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
0);
if( SQLITE_OK!=rc ){
- rc = SQLITE_CANTOPEN_BKPT;
goto shm_open_err;
}
/* Check to see if another process is holding the dead-man switch.
- ** If not, truncate the file to zero length.
+ ** If not, truncate the file to zero length.
*/
if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath);
+ rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
+ "winOpenShm", pDbFd->zPath);
}
}
if( rc==SQLITE_OK ){
@@ -33055,7 +36146,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
** the cover of the winShmEnterMutex() mutex and the pointer from the
** new (struct winShm) object to the pShmNode has been set. All that is
** left to do is to link the new object into the linked list starting
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
+ ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
** mutex.
*/
sqlite3_mutex_enter(pShmNode->mutex);
@@ -33075,7 +36166,7 @@ shm_open_err:
}
/*
-** Close a connection to shared-memory. Delete the underlying
+** Close a connection to shared-memory. Delete the underlying
** storage if deleteFlag is true.
*/
static int winShmUnmap(
@@ -33164,7 +36255,7 @@ static int winShmLock(
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
- }
+ }
}else if( flags & SQLITE_SHM_SHARED ){
u16 allShared = 0; /* Union of locks held by connections other than "p" */
@@ -33203,7 +36294,7 @@ static int winShmLock(
break;
}
}
-
+
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
@@ -33216,14 +36307,14 @@ static int winShmLock(
}
}
sqlite3_mutex_leave(pShmNode->mutex);
- OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n",
- p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask,
- rc ? "failed" : "ok"));
+ OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
+ osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
+ sqlite3ErrName(rc)));
return rc;
}
/*
-** Implement a memory barrier or memory fence on shared memory.
+** Implement a memory barrier or memory fence on shared memory.
**
** All loads and stores begun before the barrier must complete before
** any load or store begun after the barrier.
@@ -33238,22 +36329,22 @@ static void winShmBarrier(
}
/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file fd. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
+** This function is called to obtain a pointer to region iRegion of the
+** shared-memory associated with the database file fd. Shared-memory regions
+** are numbered starting from zero. Each shared-memory region is szRegion
** bytes in size.
**
** If an error occurs, an error code is returned and *pp is set to NULL.
**
** Otherwise, if the isWrite parameter is 0 and the requested shared-memory
** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** isWrite is non-zero and the requested shared-memory region has not yet
+** separate process), then *pp is set to NULL and SQLITE_OK returned. If
+** isWrite is non-zero and the requested shared-memory region has not yet
** been allocated, it is allocated by this function.
**
** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
+** this call as described above, then it is mapped into this processes
+** address space (if it is not already), *pp is set to point to the mapped
** memory and SQLITE_OK returned.
*/
static int winShmMap(
@@ -33291,7 +36382,8 @@ static int winShmMap(
*/
rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath);
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
+ "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
@@ -33305,7 +36397,8 @@ static int winShmMap(
if( !isWrite ) goto shmpage_out;
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath);
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
+ "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
@@ -33321,29 +36414,46 @@ static int winShmMap(
pShmNode->aRegion = apNew;
while( pShmNode->nRegion<=iRegion ){
- HANDLE hMap; /* file-mapping handle */
+ HANDLE hMap = NULL; /* file-mapping handle */
void *pMap = 0; /* Mapped memory region */
-
- hMap = CreateFileMapping(pShmNode->hFile.h,
+
+#if SQLITE_OS_WINRT
+ hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
+ NULL, PAGE_READWRITE, nByte, NULL
+ );
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ hMap = osCreateFileMappingW(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
- OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n",
- (int)GetCurrentProcessId(), pShmNode->nRegion, nByte,
+#elif defined(SQLITE_WIN32_HAS_ANSI)
+ hMap = osCreateFileMappingA(pShmNode->hFile.h,
+ NULL, PAGE_READWRITE, 0, nByte, NULL
+ );
+#endif
+ OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
+ osGetCurrentProcessId(), pShmNode->nRegion, nByte,
hMap ? "ok" : "failed"));
if( hMap ){
int iOffset = pShmNode->nRegion*szRegion;
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
- pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+#if SQLITE_OS_WINRT
+ pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+ iOffset - iOffsetShift, szRegion + iOffsetShift
+ );
+#else
+ pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
0, iOffset - iOffsetShift, szRegion + iOffsetShift
);
- OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n",
- (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion,
- pMap ? "ok" : "failed"));
+#endif
+ OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n",
+ osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
+ szRegion, pMap ? "ok" : "failed"));
}
if( !pMap ){
- pShmNode->lastErrno = GetLastError();
- rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath);
- if( hMap ) CloseHandle(hMap);
+ pShmNode->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno,
+ "winShmMap3", pDbFd->zPath);
+ if( hMap ) osCloseHandle(hMap);
goto shmpage_out;
}
@@ -33374,6 +36484,231 @@ shmpage_out:
#endif /* #ifndef SQLITE_OMIT_WAL */
/*
+** Cleans up the mapped region of the specified file, if any.
+*/
+#if SQLITE_MAX_MMAP_SIZE>0
+static int winUnmapfile(winFile *pFile){
+ assert( pFile!=0 );
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, "
+ "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n",
+ osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion,
+ pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax));
+ if( pFile->pMapRegion ){
+ if( !osUnmapViewOfFile(pFile->pMapRegion) ){
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, "
+ "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile,
+ pFile->pMapRegion));
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
+ "winUnmapfile1", pFile->zPath);
+ }
+ pFile->pMapRegion = 0;
+ pFile->mmapSize = 0;
+ pFile->mmapSizeActual = 0;
+ }
+ if( pFile->hMap!=NULL ){
+ if( !osCloseHandle(pFile->hMap) ){
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
+ osGetCurrentProcessId(), pFile, pFile->hMap));
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
+ "winUnmapfile2", pFile->zPath);
+ }
+ pFile->hMap = NULL;
+ }
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile));
+ return SQLITE_OK;
+}
+
+/*
+** Memory map or remap the file opened by file-descriptor pFd (if the file
+** is already mapped, the existing mapping is replaced by the new). Or, if
+** there already exists a mapping for this file, and there are still
+** outstanding xFetch() references to it, this function is a no-op.
+**
+** If parameter nByte is non-negative, then it is the requested size of
+** the mapping to create. Otherwise, if nByte is less than zero, then the
+** requested size is the size of the file on disk. The actual size of the
+** created mapping is either the requested size or the value configured
+** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller.
+**
+** SQLITE_OK is returned if no error occurs (even if the mapping is not
+** recreated as a result of outstanding references) or an SQLite error
+** code otherwise.
+*/
+static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
+ sqlite3_int64 nMap = nByte;
+ int rc;
+
+ assert( nMap>=0 || pFd->nFetchOut==0 );
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n",
+ osGetCurrentProcessId(), pFd, nByte));
+
+ if( pFd->nFetchOut>0 ) return SQLITE_OK;
+
+ if( nMap<0 ){
+ rc = winFileSize((sqlite3_file*)pFd, &nMap);
+ if( rc ){
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n",
+ osGetCurrentProcessId(), pFd));
+ return SQLITE_IOERR_FSTAT;
+ }
+ }
+ if( nMap>pFd->mmapSizeMax ){
+ nMap = pFd->mmapSizeMax;
+ }
+ nMap &= ~(sqlite3_int64)(winSysInfo.dwPageSize - 1);
+
+ if( nMap==0 && pFd->mmapSize>0 ){
+ winUnmapfile(pFd);
+ }
+ if( nMap!=pFd->mmapSize ){
+ void *pNew = 0;
+ DWORD protect = PAGE_READONLY;
+ DWORD flags = FILE_MAP_READ;
+
+ winUnmapfile(pFd);
+ if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){
+ protect = PAGE_READWRITE;
+ flags |= FILE_MAP_WRITE;
+ }
+#if SQLITE_OS_WINRT
+ pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL);
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect,
+ (DWORD)((nMap>>32) & 0xffffffff),
+ (DWORD)(nMap & 0xffffffff), NULL);
+#elif defined(SQLITE_WIN32_HAS_ANSI)
+ pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect,
+ (DWORD)((nMap>>32) & 0xffffffff),
+ (DWORD)(nMap & 0xffffffff), NULL);
+#endif
+ if( pFd->hMap==NULL ){
+ pFd->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
+ "winMapfile1", pFd->zPath);
+ /* Log the error, but continue normal operation using xRead/xWrite */
+ OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
+ return SQLITE_OK;
+ }
+ assert( (nMap % winSysInfo.dwPageSize)==0 );
+ assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
+#if SQLITE_OS_WINRT
+ pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap);
+#else
+ pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap);
+#endif
+ if( pNew==NULL ){
+ osCloseHandle(pFd->hMap);
+ pFd->hMap = NULL;
+ pFd->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
+ "winMapfile2", pFd->zPath);
+ /* Log the error, but continue normal operation using xRead/xWrite */
+ OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
+ return SQLITE_OK;
+ }
+ pFd->pMapRegion = pNew;
+ pFd->mmapSize = nMap;
+ pFd->mmapSizeActual = nMap;
+ }
+
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFd));
+ return SQLITE_OK;
+}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
+
+/*
+** If possible, return a pointer to a mapping of file fd starting at offset
+** iOff. The mapping must be valid for at least nAmt bytes.
+**
+** If such a pointer can be obtained, store it in *pp and return SQLITE_OK.
+** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK.
+** Finally, if an error does occur, return an SQLite error code. The final
+** value of *pp is undefined in this case.
+**
+** If this function does return a pointer, the caller must eventually
+** release the reference by calling winUnfetch().
+*/
+static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
+#if SQLITE_MAX_MMAP_SIZE>0
+ winFile *pFd = (winFile*)fd; /* The underlying database file */
+#endif
+ *pp = 0;
+
+ OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
+ osGetCurrentProcessId(), fd, iOff, nAmt, pp));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFd->mmapSizeMax>0 ){
+ if( pFd->pMapRegion==0 ){
+ int rc = winMapfile(pFd, -1);
+ if( rc!=SQLITE_OK ){
+ OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
+ return rc;
+ }
+ }
+ if( pFd->mmapSize >= iOff+nAmt ){
+ *pp = &((u8 *)pFd->pMapRegion)[iOff];
+ pFd->nFetchOut++;
+ }
+ }
+#endif
+
+ OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), fd, pp, *pp));
+ return SQLITE_OK;
+}
+
+/*
+** If the third argument is non-NULL, then this function releases a
+** reference obtained by an earlier call to winFetch(). The second
+** argument passed to this function must be the same as the corresponding
+** argument that was passed to the winFetch() invocation.
+**
+** Or, if the third argument is NULL, then this function is being called
+** to inform the VFS layer that, according to POSIX, any existing mapping
+** may now be invalid and should be unmapped.
+*/
+static int winUnfetch(sqlite3_file *fd, i64 iOff, void *p){
+#if SQLITE_MAX_MMAP_SIZE>0
+ winFile *pFd = (winFile*)fd; /* The underlying database file */
+
+ /* If p==0 (unmap the entire file) then there must be no outstanding
+ ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
+ ** then there must be at least one outstanding. */
+ assert( (p==0)==(pFd->nFetchOut==0) );
+
+ /* If p!=0, it must match the iOff value. */
+ assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
+
+ OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n",
+ osGetCurrentProcessId(), pFd, iOff, p));
+
+ if( p ){
+ pFd->nFetchOut--;
+ }else{
+ /* FIXME: If Windows truly always prevents truncating or deleting a
+ ** file while a mapping is held, then the following winUnmapfile() call
+ ** is unnecessary can be omitted - potentially improving
+ ** performance. */
+ winUnmapfile(pFd);
+ }
+
+ assert( pFd->nFetchOut>=0 );
+#endif
+
+ OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), fd));
+ return SQLITE_OK;
+}
+
+/*
** Here ends the implementation of all sqlite3_file methods.
**
********************** End sqlite3_file Methods *******************************
@@ -33384,7 +36719,7 @@ shmpage_out:
** sqlite3_file for win32.
*/
static const sqlite3_io_methods winIoMethod = {
- 2, /* iVersion */
+ 3, /* iVersion */
winClose, /* xClose */
winRead, /* xRead */
winWrite, /* xWrite */
@@ -33400,7 +36735,9 @@ static const sqlite3_io_methods winIoMethod = {
winShmMap, /* xShmMap */
winShmLock, /* xShmLock */
winShmBarrier, /* xShmBarrier */
- winShmUnmap /* xShmUnmap */
+ winShmUnmap, /* xShmUnmap */
+ winFetch, /* xFetch */
+ winUnfetch /* xUnfetch */
};
/****************************************************************************
@@ -33410,111 +36747,339 @@ static const sqlite3_io_methods winIoMethod = {
** sqlite3_vfs object.
*/
+#if defined(__CYGWIN__)
+/*
+** Convert a filename from whatever the underlying operating system
+** supports for filenames into UTF-8. Space to hold the result is
+** obtained from malloc and must be freed by the calling function.
+*/
+static char *winConvertToUtf8Filename(const void *zFilename){
+ char *zConverted = 0;
+ if( osIsNT() ){
+ zConverted = winUnicodeToUtf8(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ zConverted = sqlite3_win32_mbcs_to_utf8(zFilename);
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+#endif
+
/*
** Convert a UTF-8 filename into whatever form the underlying
** operating system wants filenames in. Space to hold the result
** is obtained from malloc and must be freed by the calling
** function.
*/
-static void *convertUtf8Filename(const char *zFilename){
+static void *winConvertFromUtf8Filename(const char *zFilename){
void *zConverted = 0;
- if( isNT() ){
- zConverted = utf8ToUnicode(zFilename);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ if( osIsNT() ){
+ zConverted = winUtf8ToUnicode(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
-#endif
}
+#endif
/* caller will handle out of memory */
return zConverted;
}
/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
+** This function returns non-zero if the specified UTF-8 string buffer
+** ends with a directory separator character or one was successfully
+** added to it.
+*/
+static int winMakeEndInDirSep(int nBuf, char *zBuf){
+ if( zBuf ){
+ int nLen = sqlite3Strlen30(zBuf);
+ if( nLen>0 ){
+ if( winIsDirSep(zBuf[nLen-1]) ){
+ return 1;
+ }else if( nLen+1<nBuf ){
+ zBuf[nLen] = winGetDirSep();
+ zBuf[nLen+1] = '\0';
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Create a temporary file name and store the resulting pointer into pzBuf.
+** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
-static int getTempname(int nBuf, char *zBuf){
+static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
static char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
- char zTempPath[MAX_PATH+1];
+ int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX);
+ int nMax, nBuf, nDir, nLen;
+ char *zBuf;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
- ** function failing.
+ ** function failing.
*/
SimulateIOError( return SQLITE_IOERR );
+ /* Allocate a temporary buffer to store the fully qualified file
+ ** name for the temporary file. If this fails, we cannot continue.
+ */
+ nMax = pVfs->mxPathname; nBuf = nMax + 2;
+ zBuf = sqlite3MallocZero( nBuf );
+ if( !zBuf ){
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+
+ /* Figure out the effective temporary directory. First, check if one
+ ** has been explicitly set by the application; otherwise, use the one
+ ** configured by the operating system.
+ */
+ nDir = nMax - (nPre + 15);
+ assert( nDir>0 );
if( sqlite3_temp_directory ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
- }else if( isNT() ){
+ int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
+ if( nDirLen>0 ){
+ if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
+ nDirLen++;
+ }
+ if( nDirLen>nDir ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
+ }
+ sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
+ }
+ }
+#if defined(__CYGWIN__)
+ else{
+ static const char *azDirs[] = {
+ 0, /* getenv("SQLITE_TMPDIR") */
+ 0, /* getenv("TMPDIR") */
+ 0, /* getenv("TMP") */
+ 0, /* getenv("TEMP") */
+ 0, /* getenv("USERPROFILE") */
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ ".",
+ 0 /* List terminator */
+ };
+ unsigned int i;
+ const char *zDir = 0;
+
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMP");
+ if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
+ if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
+ void *zConverted;
+ if( zDir==0 ) continue;
+ /* If the path starts with a drive letter followed by the colon
+ ** character, assume it is already a native Win32 path; otherwise,
+ ** it must be converted to a native Win32 path via the Cygwin API
+ ** prior to using it.
+ */
+ if( winIsDriveLetterAndColon(zDir) ){
+ zConverted = winConvertFromUtf8Filename(zDir);
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( winIsDir(zConverted) ){
+ sqlite3_snprintf(nMax, zBuf, "%s", zDir);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }else{
+ zConverted = sqlite3MallocZero( nMax+1 );
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
+ zConverted, nMax+1)<0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
+ return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
+ "winGetTempname2", zDir);
+ }
+ if( winIsDir(zConverted) ){
+ /* At this point, we know the candidate directory exists and should
+ ** be used. However, we may need to convert the string containing
+ ** its name into UTF-8 (i.e. if it is UTF-16 right now).
+ */
+ char *zUtf8 = winConvertToUtf8Filename(zConverted);
+ if( !zUtf8 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }
+ }
+ }
+#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+ else if( osIsNT() ){
char *zMulti;
- WCHAR zWidePath[MAX_PATH];
- GetTempPathW(MAX_PATH-30, zWidePath);
- zMulti = unicodeToUtf8(zWidePath);
+ LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
+ if( !zWidePath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathW(nMax, zWidePath)==0 ){
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname2", 0);
+ }
+ zMulti = winUnicodeToUtf8(zWidePath);
if( zMulti ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
- free(zMulti);
+ sqlite3_snprintf(nMax, zBuf, "%s", zMulti);
+ sqlite3_free(zMulti);
+ sqlite3_free(zWidePath);
}else{
- return SQLITE_NOMEM;
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zUtf8;
- char zMbcsPath[MAX_PATH];
- GetTempPathA(MAX_PATH-30, zMbcsPath);
+ char *zMbcsPath = sqlite3MallocZero( nMax );
+ if( !zMbcsPath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathA(nMax, zMbcsPath)==0 ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname3", 0);
+ }
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
if( zUtf8 ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
- free(zUtf8);
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
+ sqlite3_free(zUtf8);
}else{
- return SQLITE_NOMEM;
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
}
-#endif
}
+#endif /* SQLITE_WIN32_HAS_ANSI */
+#endif /* !SQLITE_OS_WINRT */
- /* Check that the output buffer is large enough for the temporary file
- ** name. If it is not, return SQLITE_ERROR.
+ /*
+ ** Check to make sure the temporary directory ends with an appropriate
+ ** separator. If it does not and there is not enough space left to add
+ ** one, fail.
*/
- if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 17) >= nBuf ){
- return SQLITE_ERROR;
+ if( !winMakeEndInDirSep(nDir+1, zBuf) ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0);
}
- for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
- zTempPath[i] = 0;
+ /*
+ ** Check that the output buffer is large enough for the temporary file
+ ** name in the following format:
+ **
+ ** "<temporary_directory>/etilqs_XXXXXXXXXXXXXXX\0\0"
+ **
+ ** If not, return SQLITE_ERROR. The number 17 is used here in order to
+ ** account for the space used by the 15 character random suffix and the
+ ** two trailing NUL characters. The final directory separator character
+ ** has already added if it was not already present.
+ */
+ nLen = sqlite3Strlen30(zBuf);
+ if( (nLen + nPre + 17) > nBuf ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0);
+ }
+
+ sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
- sqlite3_snprintf(nBuf-17, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
+ zBuf[j+1] = 0;
+ *pzBuf = zBuf;
- OSTRACE(("TEMP FILENAME: %s\n", zBuf));
- return SQLITE_OK;
+ OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the named file is really a directory. Return false if
+** it is something other than a directory, or if there is any kind of memory
+** allocation failure.
+*/
+static int winIsDir(const void *zConverted){
+ DWORD attr;
+ int rc = 0;
+ DWORD lastErrno;
+
+ if( osIsNT() ){
+ int cnt = 0;
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData;
+ memset(&sAttrData, 0, sizeof(sAttrData));
+ while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
+ GetFileExInfoStandard,
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
+ if( !rc ){
+ return 0; /* Invalid name? */
+ }
+ attr = sAttrData.dwFileAttributes;
+#if SQLITE_OS_WINCE==0
+ }else{
+ attr = osGetFileAttributesA((char*)zConverted);
+#endif
+ }
+ return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
}
/*
** Open a file.
*/
static int winOpen(
- sqlite3_vfs *pVfs, /* Not used */
+ sqlite3_vfs *pVfs, /* Used to get maximum path name length */
const char *zName, /* Name of the file (UTF-8) */
sqlite3_file *id, /* Write the SQLite file handle here */
int flags, /* Open mode flags */
int *pOutFlags /* Status return flags */
){
HANDLE h;
+ DWORD lastErrno = 0;
DWORD dwDesiredAccess;
DWORD dwShareMode;
DWORD dwCreationDisposition;
@@ -33525,11 +37090,12 @@ static int winOpen(
winFile *pFile = (winFile*)id;
void *zConverted; /* Filename in OS encoding */
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
+ int cnt = 0;
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
- char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
+ char *zTmpname = 0; /* For temporary filename, if necessary. */
int rc = SQLITE_OK; /* Function Return Code */
#if !defined(NDEBUG) || SQLITE_OS_WINCE
@@ -33539,22 +37105,23 @@ static int winOpen(
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
int isCreate = (flags & SQLITE_OPEN_CREATE);
-#ifndef NDEBUG
int isReadonly = (flags & SQLITE_OPEN_READONLY);
-#endif
int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
#ifndef NDEBUG
int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
+ eType==SQLITE_OPEN_MASTER_JOURNAL
+ || eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
));
#endif
- /* Check the following statements are true:
+ OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n",
+ zUtf8Name, id, flags, pOutFlags));
+
+ /* Check the following statements are true:
**
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
+ ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
** (b) if CREATE is set, then READWRITE must also be set, and
** (c) if EXCLUSIVE is set, then CREATE must also be set.
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
@@ -33564,7 +37131,7 @@ static int winOpen(
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
- /* The main DB, main journal, WAL file and master journal are never
+ /* The main DB, main journal, WAL file and master journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
@@ -33572,33 +37139,56 @@ static int winOpen(
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
/* Assert that the upper layer has set one of the "file-type" flags. */
- assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
- || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
+ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
+ || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
- assert( id!=0 );
- UNUSED_PARAMETER(pVfs);
-
+ assert( pFile!=0 );
+ memset(pFile, 0, sizeof(winFile));
pFile->h = INVALID_HANDLE_VALUE;
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
+#if SQLITE_OS_WINRT
+ if( !zUtf8Name && !sqlite3_temp_directory ){
+ sqlite3_log(SQLITE_ERROR,
+ "sqlite3_temp_directory variable should be set for WinRT");
+ }
+#endif
+
+ /* If the second argument to this function is NULL, generate a
+ ** temporary file name to use
*/
if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- rc = getTempname(MAX_PATH+1, zTmpname);
+ assert( isDelete && !isOpenJournal );
+ rc = winGetTempname(pVfs, &zTmpname);
if( rc!=SQLITE_OK ){
+ OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
return rc;
}
zUtf8Name = zTmpname;
}
+ /* Database filenames are double-zero terminated if they are not
+ ** URIs with parameters. Hence, they can always be passed into
+ ** sqlite3_uri_parameter().
+ */
+ assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) ||
+ zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
+
/* Convert the filename to the system encoding. */
- zConverted = convertUtf8Filename(zUtf8Name);
+ zConverted = winConvertFromUtf8Filename(zUtf8Name);
if( zConverted==0 ){
- return SQLITE_NOMEM;
+ sqlite3_free(zTmpname);
+ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
+ return SQLITE_IOERR_NOMEM;
+ }
+
+ if( winIsDir(zConverted) ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
+ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
+ return SQLITE_CANTOPEN_ISDIR;
}
if( isReadWrite ){
@@ -33607,8 +37197,8 @@ static int winOpen(
dwDesiredAccess = GENERIC_READ;
}
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
+ /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
+ ** created. SQLite doesn't use it to indicate "exclusive access"
** as it is usually understood.
*/
if( isExclusive ){
@@ -33643,43 +37233,64 @@ static int winOpen(
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
- if( isNT() ){
- h = CreateFileW((WCHAR*)zConverted,
- dwDesiredAccess,
- dwShareMode,
- NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL
- );
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- h = CreateFileA((char*)zConverted,
- dwDesiredAccess,
- dwShareMode,
- NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL
- );
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
+ extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+ extendedParameters.dwFileAttributes =
+ dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK;
+ extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK;
+ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
+ extendedParameters.lpSecurityAttributes = NULL;
+ extendedParameters.hTemplateFile = NULL;
+ while( (h = osCreateFile2((LPCWSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ dwCreationDisposition,
+ &extendedParameters))==INVALID_HANDLE_VALUE &&
+ winRetryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
+#else
+ while( (h = osCreateFileW((LPCWSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode, NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL))==INVALID_HANDLE_VALUE &&
+ winRetryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
#endif
}
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ while( (h = osCreateFileA((LPCSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode, NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL))==INVALID_HANDLE_VALUE &&
+ winRetryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
+ }
+#endif
+ winLogIoerr(cnt);
- OSTRACE(("OPEN %d %s 0x%lx %s\n",
- h, zName, dwDesiredAccess,
- h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
+ OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name,
+ dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
if( h==INVALID_HANDLE_VALUE ){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
- free(zConverted);
- if( isReadWrite ){
- return winOpen(pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
+ sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
+ if( isReadWrite && !isExclusive ){
+ return winOpen(pVfs, zName, id,
+ ((flags|SQLITE_OPEN_READONLY) &
+ ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
+ pOutFlags);
}else{
return SQLITE_CANTOPEN_BKPT;
}
@@ -33693,30 +37304,47 @@ static int winOpen(
}
}
- memset(pFile, 0, sizeof(*pFile));
- pFile->pMethod = &winIoMethod;
- pFile->h = h;
- pFile->lastErrno = NO_ERROR;
- pFile->pVfs = pVfs;
- pFile->pShm = 0;
- pFile->zPath = zName;
- pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
+ OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, "
+ "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ?
+ *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
#if SQLITE_OS_WINCE
if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
- && !winceCreateLock(zName, pFile)
+ && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
){
- CloseHandle(h);
- free(zConverted);
- return SQLITE_CANTOPEN_BKPT;
+ osCloseHandle(h);
+ sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
+ OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
+ return rc;
}
if( isTemp ){
pFile->zDeleteOnClose = zConverted;
}else
#endif
{
- free(zConverted);
+ sqlite3_free(zConverted);
+ }
+
+ sqlite3_free(zTmpname);
+ pFile->pMethod = &winIoMethod;
+ pFile->pVfs = pVfs;
+ pFile->h = h;
+ if( isReadonly ){
+ pFile->ctrlFlags |= WINFILE_RDONLY;
+ }
+ if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
+ pFile->ctrlFlags |= WINFILE_PSOW;
}
+ pFile->lastErrno = NO_ERROR;
+ pFile->zPath = zName;
+#if SQLITE_MAX_MMAP_SIZE>0
+ pFile->hMap = NULL;
+ pFile->pMapRegion = 0;
+ pFile->mmapSize = 0;
+ pFile->mmapSizeActual = 0;
+ pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap;
+#endif
OpenCounter(+1);
return rc;
@@ -33725,7 +37353,7 @@ static int winOpen(
/*
** Delete the named file.
**
-** Note that windows does not allow a file to be deleted if some other
+** Note that Windows does not allow a file to be deleted if some other
** process has it open. Sometimes a virus scanner or indexing program
** will open a journal file shortly after it is created in order to do
** whatever it does. While this other process is holding the
@@ -33734,57 +37362,113 @@ static int winOpen(
** to MX_DELETION_ATTEMPTs deletion attempts are run before giving
** up and returning an error.
*/
-#define MX_DELETION_ATTEMPTS 5
static int winDelete(
sqlite3_vfs *pVfs, /* Not used on win32 */
const char *zFilename, /* Name of file to delete */
int syncDir /* Not used on win32 */
){
int cnt = 0;
- DWORD rc;
- DWORD error = 0;
+ int rc;
+ DWORD attr;
+ DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(syncDir);
SimulateIOError(return SQLITE_IOERR_DELETE);
- zConverted = convertUtf8Filename(zFilename);
+ OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
+
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
- return SQLITE_NOMEM;
+ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osIsNT() ){
+ do {
+#if SQLITE_OS_WINRT
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData;
+ memset(&sAttrData, 0, sizeof(sAttrData));
+ if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard,
+ &sAttrData) ){
+ attr = sAttrData.dwFileAttributes;
+ }else{
+ lastErrno = osGetLastError();
+ if( lastErrno==ERROR_FILE_NOT_FOUND
+ || lastErrno==ERROR_PATH_NOT_FOUND ){
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ break;
+ }
+#else
+ attr = osGetFileAttributesW(zConverted);
+#endif
+ if ( attr==INVALID_FILE_ATTRIBUTES ){
+ lastErrno = osGetLastError();
+ if( lastErrno==ERROR_FILE_NOT_FOUND
+ || lastErrno==ERROR_PATH_NOT_FOUND ){
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ break;
+ }
+ if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
+ rc = SQLITE_ERROR; /* Files only. */
+ break;
+ }
+ if ( osDeleteFileW(zConverted) ){
+ rc = SQLITE_OK; /* Deleted OK. */
+ break;
+ }
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
+ rc = SQLITE_ERROR; /* No more retries. */
+ break;
+ }
+ } while(1);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ do {
+ attr = osGetFileAttributesA(zConverted);
+ if ( attr==INVALID_FILE_ATTRIBUTES ){
+ lastErrno = osGetLastError();
+ if( lastErrno==ERROR_FILE_NOT_FOUND
+ || lastErrno==ERROR_PATH_NOT_FOUND ){
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ break;
+ }
+ if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
+ rc = SQLITE_ERROR; /* Files only. */
+ break;
+ }
+ if ( osDeleteFileA(zConverted) ){
+ rc = SQLITE_OK; /* Deleted OK. */
+ break;
+ }
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
+ rc = SQLITE_ERROR; /* No more retries. */
+ break;
+ }
+ } while(1);
}
- if( isNT() ){
- do{
- DeleteFileW(zConverted);
- }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES)
- || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
- && (++cnt < MX_DELETION_ATTEMPTS)
- && (Sleep(100), 1) );
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- do{
- DeleteFileA(zConverted);
- }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES)
- || ((error = GetLastError()) == ERROR_ACCESS_DENIED))
- && (++cnt < MX_DELETION_ATTEMPTS)
- && (Sleep(100), 1) );
#endif
+ if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename);
+ }else{
+ winLogIoerr(cnt);
}
- free(zConverted);
- OSTRACE(("DELETE \"%s\" %s\n", zFilename,
- ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ?
- "ok" : "failed" ));
-
- return ( (rc == INVALID_FILE_ATTRIBUTES)
- && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK :
- winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
+ sqlite3_free(zConverted);
+ OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
+ return rc;
}
/*
-** Check the existance and status of a file.
+** Check the existence and status of a file.
*/
static int winAccess(
sqlite3_vfs *pVfs, /* Not used on win32 */
@@ -33794,64 +37478,118 @@ static int winAccess(
){
DWORD attr;
int rc = 0;
+ DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
SimulateIOError( return SQLITE_IOERR_ACCESS; );
- zConverted = convertUtf8Filename(zFilename);
+ OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
+ zFilename, flags, pResOut));
+
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
- return SQLITE_NOMEM;
+ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
+ return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
+ int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
- if( GetFileAttributesExW((WCHAR*)zConverted,
- GetFileExInfoStandard,
- &sAttrData) ){
+ while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
+ GetFileExInfoStandard,
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
+ if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
*/
if( flags==SQLITE_ACCESS_EXISTS
- && sAttrData.nFileSizeHigh==0
+ && sAttrData.nFileSizeHigh==0
&& sAttrData.nFileSizeLow==0 ){
attr = INVALID_FILE_ATTRIBUTES;
}else{
attr = sAttrData.dwFileAttributes;
}
}else{
- if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
- winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename);
- free(zConverted);
- return SQLITE_IOERR_ACCESS;
+ winLogIoerr(cnt);
+ if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
+ sqlite3_free(zConverted);
+ return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
+ zFilename);
}else{
attr = INVALID_FILE_ATTRIBUTES;
}
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- attr = GetFileAttributesA((char*)zConverted);
-#endif
}
- free(zConverted);
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ attr = osGetFileAttributesA((char*)zConverted);
+ }
+#endif
+ sqlite3_free(zConverted);
switch( flags ){
case SQLITE_ACCESS_READ:
case SQLITE_ACCESS_EXISTS:
rc = attr!=INVALID_FILE_ATTRIBUTES;
break;
case SQLITE_ACCESS_READWRITE:
- rc = (attr & FILE_ATTRIBUTE_READONLY)==0;
+ rc = attr!=INVALID_FILE_ATTRIBUTES &&
+ (attr & FILE_ATTRIBUTE_READONLY)==0;
break;
default:
assert(!"Invalid flags argument");
}
*pResOut = rc;
+ OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
+ zFilename, pResOut, *pResOut));
return SQLITE_OK;
}
+/*
+** Returns non-zero if the specified path name starts with a drive letter
+** followed by a colon character.
+*/
+static BOOL winIsDriveLetterAndColon(
+ const char *zPathname
+){
+ return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
+}
+
+/*
+** Returns non-zero if the specified path name should be used verbatim. If
+** non-zero is returned from this function, the calling function must simply
+** use the provided path name verbatim -OR- resolve it into a full path name
+** using the GetFullPathName Win32 API function (if available).
+*/
+static BOOL winIsVerbatimPathname(
+ const char *zPathname
+){
+ /*
+ ** If the path name starts with a forward slash or a backslash, it is either
+ ** a legal UNC name, a volume relative path, or an absolute path name in the
+ ** "Unix" format on Windows. There is no easy way to differentiate between
+ ** the final two cases; therefore, we return the safer return value of TRUE
+ ** so that callers of this function will simply use it verbatim.
+ */
+ if ( winIsDirSep(zPathname[0]) ){
+ return TRUE;
+ }
+
+ /*
+ ** If the path name starts with a letter and a colon it is either a volume
+ ** relative path or an absolute path. Callers of this function must not
+ ** attempt to treat it as a relative path name (i.e. they should simply use
+ ** it verbatim).
+ */
+ if ( winIsDriveLetterAndColon(zPathname) ){
+ return TRUE;
+ }
+
+ /*
+ ** If we get to this point, the path name should almost certainly be a purely
+ ** relative one (i.e. not a UNC name, not absolute, and not volume relative).
+ */
+ return FALSE;
+}
/*
** Turn a relative pathname into a full pathname. Write the full
@@ -33864,31 +37602,92 @@ static int winFullPathname(
int nFull, /* Size of output buffer in bytes */
char *zFull /* Output buffer */
){
-
+
#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
UNUSED_PARAMETER(nFull);
- cygwin_conv_to_full_win32_path(zRelative, zFull);
+ assert( nFull>=pVfs->mxPathname );
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a slash.
+ */
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
+ CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname1", zRelative);
+ }else{
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
+ if( !zUtf8 ){
+ sqlite3_free(zOut);
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zOut);
+ }
+ }else{
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
+ zRelative, zOut, pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname2", zRelative);
+ }else{
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
+ if( !zUtf8 ){
+ sqlite3_free(zOut);
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zOut);
+ }
+ }
return SQLITE_OK;
#endif
-#if SQLITE_OS_WINCE
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
/* WinCE has no concept of a relative pathname, or so I am told. */
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
+ /* WinRT has no way to convert a relative path to an absolute one. */
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a backslash.
+ */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zRelative);
+ }else{
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
+ }
return SQLITE_OK;
#endif
-#if !SQLITE_OS_WINCE && !defined(__CYGWIN__)
- int nByte;
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+ DWORD nByte;
void *zConverted;
char *zOut;
/* If this path name begins with "/X:", where "X" is any alphabetic
** character, discard the initial "/" from the pathname.
*/
- if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){
+ if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
zRelative++;
}
@@ -33898,117 +37697,81 @@ static int winFullPathname(
** current working directory has been unlinked.
*/
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
- zConverted = convertUtf8Filename(zRelative);
- if( isNT() ){
- WCHAR *zTemp;
- nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
- zTemp = malloc( nByte*sizeof(zTemp[0]) );
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a backslash.
+ */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zRelative);
+ return SQLITE_OK;
+ }
+ zConverted = winConvertFromUtf8Filename(zRelative);
+ if( zConverted==0 ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osIsNT() ){
+ LPWSTR zTemp;
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname1", zRelative);
+ }
+ nByte += 3;
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
if( zTemp==0 ){
- free(zConverted);
- return SQLITE_NOMEM;
- }
- GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
- free(zConverted);
- zOut = unicodeToUtf8(zTemp);
- free(zTemp);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ sqlite3_free(zConverted);
+ return SQLITE_IOERR_NOMEM;
+ }
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zTemp);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname2", zRelative);
+ }
+ sqlite3_free(zConverted);
+ zOut = winUnicodeToUtf8(zTemp);
+ sqlite3_free(zTemp);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zTemp;
- nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
- zTemp = malloc( nByte*sizeof(zTemp[0]) );
+ nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname3", zRelative);
+ }
+ nByte += 3;
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
if( zTemp==0 ){
- free(zConverted);
- return SQLITE_NOMEM;
+ sqlite3_free(zConverted);
+ return SQLITE_IOERR_NOMEM;
}
- GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
- free(zConverted);
+ nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zTemp);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname4", zRelative);
+ }
+ sqlite3_free(zConverted);
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
- free(zTemp);
-#endif
+ sqlite3_free(zTemp);
}
+#endif
if( zOut ){
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
- free(zOut);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
+ sqlite3_free(zOut);
return SQLITE_OK;
}else{
- return SQLITE_NOMEM;
- }
-#endif
-}
-
-/*
-** Get the sector size of the device used to store
-** file.
-*/
-static int getSectorSize(
- sqlite3_vfs *pVfs,
- const char *zRelative /* UTF-8 file name */
-){
- DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
- /* GetDiskFreeSpace is not supported under WINCE */
-#if SQLITE_OS_WINCE
- UNUSED_PARAMETER(pVfs);
- UNUSED_PARAMETER(zRelative);
-#else
- char zFullpath[MAX_PATH+1];
- int rc;
- DWORD dwRet = 0;
- DWORD dwDummy;
-
- /*
- ** We need to get the full path name of the file
- ** to get the drive letter to look up the sector
- ** size.
- */
- SimulateIOErrorBenign(1);
- rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
- SimulateIOErrorBenign(0);
- if( rc == SQLITE_OK )
- {
- void *zConverted = convertUtf8Filename(zFullpath);
- if( zConverted ){
- if( isNT() ){
- /* trim path to just drive reference */
- WCHAR *p = zConverted;
- for(;*p;p++){
- if( *p == '\\' ){
- *p = '\0';
- break;
- }
- }
- dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
- &dwDummy,
- &bytesPerSector,
- &dwDummy,
- &dwDummy);
- }else{
- /* trim path to just drive reference */
- char *p = (char *)zConverted;
- for(;*p;p++){
- if( *p == '\\' ){
- *p = '\0';
- break;
- }
- }
- dwRet = GetDiskFreeSpaceA((char*)zConverted,
- &dwDummy,
- &bytesPerSector,
- &dwDummy,
- &dwDummy);
- }
- free(zConverted);
- }
- if( !dwRet ){
- bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
- }
+ return SQLITE_IOERR_NOMEM;
}
#endif
- return (int) bytesPerSector;
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
@@ -34016,49 +37779,63 @@ static int getSectorSize(
** Interfaces for opening a shared library, finding entry points
** within the shared library, and closing the shared library.
*/
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
+#if defined(__CYGWIN__)
+ int nFull = pVfs->mxPathname+1;
+ char *zFull = sqlite3MallocZero( nFull );
+ void *zConverted = 0;
+ if( zFull==0 ){
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
+ return 0;
+ }
+ if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){
+ sqlite3_free(zFull);
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
+ return 0;
+ }
+ zConverted = winConvertFromUtf8Filename(zFull);
+ sqlite3_free(zFull);
+#else
+ void *zConverted = winConvertFromUtf8Filename(zFilename);
UNUSED_PARAMETER(pVfs);
+#endif
if( zConverted==0 ){
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
return 0;
}
- if( isNT() ){
- h = LoadLibraryW((WCHAR*)zConverted);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- h = LoadLibraryA((char*)zConverted);
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
+#else
+ h = osLoadLibraryW((LPCWSTR)zConverted);
#endif
}
- free(zConverted);
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ h = osLoadLibraryA((char*)zConverted);
+ }
+#endif
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h));
+ sqlite3_free(zConverted);
return (void*)h;
}
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
UNUSED_PARAMETER(pVfs);
- getLastErrorMsg(nBuf, zBufOut);
+ winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut);
}
-void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
+static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){
+ FARPROC proc;
UNUSED_PARAMETER(pVfs);
-#if SQLITE_OS_WINCE
- /* The GetProcAddressA() routine is only available on wince. */
- return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol);
-#else
- /* All other windows platforms expect GetProcAddress() to take
- ** an Ansi string regardless of the _UNICODE setting */
- return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol);
-#endif
+ proc = osGetProcAddressA((HANDLE)pH, zSym);
+ OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n",
+ (void*)pH, zSym, (void*)proc));
+ return (void(*)(void))proc;
}
-void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
+static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
UNUSED_PARAMETER(pVfs);
- FreeLibrary((HANDLE)pHandle);
+ osFreeLibrary((HANDLE)pHandle);
+ OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle));
}
#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
#define winDlOpen 0
@@ -34080,23 +37857,31 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
#else
if( sizeof(SYSTEMTIME)<=nBuf-n ){
SYSTEMTIME x;
- GetSystemTime(&x);
+ osGetSystemTime(&x);
memcpy(&zBuf[n], &x, sizeof(x));
n += sizeof(x);
}
if( sizeof(DWORD)<=nBuf-n ){
- DWORD pid = GetCurrentProcessId();
+ DWORD pid = osGetCurrentProcessId();
memcpy(&zBuf[n], &pid, sizeof(pid));
n += sizeof(pid);
}
+#if SQLITE_OS_WINRT
+ if( sizeof(ULONGLONG)<=nBuf-n ){
+ ULONGLONG cnt = osGetTickCount64();
+ memcpy(&zBuf[n], &cnt, sizeof(cnt));
+ n += sizeof(cnt);
+ }
+#else
if( sizeof(DWORD)<=nBuf-n ){
- DWORD cnt = GetTickCount();
+ DWORD cnt = osGetTickCount();
memcpy(&zBuf[n], &cnt, sizeof(cnt));
n += sizeof(cnt);
}
+#endif
if( sizeof(LARGE_INTEGER)<=nBuf-n ){
LARGE_INTEGER i;
- QueryPerformanceCounter(&i);
+ osQueryPerformanceCounter(&i);
memcpy(&zBuf[n], &i, sizeof(i));
n += sizeof(i);
}
@@ -34109,7 +37894,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
** Sleep for a little while. Return the amount of time slept.
*/
static int winSleep(sqlite3_vfs *pVfs, int microsec){
- Sleep((microsec+999)/1000);
+ sqlite3_win32_sleep((microsec+999)/1000);
UNUSED_PARAMETER(pVfs);
return ((microsec+999)/1000)*1000;
}
@@ -34130,11 +37915,12 @@ SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
-** On success, return 0. Return 1 if the time and date cannot be found.
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
+** cannot be found.
*/
static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
- /* FILETIME structure is a 64-bit value representing the number of
- 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
+ /* FILETIME structure is a 64-bit value representing the number of
+ 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/
FILETIME ft;
static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000;
@@ -34142,22 +37928,23 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
#endif
/* 2^32 - to avoid use of LL and warnings in gcc */
- static const sqlite3_int64 max32BitValue =
- (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296;
+ static const sqlite3_int64 max32BitValue =
+ (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 +
+ (sqlite3_int64)294967296;
#if SQLITE_OS_WINCE
SYSTEMTIME time;
- GetSystemTime(&time);
+ osGetSystemTime(&time);
/* if SystemTimeToFileTime() fails, it returns zero. */
- if (!SystemTimeToFileTime(&time,&ft)){
- return 1;
+ if (!osSystemTimeToFileTime(&time,&ft)){
+ return SQLITE_ERROR;
}
#else
- GetSystemTimeAsFileTime( &ft );
+ osGetSystemTimeAsFileTime( &ft );
#endif
*piNow = winFiletimeEpoch +
- ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
+ ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
(sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000;
#ifdef SQLITE_TEST
@@ -34166,7 +37953,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
}
#endif
UNUSED_PARAMETER(pVfs);
- return 0;
+ return SQLITE_OK;
}
/*
@@ -34174,7 +37961,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
** current time and date as a Julian Day number into *prNow and
** return 0. Return 1 if the time and date cannot be found.
*/
-int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
+static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
int rc;
sqlite3_int64 i;
rc = winCurrentTimeInt64(pVfs, &i);
@@ -34186,8 +37973,8 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
/*
** The idea is that this function works like a combination of
-** GetLastError() and FormatMessage() on windows (or errno and
-** strerror_r() on unix). After an error is returned by an OS
+** GetLastError() and FormatMessage() on Windows (or errno and
+** strerror_r() on Unix). After an error is returned by an OS
** function, SQLite calls this function with zBuf pointing to
** a buffer of nBuf bytes. The OS layer should populate the
** buffer with a nul-terminated UTF-8 encoded error message
@@ -34216,11 +38003,9 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
*/
static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
UNUSED_PARAMETER(pVfs);
- return getLastErrorMsg(nBuf, zBuf);
+ return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf);
}
-
-
/*
** Initialize and deinitialize the operating system interface.
*/
@@ -34228,7 +38013,7 @@ SQLITE_API int sqlite3_os_init(void){
static sqlite3_vfs winVfs = {
3, /* iVersion */
sizeof(winFile), /* szOsFile */
- MAX_PATH, /* mxPathname */
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
0, /* pNext */
"win32", /* zName */
0, /* pAppData */
@@ -34245,22 +38030,67 @@ SQLITE_API int sqlite3_os_init(void){
winCurrentTime, /* xCurrentTime */
winGetLastError, /* xGetLastError */
winCurrentTimeInt64, /* xCurrentTimeInt64 */
- 0, /* xSetSystemCall */
- 0, /* xGetSystemCall */
- 0, /* xNextSystemCall */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
};
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ static sqlite3_vfs winLongPathVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-longpath", /* zName */
+ 0, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#endif
+
+ /* Double-check that the aSyscall[] array has been constructed
+ ** correctly. See ticket [bb3a86e890c8e96ab] */
+ assert( ArraySize(aSyscall)==77 );
-#ifndef SQLITE_OMIT_WAL
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
- GetSystemInfo(&winSysInfo);
- assert(winSysInfo.dwAllocationGranularity > 0);
+#if SQLITE_OS_WINRT
+ osGetNativeSystemInfo(&winSysInfo);
+#else
+ osGetSystemInfo(&winSysInfo);
#endif
+ assert( winSysInfo.dwAllocationGranularity>0 );
+ assert( winSysInfo.dwPageSize>0 );
sqlite3_vfs_register(&winVfs, 1);
- return SQLITE_OK;
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ sqlite3_vfs_register(&winLongPathVfs, 0);
+#endif
+
+ return SQLITE_OK;
}
-SQLITE_API int sqlite3_os_end(void){
+
+SQLITE_API int sqlite3_os_end(void){
+#if SQLITE_OS_WINRT
+ if( sleepObj!=NULL ){
+ osCloseHandle(sleepObj);
+ sleepObj = NULL;
+ }
+#endif
return SQLITE_OK;
}
@@ -34341,7 +38171,7 @@ SQLITE_API int sqlite3_os_end(void){
/*
** A bitmap is an instance of the following structure.
**
-** This bitmap records the existance of zero or more bits
+** This bitmap records the existence of zero or more bits
** with values between 1 and iSize, inclusive.
**
** There are three possible representations of the bitmap.
@@ -34609,10 +38439,9 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
/* Allocate the Bitvec to be tested and a linear array of
** bits to act as the reference */
pBitvec = sqlite3BitvecCreate( sz );
- pV = sqlite3_malloc( (sz+7)/8 + 1 );
+ pV = sqlite3MallocZero( (sz+7)/8 + 1 );
pTmpSpace = sqlite3_malloc(BITVEC_SZ);
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
- memset(pV, 0, (sz+7)/8 + 1);
/* NULL pBitvec tests */
sqlite3BitvecSet(0, 1);
@@ -34699,10 +38528,11 @@ struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */
int nRef; /* Number of referenced pages */
- int nMax; /* Configured cache size */
+ int szCache; /* Configured cache size */
int szPage; /* Size of every page in this cache */
int szExtra; /* Size of extra space for each page */
- int bPurgeable; /* True if pages are on backing store */
+ u8 bPurgeable; /* True if pages are on backing store */
+ u8 eCreate; /* eCreate value for for xFetch() */
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
void *pStress; /* Argument to xStress */
sqlite3_pcache *pCache; /* Pluggable cache module */
@@ -34723,81 +38553,71 @@ struct PCache {
/********************************** Linked List Management ********************/
-#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
-/*
-** Check that the pCache->pSynced variable is set correctly. If it
-** is not, either fail an assert or return zero. Otherwise, return
-** non-zero. This is only used in debugging builds, as follows:
-**
-** expensive_assert( pcacheCheckSynced(pCache) );
-*/
-static int pcacheCheckSynced(PCache *pCache){
- PgHdr *p;
- for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){
- assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) );
- }
- return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);
-}
-#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
+/* Allowed values for second argument to pcacheManageDirtyList() */
+#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */
+#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */
+#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */
/*
-** Remove page pPage from the list of dirty pages.
+** Manage pPage's participation on the dirty list. Bits of the addRemove
+** argument determines what operation to do. The 0x01 bit means first
+** remove pPage from the dirty list. The 0x02 means add pPage back to
+** the dirty list. Doing both moves pPage to the front of the dirty list.
*/
-static void pcacheRemoveFromDirtyList(PgHdr *pPage){
+static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
PCache *p = pPage->pCache;
- assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
- assert( pPage->pDirtyPrev || pPage==p->pDirty );
-
- /* Update the PCache1.pSynced variable if necessary. */
- if( p->pSynced==pPage ){
- PgHdr *pSynced = pPage->pDirtyPrev;
- while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
- pSynced = pSynced->pDirtyPrev;
+ if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
+ assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
+ assert( pPage->pDirtyPrev || pPage==p->pDirty );
+
+ /* Update the PCache1.pSynced variable if necessary. */
+ if( p->pSynced==pPage ){
+ PgHdr *pSynced = pPage->pDirtyPrev;
+ while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
+ pSynced = pSynced->pDirtyPrev;
+ }
+ p->pSynced = pSynced;
}
- p->pSynced = pSynced;
- }
-
- if( pPage->pDirtyNext ){
- pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
- }else{
- assert( pPage==p->pDirtyTail );
- p->pDirtyTail = pPage->pDirtyPrev;
- }
- if( pPage->pDirtyPrev ){
- pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
- }else{
- assert( pPage==p->pDirty );
- p->pDirty = pPage->pDirtyNext;
- }
- pPage->pDirtyNext = 0;
- pPage->pDirtyPrev = 0;
-
- expensive_assert( pcacheCheckSynced(p) );
-}
-
-/*
-** Add page pPage to the head of the dirty list (PCache1.pDirty is set to
-** pPage).
-*/
-static void pcacheAddToDirtyList(PgHdr *pPage){
- PCache *p = pPage->pCache;
-
- assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
-
- pPage->pDirtyNext = p->pDirty;
- if( pPage->pDirtyNext ){
- assert( pPage->pDirtyNext->pDirtyPrev==0 );
- pPage->pDirtyNext->pDirtyPrev = pPage;
- }
- p->pDirty = pPage;
- if( !p->pDirtyTail ){
- p->pDirtyTail = pPage;
+
+ if( pPage->pDirtyNext ){
+ pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
+ }else{
+ assert( pPage==p->pDirtyTail );
+ p->pDirtyTail = pPage->pDirtyPrev;
+ }
+ if( pPage->pDirtyPrev ){
+ pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
+ }else{
+ assert( pPage==p->pDirty );
+ p->pDirty = pPage->pDirtyNext;
+ if( p->pDirty==0 && p->bPurgeable ){
+ assert( p->eCreate==1 );
+ p->eCreate = 2;
+ }
+ }
+ pPage->pDirtyNext = 0;
+ pPage->pDirtyPrev = 0;
}
- if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
- p->pSynced = pPage;
+ if( addRemove & PCACHE_DIRTYLIST_ADD ){
+ assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
+
+ pPage->pDirtyNext = p->pDirty;
+ if( pPage->pDirtyNext ){
+ assert( pPage->pDirtyNext->pDirtyPrev==0 );
+ pPage->pDirtyNext->pDirtyPrev = pPage;
+ }else{
+ p->pDirtyTail = pPage;
+ if( p->bPurgeable ){
+ assert( p->eCreate==2 );
+ p->eCreate = 1;
+ }
+ }
+ p->pDirty = pPage;
+ if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
+ p->pSynced = pPage;
+ }
}
- expensive_assert( pcacheCheckSynced(p) );
}
/*
@@ -34805,12 +38625,22 @@ static void pcacheAddToDirtyList(PgHdr *pPage){
** being used for an in-memory database, this function is a no-op.
*/
static void pcacheUnpin(PgHdr *p){
- PCache *pCache = p->pCache;
- if( pCache->bPurgeable ){
+ if( p->pCache->bPurgeable ){
if( p->pgno==1 ){
- pCache->pPage1 = 0;
+ p->pCache->pPage1 = 0;
}
- sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0);
+ sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
+ }
+}
+
+/*
+** Compute the number of pages of cache requested.
+*/
+static int numberOfCachePages(PCache *p){
+ if( p->szCache>=0 ){
+ return p->szCache;
+ }else{
+ return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
}
}
@@ -34820,18 +38650,18 @@ static void pcacheUnpin(PgHdr *p){
** functions are threadsafe.
*/
SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
- if( sqlite3GlobalConfig.pcache.xInit==0 ){
+ if( sqlite3GlobalConfig.pcache2.xInit==0 ){
/* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
** built-in default page cache is used instead of the application defined
** page cache. */
sqlite3PCacheSetDefault();
}
- return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
+ return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
}
SQLITE_PRIVATE void sqlite3PcacheShutdown(void){
- if( sqlite3GlobalConfig.pcache.xShutdown ){
+ if( sqlite3GlobalConfig.pcache2.xShutdown ){
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
- sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
+ sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg);
}
}
@@ -34846,7 +38676,7 @@ SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); }
** The caller discovers how much space needs to be allocated by
** calling sqlite3PcacheSize().
*/
-SQLITE_PRIVATE void sqlite3PcacheOpen(
+SQLITE_PRIVATE int sqlite3PcacheOpen(
int szPage, /* Size of every page */
int szExtra, /* Extra space associated with each page */
int bPurgeable, /* True if pages are on backing store */
@@ -34855,141 +38685,211 @@ SQLITE_PRIVATE void sqlite3PcacheOpen(
PCache *p /* Preallocated space for the PCache */
){
memset(p, 0, sizeof(PCache));
- p->szPage = szPage;
+ p->szPage = 1;
p->szExtra = szExtra;
p->bPurgeable = bPurgeable;
+ p->eCreate = 2;
p->xStress = xStress;
p->pStress = pStress;
- p->nMax = 100;
+ p->szCache = 100;
+ return sqlite3PcacheSetPageSize(p, szPage);
}
/*
** Change the page size for PCache object. The caller must ensure that there
** are no outstanding page references when this function is called.
*/
-SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
+SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
assert( pCache->nRef==0 && pCache->pDirty==0 );
- if( pCache->pCache ){
- sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
- pCache->pCache = 0;
+ if( pCache->szPage ){
+ sqlite3_pcache *pNew;
+ pNew = sqlite3GlobalConfig.pcache2.xCreate(
+ szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable
+ );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache));
+ if( pCache->pCache ){
+ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
+ }
+ pCache->pCache = pNew;
pCache->pPage1 = 0;
+ pCache->szPage = szPage;
}
- pCache->szPage = szPage;
+ return SQLITE_OK;
}
/*
** Try to obtain a page from the cache.
+**
+** This routine returns a pointer to an sqlite3_pcache_page object if
+** such an object is already in cache, or if a new one is created.
+** This routine returns a NULL pointer if the object was not in cache
+** and could not be created.
+**
+** The createFlags should be 0 to check for existing pages and should
+** be 3 (not 1, but 3) to try to create a new page.
+**
+** If the createFlag is 0, then NULL is always returned if the page
+** is not already in the cache. If createFlag is 1, then a new page
+** is created only if that can be done without spilling dirty pages
+** and without exceeding the cache size limit.
+**
+** The caller needs to invoke sqlite3PcacheFetchFinish() to properly
+** initialize the sqlite3_pcache_page object and convert it into a
+** PgHdr object. The sqlite3PcacheFetch() and sqlite3PcacheFetchFinish()
+** routines are split this way for performance reasons. When separated
+** they can both (usually) operate without having to push values to
+** the stack on entry and pop them back off on exit, which saves a
+** lot of pushing and popping.
*/
-SQLITE_PRIVATE int sqlite3PcacheFetch(
+SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
PCache *pCache, /* Obtain the page from this cache */
Pgno pgno, /* Page number to obtain */
- int createFlag, /* If true, create page if it does not exist already */
- PgHdr **ppPage /* Write the page here */
+ int createFlag /* If true, create page if it does not exist already */
){
- PgHdr *pPage = 0;
int eCreate;
assert( pCache!=0 );
- assert( createFlag==1 || createFlag==0 );
+ assert( pCache->pCache!=0 );
+ assert( createFlag==3 || createFlag==0 );
assert( pgno>0 );
- /* If the pluggable cache (sqlite3_pcache*) has not been allocated,
- ** allocate it now.
+ /* eCreate defines what to do if the page does not exist.
+ ** 0 Do not allocate a new page. (createFlag==0)
+ ** 1 Allocate a new page if doing so is inexpensive.
+ ** (createFlag==1 AND bPurgeable AND pDirty)
+ ** 2 Allocate a new page even it doing so is difficult.
+ ** (createFlag==1 AND !(bPurgeable AND pDirty)
*/
- if( !pCache->pCache && createFlag ){
- sqlite3_pcache *p;
- int nByte;
- nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr);
- p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable);
- if( !p ){
- return SQLITE_NOMEM;
- }
- sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax);
- pCache->pCache = p;
- }
+ eCreate = createFlag & pCache->eCreate;
+ assert( eCreate==0 || eCreate==1 || eCreate==2 );
+ assert( createFlag==0 || pCache->eCreate==eCreate );
+ assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
+ return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
+}
- eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
- if( pCache->pCache ){
- pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate);
- }
+/*
+** If the sqlite3PcacheFetch() routine is unable to allocate a new
+** page because new clean pages are available for reuse and the cache
+** size limit has been reached, then this routine can be invoked to
+** try harder to allocate a page. This routine might invoke the stress
+** callback to spill dirty pages to the journal. It will then try to
+** allocate the new page and will only fail to allocate a new page on
+** an OOM error.
+**
+** This routine should be invoked only after sqlite3PcacheFetch() fails.
+*/
+SQLITE_PRIVATE int sqlite3PcacheFetchStress(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number to obtain */
+ sqlite3_pcache_page **ppPage /* Write result here */
+){
+ PgHdr *pPg;
+ if( pCache->eCreate==2 ) return 0;
- if( !pPage && eCreate==1 ){
- PgHdr *pPg;
- /* Find a dirty page to write-out and recycle. First try to find a
- ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
- ** cleared), but if that is not possible settle for any other
- ** unreferenced dirty page.
- */
- expensive_assert( pcacheCheckSynced(pCache) );
- for(pPg=pCache->pSynced;
- pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
- pPg=pPg->pDirtyPrev
- );
- pCache->pSynced = pPg;
- if( !pPg ){
- for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
- }
- if( pPg ){
- int rc;
+ /* Find a dirty page to write-out and recycle. First try to find a
+ ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
+ ** cleared), but if that is not possible settle for any other
+ ** unreferenced dirty page.
+ */
+ for(pPg=pCache->pSynced;
+ pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
+ pPg=pPg->pDirtyPrev
+ );
+ pCache->pSynced = pPg;
+ if( !pPg ){
+ for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev);
+ }
+ if( pPg ){
+ int rc;
#ifdef SQLITE_LOG_CACHE_SPILL
- sqlite3_log(SQLITE_FULL,
- "spill page %d making room for %d - cache used: %d/%d",
- pPg->pgno, pgno,
- sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
- pCache->nMax);
-#endif
- rc = pCache->xStress(pCache->pStress, pPg);
- if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
- return rc;
- }
+ sqlite3_log(SQLITE_FULL,
+ "spill page %d making room for %d - cache used: %d/%d",
+ pPg->pgno, pgno,
+ sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
+ numberOfCachePages(pCache));
+#endif
+ rc = pCache->xStress(pCache->pStress, pPg);
+ if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
+ return rc;
}
-
- pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2);
}
+ *ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
+ return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK;
+}
- if( pPage ){
- if( !pPage->pData ){
- memset(pPage, 0, sizeof(PgHdr));
- pPage->pData = (void *)&pPage[1];
- pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage];
- memset(pPage->pExtra, 0, pCache->szExtra);
- pPage->pCache = pCache;
- pPage->pgno = pgno;
- }
- assert( pPage->pCache==pCache );
- assert( pPage->pgno==pgno );
- assert( pPage->pData==(void *)&pPage[1] );
- assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] );
-
- if( 0==pPage->nRef ){
- pCache->nRef++;
- }
- pPage->nRef++;
- if( pgno==1 ){
- pCache->pPage1 = pPage;
- }
+/*
+** This is a helper routine for sqlite3PcacheFetchFinish()
+**
+** In the uncommon case where the page being fetched has not been
+** initialized, this routine is invoked to do the initialization.
+** This routine is broken out into a separate function since it
+** requires extra stack manipulation that can be avoided in the common
+** case.
+*/
+static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number obtained */
+ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
+){
+ PgHdr *pPgHdr;
+ assert( pPage!=0 );
+ pPgHdr = (PgHdr*)pPage->pExtra;
+ assert( pPgHdr->pPage==0 );
+ memset(pPgHdr, 0, sizeof(PgHdr));
+ pPgHdr->pPage = pPage;
+ pPgHdr->pData = pPage->pBuf;
+ pPgHdr->pExtra = (void *)&pPgHdr[1];
+ memset(pPgHdr->pExtra, 0, pCache->szExtra);
+ pPgHdr->pCache = pCache;
+ pPgHdr->pgno = pgno;
+ return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
+}
+
+/*
+** This routine converts the sqlite3_pcache_page object returned by
+** sqlite3PcacheFetch() into an initialized PgHdr object. This routine
+** must be called after sqlite3PcacheFetch() in order to get a usable
+** result.
+*/
+SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number obtained */
+ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
+){
+ PgHdr *pPgHdr;
+
+ if( pPage==0 ) return 0;
+ pPgHdr = (PgHdr *)pPage->pExtra;
+
+ if( !pPgHdr->pPage ){
+ return pcacheFetchFinishWithInit(pCache, pgno, pPage);
}
- *ppPage = pPage;
- return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
+ if( 0==pPgHdr->nRef ){
+ pCache->nRef++;
+ }
+ pPgHdr->nRef++;
+ if( pgno==1 ){
+ pCache->pPage1 = pPgHdr;
+ }
+ return pPgHdr;
}
/*
** Decrement the reference count on a page. If the page is clean and the
-** reference count drops to 0, then it is made elible for recycling.
+** reference count drops to 0, then it is made eligible for recycling.
*/
-SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr *p){
+SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
assert( p->nRef>0 );
p->nRef--;
if( p->nRef==0 ){
- PCache *pCache = p->pCache;
- pCache->nRef--;
+ p->pCache->nRef--;
if( (p->flags&PGHDR_DIRTY)==0 ){
pcacheUnpin(p);
- }else{
+ }else if( p->pDirtyPrev!=0 ){
/* Move the page to the head of the dirty list. */
- pcacheRemoveFromDirtyList(p);
- pcacheAddToDirtyList(p);
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
}
}
}
@@ -35008,17 +38908,15 @@ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
** page pointed to by p is invalid.
*/
SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
- PCache *pCache;
assert( p->nRef==1 );
if( p->flags&PGHDR_DIRTY ){
- pcacheRemoveFromDirtyList(p);
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
}
- pCache = p->pCache;
- pCache->nRef--;
+ p->pCache->nRef--;
if( p->pgno==1 ){
- pCache->pPage1 = 0;
+ p->pCache->pPage1 = 0;
}
- sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1);
+ sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1);
}
/*
@@ -35030,7 +38928,7 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
assert( p->nRef>0 );
if( 0==(p->flags & PGHDR_DIRTY) ){
p->flags |= PGHDR_DIRTY;
- pcacheAddToDirtyList( p);
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
}
}
@@ -35040,7 +38938,7 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
*/
SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
if( (p->flags & PGHDR_DIRTY) ){
- pcacheRemoveFromDirtyList(p);
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC);
if( p->nRef==0 ){
pcacheUnpin(p);
@@ -35076,11 +38974,10 @@ SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
PCache *pCache = p->pCache;
assert( p->nRef>0 );
assert( newPgno>0 );
- sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno);
+ sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
p->pgno = newPgno;
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
- pcacheRemoveFromDirtyList(p);
- pcacheAddToDirtyList(p);
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
}
}
@@ -35113,7 +39010,7 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
memset(pCache->pPage1->pData, 0, pCache->szPage);
pgno = 1;
}
- sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1);
+ sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
}
}
@@ -35121,9 +39018,8 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
** Close a cache.
*/
SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){
- if( pCache->pCache ){
- sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
- }
+ assert( pCache->pCache!=0 );
+ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
}
/*
@@ -35232,11 +39128,8 @@ SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){
** Return the total number of pages in the cache.
*/
SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
- int nPage = 0;
- if( pCache->pCache ){
- nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache);
- }
- return nPage;
+ assert( pCache->pCache!=0 );
+ return sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache);
}
#ifdef SQLITE_TEST
@@ -35244,7 +39137,7 @@ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
** Get the suggested cache-size value.
*/
SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){
- return pCache->nMax;
+ return numberOfCachePages(pCache);
}
#endif
@@ -35252,10 +39145,18 @@ SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){
** Set the suggested cache-size value.
*/
SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
- pCache->nMax = mxPage;
- if( pCache->pCache ){
- sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage);
- }
+ assert( pCache->pCache!=0 );
+ pCache->szCache = mxPage;
+ sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache,
+ numberOfCachePages(pCache));
+}
+
+/*
+** Free up as much memory as possible from the page cache.
+*/
+SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){
+ assert( pCache->pCache!=0 );
+ sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache);
}
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
@@ -35289,7 +39190,7 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd
** This file implements the default page cache implementation (the
** sqlite3_pcache interface). It also contains part of the implementation
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
-** If the default page cache implementation is overriden, then neither of
+** If the default page cache implementation is overridden, then neither of
** these two features are available.
*/
@@ -35300,7 +39201,7 @@ typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
-** of one or more PCaches that are able to recycle each others unpinned
+** of one or more PCaches that are able to recycle each other's unpinned
** pages when they are under memory pressure. A PGroup is an instance of
** the following object.
**
@@ -35315,7 +39216,7 @@ typedef struct PGroup PGroup;
** Mode 1 uses more memory (since PCache instances are not able to rob
** unused pages from other PCaches) but it also operates without a mutex,
** and is therefore often faster. Mode 2 requires a mutex in order to be
-** threadsafe, but is able recycle pages more efficient.
+** threadsafe, but recycles pages more efficiently.
**
** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single
** PGroup which is the pcache1.grp global variable and its mutex is
@@ -35323,10 +39224,10 @@ typedef struct PGroup PGroup;
*/
struct PGroup {
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */
- int nMaxPage; /* Sum of nMax for purgeable caches */
- int nMinPage; /* Sum of nMin for purgeable caches */
- int mxPinned; /* nMaxpage + 10 - nMinPage */
- int nCurrentPage; /* Number of purgeable pages allocated */
+ unsigned int nMaxPage; /* Sum of nMax for purgeable caches */
+ unsigned int nMinPage; /* Sum of nMin for purgeable caches */
+ unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
+ unsigned int nCurrentPage; /* Number of purgeable pages allocated */
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
};
@@ -35341,15 +39242,17 @@ struct PGroup {
struct PCache1 {
/* Cache configuration parameters. Page size (szPage) and the purgeable
** flag (bPurgeable) are set when the cache is created. nMax may be
- ** modified at any time by a call to the pcache1CacheSize() method.
+ ** modified at any time by a call to the pcache1Cachesize() method.
** The PGroup mutex must be held when accessing nMax.
*/
PGroup *pGroup; /* PGroup this cache belongs to */
int szPage; /* Size of allocated pages in bytes */
+ int szExtra; /* Size of extra space in bytes */
int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */
unsigned int n90pct; /* nMax*9/10 */
+ unsigned int iMaxKey; /* Largest key seen since xTruncate() */
/* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the PGroup mutex.
@@ -35358,18 +39261,18 @@ struct PCache1 {
unsigned int nPage; /* Total number of pages in apHash */
unsigned int nHash; /* Number of slots in apHash[] */
PgHdr1 **apHash; /* Hash table for fast lookup by key */
-
- unsigned int iMaxKey; /* Largest key seen since xTruncate() */
};
/*
** Each cache entry is represented by an instance of the following
-** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
-** directly before this structure in memory (see the PGHDR1_TO_PAGE()
-** macro below).
+** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
+** PgHdr1.pCache->szPage bytes is allocated directly before this structure
+** in memory.
*/
struct PgHdr1 {
+ sqlite3_pcache_page page;
unsigned int iKey; /* Key value (page number) */
+ u8 isPinned; /* Page in use, not on the LRU list */
PgHdr1 *pNext; /* Next in hash table chain */
PCache1 *pCache; /* Cache that currently owns this page */
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
@@ -35402,8 +39305,8 @@ static SQLITE_WSD struct PCacheGlobal {
void *pStart, *pEnd; /* Bounds of pagecache malloc range */
/* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
- int nFreeSlot; /* Number of unused pcache slots */
PgFreeslot *pFree; /* Free page blocks */
+ int nFreeSlot; /* Number of unused pcache slots */
/* The following value requires a mutex to change. We skip the mutex on
** reading because (1) most platforms read a 32-bit integer atomically and
** (2) even if an incorrect value is read, no great harm is done since this
@@ -35419,21 +39322,6 @@ static SQLITE_WSD struct PCacheGlobal {
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
/*
-** When a PgHdr1 structure is allocated, the associated PCache1.szPage
-** bytes of data are located directly before it in memory (i.e. the total
-** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
-** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
-** an argument and returns a pointer to the associated block of szPage
-** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
-** a pointer to a block of szPage bytes of data and the return value is
-** a pointer to the associated PgHdr1 structure.
-**
-** assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
-*/
-#define PGHDR1_TO_PAGE(p) (void*)(((char*)p) - p->pCache->szPage)
-#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
-
-/*
** Macros to enter and leave the PCache LRU mutex.
*/
#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
@@ -35501,12 +39389,14 @@ static void *pcache1Alloc(int nByte){
** it from sqlite3Malloc instead.
*/
p = sqlite3Malloc(nByte);
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
+#endif
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
}
return p;
@@ -35515,8 +39405,9 @@ static void *pcache1Alloc(int nByte){
/*
** Free an allocated buffer obtained from pcache1Alloc().
*/
-static void pcache1Free(void *p){
- if( p==0 ) return;
+static int pcache1Free(void *p){
+ int nFreed = 0;
+ if( p==0 ) return 0;
if( p>=pcache1.pStart && p<pcache1.pEnd ){
PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex);
@@ -35529,15 +39420,17 @@ static void pcache1Free(void *p){
assert( pcache1.nFreeSlot<=pcache1.nSlot );
sqlite3_mutex_leave(pcache1.mutex);
}else{
- int iSize;
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- iSize = sqlite3MallocSize(p);
+ nFreed = sqlite3MallocSize(p);
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
+ sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -nFreed);
sqlite3_mutex_leave(pcache1.mutex);
+#endif
sqlite3_free(p);
}
+ return nFreed;
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
@@ -35562,18 +39455,37 @@ static int pcache1MemSize(void *p){
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
- int nByte = sizeof(PgHdr1) + pCache->szPage;
- void *pPg = pcache1Alloc(nByte);
- PgHdr1 *p;
+ PgHdr1 *p = 0;
+ void *pPg;
+
+ /* The group mutex must be released before pcache1Alloc() is called. This
+ ** is because it may call sqlite3_release_memory(), which assumes that
+ ** this mutex is not held. */
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
+ pcache1LeaveMutex(pCache->pGroup);
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER
+ pPg = pcache1Alloc(pCache->szPage);
+ p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
+ if( !pPg || !p ){
+ pcache1Free(pPg);
+ sqlite3_free(p);
+ pPg = 0;
+ }
+#else
+ pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra);
+ p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
+#endif
+ pcache1EnterMutex(pCache->pGroup);
+
if( pPg ){
- p = PAGE_TO_PGHDR1(pCache, pPg);
+ p->page.pBuf = pPg;
+ p->page.pExtra = &p[1];
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage++;
}
- }else{
- p = 0;
+ return p;
}
- return p;
+ return 0;
}
/*
@@ -35586,10 +39498,14 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
static void pcache1FreePage(PgHdr1 *p){
if( ALWAYS(p) ){
PCache1 *pCache = p->pCache;
+ assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ pcache1Free(p->page.pBuf);
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER
+ sqlite3_free(p);
+#endif
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage--;
}
- pcache1Free(PGHDR1_TO_PAGE(p));
}
}
@@ -35621,13 +39537,13 @@ SQLITE_PRIVATE void sqlite3PageFree(void *p){
** for all page cache needs and we should not need to spill the
** allocation onto the heap.
**
-** Or, the heap is used for all page cache memory put the heap is
+** Or, the heap is used for all page cache memory but the heap is
** under memory pressure, then again it is desirable to avoid
** allocating a new page cache entry in order to avoid stressing
** the heap even further.
*/
static int pcache1UnderMemoryPressure(PCache1 *pCache){
- if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){
+ if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
return pcache1.bUnderPressure;
}else{
return sqlite3HeapNearlyFull();
@@ -35643,7 +39559,7 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){
**
** The PCache mutex must be held when this function is called.
*/
-static int pcache1ResizeHash(PCache1 *p){
+static void pcache1ResizeHash(PCache1 *p){
PgHdr1 **apNew;
unsigned int nNew;
unsigned int i;
@@ -35657,11 +39573,10 @@ static int pcache1ResizeHash(PCache1 *p){
pcache1LeaveMutex(p->pGroup);
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
- apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
+ apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew);
if( p->nHash ){ sqlite3EndBenignMalloc(); }
pcache1EnterMutex(p->pGroup);
if( apNew ){
- memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
for(i=0; i<p->nHash; i++){
PgHdr1 *pPage;
PgHdr1 *pNext = p->apHash[i];
@@ -35676,8 +39591,6 @@ static int pcache1ResizeHash(PCache1 *p){
p->apHash = apNew;
p->nHash = nNew;
}
-
- return (p->apHash ? SQLITE_OK : SQLITE_NOMEM);
}
/*
@@ -35686,34 +39599,32 @@ static int pcache1ResizeHash(PCache1 *p){
** LRU list, then this function is a no-op.
**
** The PGroup mutex must be held when this function is called.
-**
-** If pPage is NULL then this routine is a no-op.
*/
static void pcache1PinPage(PgHdr1 *pPage){
PCache1 *pCache;
PGroup *pGroup;
- if( pPage==0 ) return;
+ assert( pPage!=0 );
+ assert( pPage->isPinned==0 );
pCache = pPage->pCache;
pGroup = pCache->pGroup;
+ assert( pPage->pLruNext || pPage==pGroup->pLruTail );
+ assert( pPage->pLruPrev || pPage==pGroup->pLruHead );
assert( sqlite3_mutex_held(pGroup->mutex) );
- if( pPage->pLruNext || pPage==pGroup->pLruTail ){
- if( pPage->pLruPrev ){
- pPage->pLruPrev->pLruNext = pPage->pLruNext;
- }
- if( pPage->pLruNext ){
- pPage->pLruNext->pLruPrev = pPage->pLruPrev;
- }
- if( pGroup->pLruHead==pPage ){
- pGroup->pLruHead = pPage->pLruNext;
- }
- if( pGroup->pLruTail==pPage ){
- pGroup->pLruTail = pPage->pLruPrev;
- }
- pPage->pLruNext = 0;
- pPage->pLruPrev = 0;
- pPage->pCache->nRecyclable--;
+ if( pPage->pLruPrev ){
+ pPage->pLruPrev->pLruNext = pPage->pLruNext;
+ }else{
+ pGroup->pLruHead = pPage->pLruNext;
}
+ if( pPage->pLruNext ){
+ pPage->pLruNext->pLruPrev = pPage->pLruPrev;
+ }else{
+ pGroup->pLruTail = pPage->pLruPrev;
+ }
+ pPage->pLruNext = 0;
+ pPage->pLruPrev = 0;
+ pPage->isPinned = 1;
+ pCache->nRecyclable--;
}
@@ -35745,6 +39656,7 @@ static void pcache1EnforceMaxPage(PGroup *pGroup){
while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
PgHdr1 *p = pGroup->pLruTail;
assert( p->pCache->pGroup==pGroup );
+ assert( p->isPinned==0 );
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
@@ -35772,7 +39684,7 @@ static void pcache1TruncateUnsafe(
if( pPage->iKey>=iLimit ){
pCache->nPage--;
*pp = pPage->pNext;
- pcache1PinPage(pPage);
+ if( !pPage->isPinned ) pcache1PinPage(pPage);
pcache1FreePage(pPage);
}else{
pp = &pPage->pNext;
@@ -35813,18 +39725,21 @@ static void pcache1Shutdown(void *NotUsed){
memset(&pcache1, 0, sizeof(pcache1));
}
+/* forward declaration */
+static void pcache1Destroy(sqlite3_pcache *p);
+
/*
** Implementation of the sqlite3_pcache.xCreate method.
**
** Allocate a new cache.
*/
-static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
+static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
PCache1 *pCache; /* The newly created page cache */
PGroup *pGroup; /* The group the new page cache will belong to */
int sz; /* Bytes of memory required to allocate the new cache */
/*
- ** The seperateCache variable is true if each PCache has its own private
+ ** The separateCache variable is true if each PCache has its own private
** PGroup. In other words, separateCache is true for mode (1) where no
** mutexing is required.
**
@@ -35841,10 +39756,12 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
#endif
+ assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
+ assert( szExtra < 300 );
+
sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
- pCache = (PCache1 *)sqlite3_malloc(sz);
+ pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){
- memset(pCache, 0, sz);
if( separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
@@ -35853,13 +39770,19 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
}
pCache->pGroup = pGroup;
pCache->szPage = szPage;
+ pCache->szExtra = szExtra;
pCache->bPurgeable = (bPurgeable ? 1 : 0);
+ pcache1EnterMutex(pGroup);
+ pcache1ResizeHash(pCache);
if( bPurgeable ){
pCache->nMin = 10;
- pcache1EnterMutex(pGroup);
pGroup->nMinPage += pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
- pcache1LeaveMutex(pGroup);
+ }
+ pcache1LeaveMutex(pGroup);
+ if( pCache->nHash==0 ){
+ pcache1Destroy((sqlite3_pcache*)pCache);
+ pCache = 0;
}
}
return (sqlite3_pcache *)pCache;
@@ -35885,6 +39808,25 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
}
/*
+** Implementation of the sqlite3_pcache.xShrink method.
+**
+** Free up as much memory as possible.
+*/
+static void pcache1Shrink(sqlite3_pcache *p){
+ PCache1 *pCache = (PCache1*)p;
+ if( pCache->bPurgeable ){
+ PGroup *pGroup = pCache->pGroup;
+ int savedMaxPage;
+ pcache1EnterMutex(pGroup);
+ savedMaxPage = pGroup->nMaxPage;
+ pGroup->nMaxPage = 0;
+ pcache1EnforceMaxPage(pGroup);
+ pGroup->nMaxPage = savedMaxPage;
+ pcache1LeaveMutex(pGroup);
+ }
+}
+
+/*
** Implementation of the sqlite3_pcache.xPagecount method.
*/
static int pcache1Pagecount(sqlite3_pcache *p){
@@ -35896,6 +39838,95 @@ static int pcache1Pagecount(sqlite3_pcache *p){
return n;
}
+
+/*
+** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described
+** in the header of the pcache1Fetch() procedure.
+**
+** This steps are broken out into a separate procedure because they are
+** usually not needed, and by avoiding the stack initialization required
+** for these steps, the main pcache1Fetch() procedure can run faster.
+*/
+static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
+ PCache1 *pCache,
+ unsigned int iKey,
+ int createFlag
+){
+ unsigned int nPinned;
+ PGroup *pGroup = pCache->pGroup;
+ PgHdr1 *pPage = 0;
+
+ /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
+ assert( pCache->nPage >= pCache->nRecyclable );
+ nPinned = pCache->nPage - pCache->nRecyclable;
+ assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
+ assert( pCache->n90pct == pCache->nMax*9/10 );
+ if( createFlag==1 && (
+ nPinned>=pGroup->mxPinned
+ || nPinned>=pCache->n90pct
+ || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned)
+ )){
+ return 0;
+ }
+
+ if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache);
+ assert( pCache->nHash>0 && pCache->apHash );
+
+ /* Step 4. Try to recycle a page. */
+ if( pCache->bPurgeable && pGroup->pLruTail && (
+ (pCache->nPage+1>=pCache->nMax)
+ || pGroup->nCurrentPage>=pGroup->nMaxPage
+ || pcache1UnderMemoryPressure(pCache)
+ )){
+ PCache1 *pOther;
+ pPage = pGroup->pLruTail;
+ assert( pPage->isPinned==0 );
+ pcache1RemoveFromHash(pPage);
+ pcache1PinPage(pPage);
+ pOther = pPage->pCache;
+
+ /* We want to verify that szPage and szExtra are the same for pOther
+ ** and pCache. Assert that we can verify this by comparing sums. */
+ assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 );
+ assert( pCache->szExtra<512 );
+ assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 );
+ assert( pOther->szExtra<512 );
+
+ if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){
+ pcache1FreePage(pPage);
+ pPage = 0;
+ }else{
+ pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable);
+ }
+ }
+
+ /* Step 5. If a usable page buffer has still not been found,
+ ** attempt to allocate a new one.
+ */
+ if( !pPage ){
+ if( createFlag==1 ) sqlite3BeginBenignMalloc();
+ pPage = pcache1AllocPage(pCache);
+ if( createFlag==1 ) sqlite3EndBenignMalloc();
+ }
+
+ if( pPage ){
+ unsigned int h = iKey % pCache->nHash;
+ pCache->nPage++;
+ pPage->iKey = iKey;
+ pPage->pNext = pCache->apHash[h];
+ pPage->pCache = pCache;
+ pPage->pLruPrev = 0;
+ pPage->pLruNext = 0;
+ pPage->isPinned = 1;
+ *(void **)pPage->page.pExtra = 0;
+ pCache->apHash[h] = pPage;
+ if( iKey>pCache->iMaxKey ){
+ pCache->iMaxKey = iKey;
+ }
+ }
+ return pPage;
+}
+
/*
** Implementation of the sqlite3_pcache.xFetch method.
**
@@ -35909,7 +39940,7 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** For a non-purgeable cache (a cache used as the storage for an in-memory
** database) there is really no difference between createFlag 1 and 2. So
** the calling function (pcache.c) will never have a createFlag of 1 on
-** a non-purgable cache.
+** a non-purgeable cache.
**
** There are three different approaches to obtaining space for a page,
** depending on the value of parameter createFlag (which may be 0, 1 or 2).
@@ -35950,107 +39981,36 @@ static int pcache1Pagecount(sqlite3_pcache *p){
**
** 5. Otherwise, allocate and return a new page buffer.
*/
-static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
- int nPinned;
+static sqlite3_pcache_page *pcache1Fetch(
+ sqlite3_pcache *p,
+ unsigned int iKey,
+ int createFlag
+){
PCache1 *pCache = (PCache1 *)p;
- PGroup *pGroup;
PgHdr1 *pPage = 0;
+ assert( offsetof(PgHdr1,page)==0 );
assert( pCache->bPurgeable || createFlag!=1 );
assert( pCache->bPurgeable || pCache->nMin==0 );
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
assert( pCache->nMin==0 || pCache->bPurgeable );
- pcache1EnterMutex(pGroup = pCache->pGroup);
+ assert( pCache->nHash>0 );
+ pcache1EnterMutex(pCache->pGroup);
/* Step 1: Search the hash table for an existing entry. */
- if( pCache->nHash>0 ){
- unsigned int h = iKey % pCache->nHash;
- for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext);
- }
+ pPage = pCache->apHash[iKey % pCache->nHash];
+ while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; }
/* Step 2: Abort if no existing page is found and createFlag is 0 */
- if( pPage || createFlag==0 ){
- pcache1PinPage(pPage);
- goto fetch_out;
- }
-
- /* The pGroup local variable will normally be initialized by the
- ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined,
- ** then pcache1EnterMutex() is a no-op, so we have to initialize the
- ** local variable here. Delaying the initialization of pGroup is an
- ** optimization: The common case is to exit the module before reaching
- ** this point.
- */
-#ifdef SQLITE_MUTEX_OMIT
- pGroup = pCache->pGroup;
-#endif
-
-
- /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
- nPinned = pCache->nPage - pCache->nRecyclable;
- assert( nPinned>=0 );
- assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
- assert( pCache->n90pct == pCache->nMax*9/10 );
- if( createFlag==1 && (
- nPinned>=pGroup->mxPinned
- || nPinned>=(int)pCache->n90pct
- || pcache1UnderMemoryPressure(pCache)
- )){
- goto fetch_out;
- }
-
- if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){
- goto fetch_out;
- }
-
- /* Step 4. Try to recycle a page. */
- if( pCache->bPurgeable && pGroup->pLruTail && (
- (pCache->nPage+1>=pCache->nMax)
- || pGroup->nCurrentPage>=pGroup->nMaxPage
- || pcache1UnderMemoryPressure(pCache)
- )){
- PCache1 *pOtherCache;
- pPage = pGroup->pLruTail;
- pcache1RemoveFromHash(pPage);
- pcache1PinPage(pPage);
- if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
- pcache1FreePage(pPage);
- pPage = 0;
- }else{
- pGroup->nCurrentPage -=
- (pOtherCache->bPurgeable - pCache->bPurgeable);
- }
- }
-
- /* Step 5. If a usable page buffer has still not been found,
- ** attempt to allocate a new one.
- */
- if( !pPage ){
- if( createFlag==1 ) sqlite3BeginBenignMalloc();
- pcache1LeaveMutex(pGroup);
- pPage = pcache1AllocPage(pCache);
- pcache1EnterMutex(pGroup);
- if( createFlag==1 ) sqlite3EndBenignMalloc();
- }
-
if( pPage ){
- unsigned int h = iKey % pCache->nHash;
- pCache->nPage++;
- pPage->iKey = iKey;
- pPage->pNext = pCache->apHash[h];
- pPage->pCache = pCache;
- pPage->pLruPrev = 0;
- pPage->pLruNext = 0;
- *(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
- pCache->apHash[h] = pPage;
+ if( !pPage->isPinned ) pcache1PinPage(pPage);
+ }else if( createFlag ){
+ /* Steps 3, 4, and 5 implemented by this subroutine */
+ pPage = pcache1FetchStage2(pCache, iKey, createFlag);
}
-
-fetch_out:
- if( pPage && iKey>pCache->iMaxKey ){
- pCache->iMaxKey = iKey;
- }
- pcache1LeaveMutex(pGroup);
- return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
+ assert( pPage==0 || pCache->iMaxKey>=iKey );
+ pcache1LeaveMutex(pCache->pGroup);
+ return (sqlite3_pcache_page*)pPage;
}
@@ -36059,9 +40019,13 @@ fetch_out:
**
** Mark a page as unpinned (eligible for asynchronous recycling).
*/
-static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
+static void pcache1Unpin(
+ sqlite3_pcache *p,
+ sqlite3_pcache_page *pPg,
+ int reuseUnlikely
+){
PCache1 *pCache = (PCache1 *)p;
- PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
+ PgHdr1 *pPage = (PgHdr1 *)pPg;
PGroup *pGroup = pCache->pGroup;
assert( pPage->pCache==pCache );
@@ -36072,6 +40036,7 @@ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
*/
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
+ assert( pPage->isPinned==1 );
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
pcache1RemoveFromHash(pPage);
@@ -36087,6 +40052,7 @@ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
pGroup->pLruHead = pPage;
}
pCache->nRecyclable++;
+ pPage->isPinned = 0;
}
pcache1LeaveMutex(pCache->pGroup);
@@ -36097,12 +40063,12 @@ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
*/
static void pcache1Rekey(
sqlite3_pcache *p,
- void *pPg,
+ sqlite3_pcache_page *pPg,
unsigned int iOld,
unsigned int iNew
){
PCache1 *pCache = (PCache1 *)p;
- PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
+ PgHdr1 *pPage = (PgHdr1 *)pPg;
PgHdr1 **pp;
unsigned int h;
assert( pPage->iKey==iOld );
@@ -36156,7 +40122,9 @@ static void pcache1Destroy(sqlite3_pcache *p){
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
pcache1EnterMutex(pGroup);
pcache1TruncateUnsafe(pCache, 0);
+ assert( pGroup->nMaxPage >= pCache->nMax );
pGroup->nMaxPage -= pCache->nMax;
+ assert( pGroup->nMinPage >= pCache->nMin );
pGroup->nMinPage -= pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1EnforceMaxPage(pGroup);
@@ -36171,7 +40139,8 @@ static void pcache1Destroy(sqlite3_pcache *p){
** already provided an alternative.
*/
SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){
- static const sqlite3_pcache_methods defaultMethods = {
+ static const sqlite3_pcache_methods2 defaultMethods = {
+ 1, /* iVersion */
0, /* pArg */
pcache1Init, /* xInit */
pcache1Shutdown, /* xShutdown */
@@ -36182,9 +40151,10 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){
pcache1Unpin, /* xUnpin */
pcache1Rekey, /* xRekey */
pcache1Truncate, /* xTruncate */
- pcache1Destroy /* xDestroy */
+ pcache1Destroy, /* xDestroy */
+ pcache1Shrink /* xShrink */
};
- sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods);
+ sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
@@ -36205,7 +40175,11 @@ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
PgHdr1 *p;
pcache1EnterMutex(&pcache1.grp);
while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
- nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
+ nFree += pcache1MemSize(p->page.pBuf);
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER
+ nFree += sqlite3MemSize(p);
+#endif
+ assert( p->isPinned==0 );
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
@@ -36230,11 +40204,12 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
PgHdr1 *p;
int nRecyclable = 0;
for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
+ assert( p->isPinned==0 );
nRecyclable++;
}
*pnCurrent = pcache1.grp.nCurrentPage;
- *pnMax = pcache1.grp.nMaxPage;
- *pnMin = pcache1.grp.nMinPage;
+ *pnMax = (int)pcache1.grp.nMaxPage;
+ *pnMin = (int)pcache1.grp.nMinPage;
*pnRecyclable = nRecyclable;
}
#endif
@@ -36293,7 +40268,7 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
** No INSERTs may occurs after a SMALLEST. An assertion will fail if
** that is attempted.
**
-** The cost of an INSERT is roughly constant. (Sometime new memory
+** The cost of an INSERT is roughly constant. (Sometimes new memory
** has to be allocated on an INSERT.) The cost of a TEST with a new
** batch number is O(NlogN) where N is the number of elements in the RowSet.
** The cost of a TEST using the same batch number is O(logN). The cost
@@ -36318,6 +40293,11 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
/*
** Each entry in a RowSet is an instance of the following object.
+**
+** This same object is reused to store a linked list of trees of RowSetEntry
+** objects. In that alternative use, pRight points to the next entry
+** in the list, pLeft points to the tree, and v is unused. The
+** RowSet.pForest value points to the head of this forest list.
*/
struct RowSetEntry {
i64 v; /* ROWID value for this entry */
@@ -36347,13 +40327,19 @@ struct RowSet {
struct RowSetEntry *pEntry; /* List of entries using pRight */
struct RowSetEntry *pLast; /* Last entry on the pEntry list */
struct RowSetEntry *pFresh; /* Source of new entry objects */
- struct RowSetEntry *pTree; /* Binary tree of entries */
+ struct RowSetEntry *pForest; /* List of binary trees of entries */
u16 nFresh; /* Number of objects on pFresh */
- u8 isSorted; /* True if pEntry is sorted */
- u8 iBatch; /* Current insert batch */
+ u16 rsFlags; /* Various flags */
+ int iBatch; /* Current insert batch */
};
/*
+** Allowed values for RowSet.rsFlags
+*/
+#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */
+#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */
+
+/*
** Turn bulk memory into a RowSet object. N bytes of memory
** are available at pSpace. The db pointer is used as a memory context
** for any subsequent allocations that need to occur.
@@ -36373,10 +40359,10 @@ SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int
p->db = db;
p->pEntry = 0;
p->pLast = 0;
- p->pTree = 0;
+ p->pForest = 0;
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
- p->isSorted = 1;
+ p->rsFlags = ROWSET_SORTED;
p->iBatch = 0;
return p;
}
@@ -36396,43 +40382,59 @@ SQLITE_PRIVATE void sqlite3RowSetClear(RowSet *p){
p->nFresh = 0;
p->pEntry = 0;
p->pLast = 0;
- p->pTree = 0;
- p->isSorted = 1;
+ p->pForest = 0;
+ p->rsFlags = ROWSET_SORTED;
}
/*
-** Insert a new value into a RowSet.
+** Allocate a new RowSetEntry object that is associated with the
+** given RowSet. Return a pointer to the new and completely uninitialized
+** objected.
**
-** The mallocFailed flag of the database connection is set if a
-** memory allocation fails.
+** In an OOM situation, the RowSet.db->mallocFailed flag is set and this
+** routine returns NULL.
*/
-SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
- struct RowSetEntry *pEntry; /* The new entry */
- struct RowSetEntry *pLast; /* The last prior entry */
+static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
assert( p!=0 );
if( p->nFresh==0 ){
struct RowSetChunk *pNew;
pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
if( pNew==0 ){
- return;
+ return 0;
}
pNew->pNextChunk = p->pChunk;
p->pChunk = pNew;
p->pFresh = pNew->aEntry;
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
}
- pEntry = p->pFresh++;
p->nFresh--;
+ return p->pFresh++;
+}
+
+/*
+** Insert a new value into a RowSet.
+**
+** The mallocFailed flag of the database connection is set if a
+** memory allocation fails.
+*/
+SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
+ struct RowSetEntry *pEntry; /* The new entry */
+ struct RowSetEntry *pLast; /* The last prior entry */
+
+ /* This routine is never called after sqlite3RowSetNext() */
+ assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
+
+ pEntry = rowSetEntryAlloc(p);
+ if( pEntry==0 ) return;
pEntry->v = rowid;
pEntry->pRight = 0;
pLast = p->pLast;
if( pLast ){
- if( p->isSorted && rowid<=pLast->v ){
- p->isSorted = 0;
+ if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){
+ p->rsFlags &= ~ROWSET_SORTED;
}
pLast->pRight = pEntry;
}else{
- assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */
p->pEntry = pEntry;
}
p->pLast = pEntry;
@@ -36444,7 +40446,7 @@ SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
** The input lists are connected via pRight pointers and are
** assumed to each already be in sorted order.
*/
-static struct RowSetEntry *rowSetMerge(
+static struct RowSetEntry *rowSetEntryMerge(
struct RowSetEntry *pA, /* First sorted list to be merged */
struct RowSetEntry *pB /* Second sorted list to be merged */
){
@@ -36478,32 +40480,29 @@ static struct RowSetEntry *rowSetMerge(
}
/*
-** Sort all elements on the pEntry list of the RowSet into ascending order.
+** Sort all elements on the list of RowSetEntry objects into order of
+** increasing v.
*/
-static void rowSetSort(RowSet *p){
+static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
unsigned int i;
- struct RowSetEntry *pEntry;
- struct RowSetEntry *aBucket[40];
+ struct RowSetEntry *pNext, *aBucket[40];
- assert( p->isSorted==0 );
memset(aBucket, 0, sizeof(aBucket));
- while( p->pEntry ){
- pEntry = p->pEntry;
- p->pEntry = pEntry->pRight;
- pEntry->pRight = 0;
+ while( pIn ){
+ pNext = pIn->pRight;
+ pIn->pRight = 0;
for(i=0; aBucket[i]; i++){
- pEntry = rowSetMerge(aBucket[i], pEntry);
+ pIn = rowSetEntryMerge(aBucket[i], pIn);
aBucket[i] = 0;
}
- aBucket[i] = pEntry;
+ aBucket[i] = pIn;
+ pIn = pNext;
}
- pEntry = 0;
+ pIn = 0;
for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
- pEntry = rowSetMerge(pEntry, aBucket[i]);
+ pIn = rowSetEntryMerge(pIn, aBucket[i]);
}
- p->pEntry = pEntry;
- p->pLast = 0;
- p->isSorted = 1;
+ return pIn;
}
@@ -36597,20 +40596,37 @@ static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
}
/*
-** Convert the list in p->pEntry into a sorted list if it is not
-** sorted already. If there is a binary tree on p->pTree, then
-** convert it into a list too and merge it into the p->pEntry list.
+** Take all the entries on p->pEntry and on the trees in p->pForest and
+** sort them all together into one big ordered list on p->pEntry.
+**
+** This routine should only be called once in the life of a RowSet.
*/
static void rowSetToList(RowSet *p){
- if( !p->isSorted ){
- rowSetSort(p);
+
+ /* This routine is called only once */
+ assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
+
+ if( (p->rsFlags & ROWSET_SORTED)==0 ){
+ p->pEntry = rowSetEntrySort(p->pEntry);
}
- if( p->pTree ){
- struct RowSetEntry *pHead, *pTail;
- rowSetTreeToList(p->pTree, &pHead, &pTail);
- p->pTree = 0;
- p->pEntry = rowSetMerge(p->pEntry, pHead);
+
+ /* While this module could theoretically support it, sqlite3RowSetNext()
+ ** is never called after sqlite3RowSetText() for the same RowSet. So
+ ** there is never a forest to deal with. Should this change, simply
+ ** remove the assert() and the #if 0. */
+ assert( p->pForest==0 );
+#if 0
+ while( p->pForest ){
+ struct RowSetEntry *pTree = p->pForest->pLeft;
+ if( pTree ){
+ struct RowSetEntry *pHead, *pTail;
+ rowSetTreeToList(pTree, &pHead, &pTail);
+ p->pEntry = rowSetEntryMerge(p->pEntry, pHead);
+ }
+ p->pForest = p->pForest->pRight;
}
+#endif
+ p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */
}
/*
@@ -36622,7 +40638,12 @@ static void rowSetToList(RowSet *p){
** routine may not be called again.
*/
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
- rowSetToList(p);
+ assert( p!=0 );
+
+ /* Merge the forest into a single sorted list on first call */
+ if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p);
+
+ /* Return the next entry on the list */
if( p->pEntry ){
*pRowid = p->pEntry->v;
p->pEntry = p->pEntry->pRight;
@@ -36636,28 +40657,68 @@ SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
}
/*
-** Check to see if element iRowid was inserted into the the rowset as
+** Check to see if element iRowid was inserted into the rowset as
** part of any insert batch prior to iBatch. Return 1 or 0.
+**
+** If this is the first test of a new batch and if there exist entries
+** on pRowSet->pEntry, then sort those entries into the forest at
+** pRowSet->pForest so that they can be tested.
*/
-SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
- struct RowSetEntry *p;
+SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){
+ struct RowSetEntry *p, *pTree;
+
+ /* This routine is never called after sqlite3RowSetNext() */
+ assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 );
+
+ /* Sort entries into the forest on the first test of a new batch
+ */
if( iBatch!=pRowSet->iBatch ){
- if( pRowSet->pEntry ){
- rowSetToList(pRowSet);
- pRowSet->pTree = rowSetListToTree(pRowSet->pEntry);
+ p = pRowSet->pEntry;
+ if( p ){
+ struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
+ if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){
+ p = rowSetEntrySort(p);
+ }
+ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
+ ppPrevTree = &pTree->pRight;
+ if( pTree->pLeft==0 ){
+ pTree->pLeft = rowSetListToTree(p);
+ break;
+ }else{
+ struct RowSetEntry *pAux, *pTail;
+ rowSetTreeToList(pTree->pLeft, &pAux, &pTail);
+ pTree->pLeft = 0;
+ p = rowSetEntryMerge(pAux, p);
+ }
+ }
+ if( pTree==0 ){
+ *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet);
+ if( pTree ){
+ pTree->v = 0;
+ pTree->pRight = 0;
+ pTree->pLeft = rowSetListToTree(p);
+ }
+ }
pRowSet->pEntry = 0;
pRowSet->pLast = 0;
+ pRowSet->rsFlags |= ROWSET_SORTED;
}
pRowSet->iBatch = iBatch;
}
- p = pRowSet->pTree;
- while( p ){
- if( p->v<iRowid ){
- p = p->pRight;
- }else if( p->v>iRowid ){
- p = p->pLeft;
- }else{
- return 1;
+
+ /* Test to see if the iRowid value appears anywhere in the forest.
+ ** Return 1 if it does and 0 if not.
+ */
+ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
+ p = pTree->pLeft;
+ while( p ){
+ if( p->v<iRowid ){
+ p = p->pRight;
+ }else if( p->v>iRowid ){
+ p = p->pLeft;
+ }else{
+ return 1;
+ }
}
}
return 0;
@@ -36708,13 +40769,18 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i
#define _WAL_H_
+/* Additional values that can be added to the sync_flags argument of
+** sqlite3WalFrames():
+*/
+#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */
+#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */
+
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalLimit(x,y)
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalEndReadTransaction(z)
-# define sqlite3WalRead(v,w,x,y,z) 0
# define sqlite3WalDbsize(y) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWriteTransaction(x) 0
@@ -36726,6 +40792,8 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i
# define sqlite3WalCallback(z) 0
# define sqlite3WalExclusiveMode(y,z) 0
# define sqlite3WalHeapMemory(z) 0
+# define sqlite3WalFramesize(z) 0
+# define sqlite3WalFindFrame(x,y,z) 0
#else
#define WAL_SAVEPOINT_NDATA 4
@@ -36753,7 +40821,8 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *);
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal);
/* Read a page from the write-ahead log, if it is present. */
-SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
+SQLITE_PRIVATE int sqlite3WalFindFrame(Wal *, Pgno, u32 *);
+SQLITE_PRIVATE int sqlite3WalReadFrame(Wal *, u32, int, u8 *);
/* If the WAL is not empty, return the size of the database. */
SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal);
@@ -36807,6 +40876,13 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op);
*/
SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
+#ifdef SQLITE_ENABLE_ZIPVFS
+/* If the WAL file is not empty, return the number of bytes of content
+** stored in each frame (i.e. the db page-size when the WAL was created).
+*/
+SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
+#endif
+
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */
@@ -36866,13 +40942,13 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
**
** Definition: Two databases (or the same database at two points it time)
** are said to be "logically equivalent" if they give the same answer to
-** all queries. Note in particular the the content of freelist leaf
-** pages can be changed arbitarily without effecting the logical equivalence
+** all queries. Note in particular the content of freelist leaf
+** pages can be changed arbitrarily without affecting the logical equivalence
** of the database.
**
** (7) At any time, if any subset, including the empty set and the total set,
** of the unsynced changes to a rollback journal are removed and the
-** journal is rolled back, the resulting database file will be logical
+** journal is rolled back, the resulting database file will be logically
** equivalent to the database file at the beginning of the transaction.
**
** (8) When a transaction is rolled back, the xTruncate method of the VFS
@@ -37064,7 +41140,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** * A write transaction is active.
** * An EXCLUSIVE or greater lock is held on the database file.
** * All writing and syncing of journal and database data has finished.
-** If no error occured, all that remains is to finalize the journal to
+** If no error occurred, all that remains is to finalize the journal to
** commit the transaction. If an error did occur, the caller will need
** to rollback the transaction.
**
@@ -37169,7 +41245,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** The exception is when the database file is unlocked as the pager moves
** from ERROR to OPEN state. At this point there may be a hot-journal file
-** in the file-system that needs to be rolled back (as part of a OPEN->SHARED
+** in the file-system that needs to be rolled back (as part of an OPEN->SHARED
** transition, by the same pager or any other). If the call to xUnlock()
** fails at this point and the pager is left holding an EXCLUSIVE lock, this
** can confuse the call to xCheckReservedLock() call made later as part
@@ -37245,7 +41321,14 @@ struct PagerSavepoint {
};
/*
-** A open page cache is an instance of struct Pager. A description of
+** Bits of the Pager.doNotSpill flag. See further description below.
+*/
+#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
+#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
+#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
+
+/*
+** An open page cache is an instance of struct Pager. A description of
** some of the more important member variables follows:
**
** eState
@@ -37310,19 +41393,21 @@ struct PagerSavepoint {
** journal file from being successfully finalized, the setMaster flag
** is cleared anyway (and the pager will move to ERROR state).
**
-** doNotSpill, doNotSyncSpill
+** doNotSpill
**
-** These two boolean variables control the behaviour of cache-spills
-** (calls made by the pcache module to the pagerStress() routine to
-** write cached data to the file-system in order to free up memory).
+** This variables control the behavior of cache-spills (calls made by
+** the pcache module to the pagerStress() routine to write cached data
+** to the file-system in order to free up memory).
**
-** When doNotSpill is non-zero, writing to the database from pagerStress()
-** is disabled altogether. This is done in a very obscure case that
+** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set,
+** writing to the database from pagerStress() is disabled altogether.
+** The SPILLFLAG_ROLLBACK case is done in a very obscure case that
** comes up during savepoint rollback that requires the pcache module
** to allocate a new page to prevent the journal file from being written
-** while it is being traversed by code in pager_playback().
+** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
+** case is a user preference.
**
-** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
+** If the SPILLFLAG_NOSYNC bit is set, writing to the database from pagerStress()
** is permitted, but syncing the journal file is not. This flag is set
** by sqlite3PagerWrite() when the file-system sector-size is larger than
** the database page-size in order to prevent a journal sync from happening
@@ -37403,18 +41488,19 @@ struct Pager {
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
u8 useJournal; /* Use a rollback journal on this file */
- u8 noReadlock; /* Do not bother to obtain readlocks */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
+ u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
- u8 tempFile; /* zFilename is a temporary file */
+ u8 tempFile; /* zFilename is a temporary or immutable file */
+ u8 noLock; /* Do not lock (except in WAL mode) */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
/**************************************************************************
** The following block contains those class members that change during
- ** routine opertion. Class members not in this block are either fixed
+ ** routine operation. Class members not in this block are either fixed
** when the pager is first created or else only change when there is a
** significant mode change (such as changing the page_size, locking_mode,
** or the journal_mode). From another view, these class members describe
@@ -37426,7 +41512,6 @@ struct Pager {
u8 changeCountDone; /* Set after incrementing the change-counter */
u8 setMaster; /* True if a m-j name has been written to jrnl */
u8 doNotSpill; /* Do not spill the cache when non-zero */
- u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
u8 subjInMemory; /* True to use in-memory sub-journals */
Pgno dbSize; /* Number of pages in the database */
Pgno dbOrigSize; /* dbSize before the current transaction */
@@ -37446,6 +41531,11 @@ struct Pager {
PagerSavepoint *aSavepoint; /* Array of active savepoints */
int nSavepoint; /* Number of elements in aSavepoint[] */
char dbFileVers[16]; /* Changes whenever database file changes */
+
+ u8 bUseFetch; /* True to use xFetch() */
+ int nMmapOut; /* Number of mmap pages currently outstanding */
+ sqlite3_int64 szMmap; /* Desired maximum mmap size */
+ PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */
/*
** End of the routinely-changing class members
***************************************************************************/
@@ -37461,9 +41551,9 @@ struct Pager {
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
+ int aStat[3]; /* Total cache hits, misses and writes */
#ifdef SQLITE_TEST
- int nHit, nMiss; /* Cache hits and missing */
- int nRead, nWrite; /* Database pages read/written */
+ int nRead; /* Database pages read */
#endif
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
#ifdef SQLITE_HAS_CODEC
@@ -37481,6 +41571,15 @@ struct Pager {
};
/*
+** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
+** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
+** or CACHE_WRITE to sqlite3_db_status().
+*/
+#define PAGER_STAT_HIT 0
+#define PAGER_STAT_MISS 1
+#define PAGER_STAT_WRITE 2
+
+/*
** The following global variables hold counters used for
** testing purposes only. These variables do not exist in
** a non-testing build. These variables are not thread-safe.
@@ -37548,6 +41647,16 @@ static const unsigned char aJournalMagic[] = {
#endif
/*
+** The macro USEFETCH is true if we are allowed to use the xFetch and xUnfetch
+** interfaces to access the database using memory-mapped I/O.
+*/
+#if SQLITE_MAX_MMAP_SIZE>0
+# define USEFETCH(x) ((x)->bUseFetch)
+#else
+# define USEFETCH(x) 0
+#endif
+
+/*
** The maximum legal page number is (2^31 - 1).
*/
#define PAGER_MAX_PGNO 2147483647
@@ -37577,7 +41686,7 @@ static int pagerUseWal(Pager *pPager){
#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
-# define pagerWalFrames(v,w,x,y,z) 0
+# define pagerWalFrames(v,w,x,y) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
@@ -37650,7 +41759,7 @@ static int assert_pager_state(Pager *p){
case PAGER_READER:
assert( pPager->errCode==SQLITE_OK );
assert( p->eLock!=UNKNOWN_LOCK );
- assert( p->eLock>=SHARED_LOCK || p->noReadlock );
+ assert( p->eLock>=SHARED_LOCK );
break;
case PAGER_WRITER_LOCKED:
@@ -37781,11 +41890,12 @@ static char *print_pager_state(Pager *p){
** PagerSavepoint.pInSavepoint.
*/
static int subjRequiresPage(PgHdr *pPg){
- Pgno pgno = pPg->pgno;
Pager *pPager = pPg->pPager;
+ PagerSavepoint *p;
+ Pgno pgno = pPg->pgno;
int i;
for(i=0; i<pPager->nSavepoint; i++){
- PagerSavepoint *p = &pPager->aSavepoint[i];
+ p = &pPager->aSavepoint[i];
if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
return 1;
}
@@ -37796,8 +41906,8 @@ static int subjRequiresPage(PgHdr *pPg){
/*
** Return true if the page is already in the journal file.
*/
-static int pageInJournal(PgHdr *pPg){
- return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
+static int pageInJournal(Pager *pPager, PgHdr *pPg){
+ return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
}
/*
@@ -37849,7 +41959,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
- rc = sqlite3OsUnlock(pPager->fd, eLock);
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
if( pPager->eLock!=UNKNOWN_LOCK ){
pPager->eLock = (u8)eLock;
}
@@ -37873,7 +41983,7 @@ static int pagerLockDb(Pager *pPager, int eLock){
assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
- rc = sqlite3OsLock(pPager->fd, eLock);
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsLock(pPager->fd, eLock);
if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
pPager->eLock = (u8)eLock;
IOTRACE(("LOCK %p %d\n", pPager, eLock))
@@ -38004,6 +42114,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
|| szJ<16
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
|| len>=nMaster
+ || len==0
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|| memcmp(aMagic, aJournalMagic, 8)
@@ -38181,7 +42292,7 @@ static int writeJournalHdr(Pager *pPager){
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
- /* The random check-hash initialiser */
+ /* The random check-hash initializer */
sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
/* The initial database size */
@@ -38381,12 +42492,11 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
if( !zMaster
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
- || pPager->journalMode==PAGER_JOURNALMODE_OFF
+ || !isOpen(pPager->jfd)
){
return SQLITE_OK;
}
pPager->setMaster = 1;
- assert( isOpen(pPager->jfd) );
assert( pPager->journalHdr <= pPager->journalOff );
/* Calculate the length in bytes and the checksum of zMaster */
@@ -38435,21 +42545,6 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
}
/*
-** Find a page in the hash table given its page number. Return
-** a pointer to the page or NULL if the requested page is not
-** already in memory.
-*/
-static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
- PgHdr *p; /* Return value */
-
- /* It is not possible for a call to PcacheFetch() with createFlag==0 to
- ** fail, since no attempt to allocate dynamic memory will be made.
- */
- (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
- return p;
-}
-
-/*
** Discard the entire contents of the in-memory page-cache.
*/
static void pager_reset(Pager *pPager){
@@ -38579,6 +42674,7 @@ static void pager_unlock(Pager *pPager){
pPager->changeCountDone = pPager->tempFile;
pPager->eState = PAGER_OPEN;
pPager->errCode = SQLITE_OK;
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
}
pPager->journalOff = 0;
@@ -38620,6 +42716,8 @@ static int pager_error(Pager *pPager, int rc){
return rc;
}
+static int pager_truncate(Pager *pPager, Pgno nPage);
+
/*
** This routine ends a transaction. A transaction is usually ended by
** either a COMMIT or a ROLLBACK operation. This routine may be called
@@ -38673,7 +42771,7 @@ static int pager_error(Pager *pPager, int rc){
** to the first error encountered (the journal finalization one) is
** returned.
*/
-static int pager_end_transaction(Pager *pPager, int hasMaster){
+static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
int rc = SQLITE_OK; /* Error code from journal finalization operation */
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
@@ -38710,6 +42808,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
rc = SQLITE_OK;
}else{
rc = sqlite3OsTruncate(pPager->jfd, 0);
+ if( rc==SQLITE_OK && pPager->fullSync ){
+ /* Make sure the new file size is written into the inode right away.
+ ** Otherwise the journal might resurrect following a power loss and
+ ** cause the last transaction to roll back. See
+ ** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773
+ */
+ rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
+ }
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
@@ -38723,12 +42829,13 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
** file should be closed and deleted. If this connection writes to
** the database file, it will do so using an in-memory journal.
*/
+ int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd));
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
sqlite3OsClose(pPager->jfd);
- if( !pPager->tempFile ){
+ if( bDelete ){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}
}
@@ -38737,10 +42844,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
#ifdef SQLITE_CHECK_PAGES
sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){
- PgHdr *p = pager_lookup(pPager, 1);
+ PgHdr *p = sqlite3PagerLookup(pPager, 1);
if( p ){
p->pageHash = 0;
- sqlite3PagerUnref(p);
+ sqlite3PagerUnrefNotNull(p);
}
}
#endif
@@ -38758,7 +42865,22 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
*/
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
assert( rc2==SQLITE_OK );
+ }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){
+ /* This branch is taken when committing a transaction in rollback-journal
+ ** mode if the database file on disk is larger than the database image.
+ ** At this point the journal has been finalized and the transaction
+ ** successfully committed, but the EXCLUSIVE lock is still held on the
+ ** file. So it is safe to truncate the database file to its minimum
+ ** required size. */
+ assert( pPager->eLock==EXCLUSIVE_LOCK );
+ rc = pager_truncate(pPager, pPager->dbSize);
+ }
+
+ if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
}
+
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
@@ -38797,7 +42919,7 @@ static void pagerUnlockAndRollback(Pager *pPager){
sqlite3EndBenignMalloc();
}else if( !pPager->exclusiveMode ){
assert( pPager->eState==PAGER_READER );
- pager_end_transaction(pPager, 0);
+ pager_end_transaction(pPager, 0, 0);
}
}
pager_unlock(pPager);
@@ -39001,7 +43123,7 @@ static int pager_playback_one_page(
if( pagerUseWal(pPager) ){
pPg = 0;
}else{
- pPg = pager_lookup(pPager, pgno);
+ pPg = sqlite3PagerLookup(pPager, pgno);
}
assert( pPg || !MEMDB );
assert( pPager->eState!=PAGER_OPEN || pPg==0 );
@@ -39021,7 +43143,7 @@ static int pager_playback_one_page(
i64 ofst = (pgno-1)*(i64)pPager->pageSize;
testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 );
assert( !pagerUseWal(pPager) );
- rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst);
+ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst);
if( pgno>pPager->dbFileSize ){
pPager->dbFileSize = pgno;
}
@@ -39048,11 +43170,11 @@ static int pager_playback_one_page(
** requiring a journal-sync before it is written.
*/
assert( isSavepnt );
- assert( pPager->doNotSpill==0 );
- pPager->doNotSpill++;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 );
+ pPager->doNotSpill |= SPILLFLAG_ROLLBACK;
rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
- assert( pPager->doNotSpill==1 );
- pPager->doNotSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
if( rc!=SQLITE_OK ) return rc;
pPg->flags &= ~PGHDR_NEED_READ;
sqlite3PcacheMakeDirty(pPg);
@@ -39181,7 +43303,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
if( rc!=SQLITE_OK ) goto delmaster_out;
nMasterPtr = pVfs->mxPathname+1;
- zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1);
+ zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1);
if( !zMasterJournal ){
rc = SQLITE_NOMEM;
goto delmaster_out;
@@ -39250,7 +43372,7 @@ delmaster_out:
** If the file on disk is currently larger than nPage pages, then use the VFS
** xTruncate() method to truncate it.
**
-** Or, it might might be the case that the file on disk is smaller than
+** Or, it might be the case that the file on disk is smaller than
** nPage pages. Some operating system implementations can get confused if
** you try to truncate a file to some size that is larger than it
** currently is, so detect this case and write a single zero byte to
@@ -39276,10 +43398,9 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
if( rc==SQLITE_OK && currentSize!=newSize ){
if( currentSize>newSize ){
rc = sqlite3OsTruncate(pPager->fd, newSize);
- }else{
+ }else if( (currentSize+szPage)<=newSize ){
char *pTmp = pPager->pTmpSpace;
memset(pTmp, 0, szPage);
- testcase( (newSize-szPage) < currentSize );
testcase( (newSize-szPage) == currentSize );
testcase( (newSize-szPage) > currentSize );
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
@@ -39293,9 +43414,24 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
}
/*
+** Return a sanitized version of the sector-size of OS file pFile. The
+** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE.
+*/
+SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){
+ int iRet = sqlite3OsSectorSize(pFile);
+ if( iRet<32 ){
+ iRet = 512;
+ }else if( iRet>MAX_SECTOR_SIZE ){
+ assert( MAX_SECTOR_SIZE>=512 );
+ iRet = MAX_SECTOR_SIZE;
+ }
+ return iRet;
+}
+
+/*
** Set the value of the Pager.sectorSize variable for the given
** pager based on the value returned by the xSectorSize method
-** of the open database file. The sector size will be used used
+** of the open database file. The sector size will be used
** to determine the size and alignment of journal header and
** master journal pointers within created journal files.
**
@@ -39305,23 +43441,29 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
** the value returned by the xSectorSize() method rounded up to 32 if
** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it
** is greater than MAX_SECTOR_SIZE.
+**
+** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set
+** the effective sector size to its minimum value (512). The purpose of
+** pPager->sectorSize is to define the "blast radius" of bytes that
+** might change if a crash occurs while writing to a single byte in
+** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero
+** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector
+** size. For backwards compatibility of the rollback journal file format,
+** we cannot reduce the effective sector size below 512.
*/
static void setSectorSize(Pager *pPager){
assert( isOpen(pPager->fd) || pPager->tempFile );
- if( !pPager->tempFile ){
+ if( pPager->tempFile
+ || (sqlite3OsDeviceCharacteristics(pPager->fd) &
+ SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
+ ){
/* Sector size doesn't matter for temporary files. Also, the file
** may not have been opened yet, in which case the OsSectorSize()
- ** call will segfault.
- */
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
- }
- if( pPager->sectorSize<32 ){
+ ** call will segfault. */
pPager->sectorSize = 512;
- }
- if( pPager->sectorSize>MAX_SECTOR_SIZE ){
- assert( MAX_SECTOR_SIZE>=512 );
- pPager->sectorSize = MAX_SECTOR_SIZE;
+ }else{
+ pPager->sectorSize = sqlite3SectorSize(pPager->fd);
}
}
@@ -39392,6 +43534,7 @@ static int pager_playback(Pager *pPager, int isHot){
int res = 1; /* Value returned by sqlite3OsAccess() */
char *zMaster = 0; /* Name of master journal file if any */
int needPagerReset; /* True to reset page prior to first page rollback */
+ int nPlayback = 0; /* Total number of pages restored from journal */
/* Figure out how many records are in the journal. Abort early if
** the journal is empty.
@@ -39492,9 +43635,10 @@ static int pager_playback(Pager *pPager, int isHot){
needPagerReset = 0;
}
rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0);
- if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_OK ){
+ nPlayback++;
+ }else{
if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
pPager->journalOff = szJ;
break;
}else if( rc==SQLITE_IOERR_SHORT_READ ){
@@ -39525,10 +43669,11 @@ end_playback:
** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the
** assertion that the transaction counter was modified.
*/
- assert(
- pPager->fd->pMethods==0 ||
- sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK
- );
+#ifdef SQLITE_DEBUG
+ if( pPager->fd->pMethods ){
+ sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
+ }
+#endif
/* If this playback is happening automatically as a result of an IO or
** malloc error that occurred after the change-counter was updated but
@@ -39549,10 +43694,10 @@ end_playback:
if( rc==SQLITE_OK
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
- rc = sqlite3PagerSync(pPager);
+ rc = sqlite3PagerSync(pPager, 0);
}
if( rc==SQLITE_OK ){
- rc = pager_end_transaction(pPager, zMaster[0]!='\0');
+ rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && zMaster[0] && res ){
@@ -39562,6 +43707,10 @@ end_playback:
rc = pager_delmaster(pPager, zMaster);
testcase( rc!=SQLITE_OK );
}
+ if( isHot && nPlayback ){
+ sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s",
+ nPlayback, pPager->zJournal);
+ }
/* The Pager.sectorSize variable may have been updated while rolling
** back a journal created by a process with a different sector size
@@ -39583,27 +43732,22 @@ end_playback:
** If an IO error occurs, then the IO error is returned to the caller.
** Otherwise, SQLITE_OK is returned.
*/
-static int readDbPage(PgHdr *pPg){
+static int readDbPage(PgHdr *pPg, u32 iFrame){
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
Pgno pgno = pPg->pgno; /* Page number to read */
int rc = SQLITE_OK; /* Return code */
- int isInWal = 0; /* True if page is in log file */
int pgsz = pPager->pageSize; /* Number of bytes to read */
assert( pPager->eState>=PAGER_READER && !MEMDB );
assert( isOpen(pPager->fd) );
- if( NEVER(!isOpen(pPager->fd)) ){
- assert( pPager->tempFile );
- memset(pPg->pData, 0, pPager->pageSize);
- return SQLITE_OK;
- }
-
- if( pagerUseWal(pPager) ){
+#ifndef SQLITE_OMIT_WAL
+ if( iFrame ){
/* Try to pull the page from the write-ahead log. */
- rc = sqlite3WalRead(pPager->pWal, pgno, &isInWal, pgsz, pPg->pData);
- }
- if( rc==SQLITE_OK && !isInWal ){
+ rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
+ }else
+#endif
+ {
i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
@@ -39682,16 +43826,21 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){
Pager *pPager = (Pager *)pCtx;
PgHdr *pPg;
+ assert( pagerUseWal(pPager) );
pPg = sqlite3PagerLookup(pPager, iPg);
if( pPg ){
if( sqlite3PcachePageRefcount(pPg)==1 ){
sqlite3PcacheDrop(pPg);
}else{
- rc = readDbPage(pPg);
+ u32 iFrame = 0;
+ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
+ if( rc==SQLITE_OK ){
+ rc = readDbPage(pPg, iFrame);
+ }
if( rc==SQLITE_OK ){
pPager->xReiniter(pPg);
}
- sqlite3PagerUnref(pPg);
+ sqlite3PagerUnrefNotNull(pPg);
}
}
@@ -39747,15 +43896,16 @@ static int pagerWalFrames(
Pager *pPager, /* Pager object */
PgHdr *pList, /* List of frames to log */
Pgno nTruncate, /* Database size after this commit */
- int isCommit, /* True if this is a commit */
- int syncFlags /* Flags to pass to OsSync() (or 0) */
+ int isCommit /* True if this is a commit */
){
int rc; /* Return code */
+ int nList; /* Number of pages in pList */
#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
PgHdr *p; /* For looping over pages */
#endif
assert( pPager->pWal );
+ assert( pList );
#ifdef SQLITE_DEBUG
/* Verify that the page list is in accending order */
for(p=pList; p && p->pDirty; p=p->pDirty){
@@ -39763,6 +43913,7 @@ static int pagerWalFrames(
}
#endif
+ assert( pList->pDirty==0 || isCommit );
if( isCommit ){
/* If a WAL transaction is being committed, there is no point in writing
** any pages with page numbers greater than nTruncate into the WAL file.
@@ -39770,15 +43921,22 @@ static int pagerWalFrames(
** list here. */
PgHdr *p;
PgHdr **ppNext = &pList;
- for(p=pList; (*ppNext = p); p=p->pDirty){
- if( p->pgno<=nTruncate ) ppNext = &p->pDirty;
+ nList = 0;
+ for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
+ if( p->pgno<=nTruncate ){
+ ppNext = &p->pDirty;
+ nList++;
+ }
}
assert( pList );
+ }else{
+ nList = 1;
}
+ pPager->aStat[PAGER_STAT_WRITE] += nList;
if( pList->pgno==1 ) pager_write_changecounter(pList);
rc = sqlite3WalFrames(pPager->pWal,
- pPager->pageSize, pList, nTruncate, isCommit, syncFlags
+ pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
);
if( rc==SQLITE_OK && pPager->pBackup ){
PgHdr *p;
@@ -39822,6 +43980,7 @@ static int pagerBeginReadTransaction(Pager *pPager){
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
if( rc!=SQLITE_OK || changed ){
pager_reset(pPager);
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
}
return rc;
@@ -39847,7 +44006,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
** contains no valid committed transactions.
*/
assert( pPager->eState==PAGER_OPEN );
- assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
+ assert( pPager->eLock>=SHARED_LOCK );
nPage = sqlite3WalDbsize(pPager->pWal);
/* If the database size was not available from the WAL sub-system,
@@ -39865,10 +44024,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
return rc;
}
}
- nPage = (Pgno)(n / pPager->pageSize);
- if( nPage==0 && n>0 ){
- nPage = 1;
- }
+ nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize);
}
/* If the current number of pages in the file is greater than the
@@ -39905,7 +44061,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
static int pagerOpenWalIfPresent(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->eState==PAGER_OPEN );
- assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
+ assert( pPager->eLock>=SHARED_LOCK );
if( !pPager->tempFile ){
int isWal; /* True if WAL file exists */
@@ -39915,6 +44071,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){
if( rc ) return rc;
if( nPage==0 ){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
+ if( rc==SQLITE_IOERR_DELETE_NOENT ) rc = SQLITE_OK;
isWal = 0;
}else{
rc = sqlite3OsAccess(
@@ -40058,13 +44215,13 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
*/
if( pSavepoint ){
u32 ii; /* Loop counter */
- i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize);
+ i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize);
if( pagerUseWal(pPager) ){
rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData);
}
for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){
- assert( offset==ii*(4+pPager->pageSize) );
+ assert( offset==(i64)ii*(4+pPager->pageSize) );
rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1);
}
assert( rc!=SQLITE_DONE );
@@ -40086,9 +44243,42 @@ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
}
/*
-** Adjust the robustness of the database to damage due to OS crashes
-** or power failures by changing the number of syncs()s when writing
-** the rollback journal. There are three levels:
+** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap.
+*/
+static void pagerFixMaplimit(Pager *pPager){
+#if SQLITE_MAX_MMAP_SIZE>0
+ sqlite3_file *fd = pPager->fd;
+ if( isOpen(fd) && fd->pMethods->iVersion>=3 ){
+ sqlite3_int64 sz;
+ sz = pPager->szMmap;
+ pPager->bUseFetch = (sz>0);
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz);
+ }
+#endif
+}
+
+/*
+** Change the maximum size of any memory mapping made of the database file.
+*/
+SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *pPager, sqlite3_int64 szMmap){
+ pPager->szMmap = szMmap;
+ pagerFixMaplimit(pPager);
+}
+
+/*
+** Free as much memory as possible from the pager.
+*/
+SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
+ sqlite3PcacheShrink(pPager->pPCache);
+}
+
+/*
+** Adjust settings of the pager to those specified in the pgFlags parameter.
+**
+** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness
+** of the database to damage due to OS crashes or power failures by
+** changing the number of syncs()s when writing the journals.
+** There are three levels:
**
** OFF sqlite3OsSync() is never called. This is the default
** for temporary and transient files.
@@ -40129,28 +44319,36 @@ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(
+SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int bFullFsync, /* PRAGMA fullfsync */
- int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
+ unsigned pgFlags /* Various flags */
){
+ unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
assert( level>=1 && level<=3 );
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
- }else if( bFullFsync ){
+ }else if( pgFlags & PAGER_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_FULL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
- }else if( bCkptFullFsync ){
+ }else if( pgFlags & PAGER_CKPT_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else{
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
}
+ pPager->walSyncFlags = pPager->syncFlags;
+ if( pPager->fullSync ){
+ pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
+ }
+ if( pgFlags & PAGER_CACHESPILL ){
+ pPager->doNotSpill &= ~SPILLFLAG_OFF;
+ }else{
+ pPager->doNotSpill |= SPILLFLAG_OFF;
+ }
}
#endif
@@ -40221,9 +44419,16 @@ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
Pager *pPager, /* Pager object */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
-){
+){
pPager->xBusyHandler = xBusyHandler;
pPager->pBusyHandlerArg = pBusyHandlerArg;
+
+ if( isOpen(pPager->fd) ){
+ void **ap = (void **)&pPager->xBusyHandler;
+ assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
+ assert( ap[1]==pBusyHandlerArg );
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
+ }
}
/*
@@ -40288,11 +44493,15 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
if( rc==SQLITE_OK ){
pager_reset(pPager);
- pPager->dbSize = (Pgno)(nByte/pageSize);
- pPager->pageSize = pageSize;
+ rc = sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
+ }
+ if( rc==SQLITE_OK ){
sqlite3PageFree(pPager->pTmpSpace);
pPager->pTmpSpace = pNew;
- sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
+ pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize);
+ pPager->pageSize = pageSize;
+ }else{
+ sqlite3PageFree(pNew);
}
}
@@ -40302,6 +44511,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
assert( nReserve>=0 && nReserve<1000 );
pPager->nReserve = (i16)nReserve;
pagerReportSize(pPager);
+ pagerFixMaplimit(pPager);
}
return rc;
}
@@ -40425,7 +44635,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
int rc; /* Return code */
/* Check that this is either a no-op (because the requested lock is
- ** already held, or one of the transistions that the busy-handler
+ ** already held), or one of the transitions that the busy-handler
** may be invoked during, according to the comment above
** sqlite3PagerSetBusyhandler().
*/
@@ -40455,7 +44665,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
** dirty page were to be discarded from the cache via the pagerStress()
** routine, pagerStress() would not write the current page content to
** the database file. If a savepoint transaction were rolled back after
-** this happened, the correct behaviour would be to restore the current
+** this happened, the correct behavior would be to restore the current
** content of the page. However, since this content is not present in either
** the database file or the portion of the rollback journal and
** sub-journal rolled back the content could not be restored and the
@@ -40479,12 +44689,26 @@ static void assertTruncateConstraint(Pager *pPager){
** function does not actually modify the database file on disk. It
** just sets the internal state of the pager object so that the
** truncation will be done when the current transaction is committed.
+**
+** This function is only called right before committing a transaction.
+** Once this function has been called, the transaction must either be
+** rolled back or committed. It is not safe to call this function and
+** then continue writing to the database.
*/
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
assert( pPager->dbSize>=nPage );
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
pPager->dbSize = nPage;
- assertTruncateConstraint(pPager);
+
+ /* At one point the code here called assertTruncateConstraint() to
+ ** ensure that all pages being truncated away by this operation are,
+ ** if one or more savepoints are open, present in the savepoint
+ ** journal so that they can be restored if the savepoint is rolled
+ ** back. This is no longer necessary as this function is now only
+ ** called right before committing a transaction. So although the
+ ** Pager object may still have open savepoints (Pager.nSavepoint!=0),
+ ** they cannot be rolled back. So the assertTruncateConstraint() call
+ ** is no longer correct. */
}
@@ -40514,6 +44738,81 @@ static int pagerSyncHotJournal(Pager *pPager){
}
/*
+** Obtain a reference to a memory mapped page object for page number pgno.
+** The new object will use the pointer pData, obtained from xFetch().
+** If successful, set *ppPage to point to the new page reference
+** and return SQLITE_OK. Otherwise, return an SQLite error code and set
+** *ppPage to zero.
+**
+** Page references obtained by calling this function should be released
+** by calling pagerReleaseMapPage().
+*/
+static int pagerAcquireMapPage(
+ Pager *pPager, /* Pager object */
+ Pgno pgno, /* Page number */
+ void *pData, /* xFetch()'d data for this page */
+ PgHdr **ppPage /* OUT: Acquired page object */
+){
+ PgHdr *p; /* Memory mapped page to return */
+
+ if( pPager->pMmapFreelist ){
+ *ppPage = p = pPager->pMmapFreelist;
+ pPager->pMmapFreelist = p->pDirty;
+ p->pDirty = 0;
+ memset(p->pExtra, 0, pPager->nExtra);
+ }else{
+ *ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra);
+ if( p==0 ){
+ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData);
+ return SQLITE_NOMEM;
+ }
+ p->pExtra = (void *)&p[1];
+ p->flags = PGHDR_MMAP;
+ p->nRef = 1;
+ p->pPager = pPager;
+ }
+
+ assert( p->pExtra==(void *)&p[1] );
+ assert( p->pPage==0 );
+ assert( p->flags==PGHDR_MMAP );
+ assert( p->pPager==pPager );
+ assert( p->nRef==1 );
+
+ p->pgno = pgno;
+ p->pData = pData;
+ pPager->nMmapOut++;
+
+ return SQLITE_OK;
+}
+
+/*
+** Release a reference to page pPg. pPg must have been returned by an
+** earlier call to pagerAcquireMapPage().
+*/
+static void pagerReleaseMapPage(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ pPager->nMmapOut--;
+ pPg->pDirty = pPager->pMmapFreelist;
+ pPager->pMmapFreelist = pPg;
+
+ assert( pPager->fd->pMethods->iVersion>=3 );
+ sqlite3OsUnfetch(pPager->fd, (i64)(pPg->pgno-1)*pPager->pageSize, pPg->pData);
+}
+
+/*
+** Free all PgHdr objects stored in the Pager.pMmapFreelist list.
+*/
+static void pagerFreeMapHdrs(Pager *pPager){
+ PgHdr *p;
+ PgHdr *pNext;
+ for(p=pPager->pMmapFreelist; p; p=pNext){
+ pNext = p->pDirty;
+ sqlite3_free(p);
+ }
+}
+
+
+/*
** Shutdown the page cache. Free all memory and close all files.
**
** If a transaction was in progress when this routine is called, that
@@ -40530,8 +44829,10 @@ static int pagerSyncHotJournal(Pager *pPager){
SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
u8 *pTmp = (u8 *)pPager->pTmpSpace;
+ assert( assert_pager_state(pPager) );
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
+ pagerFreeMapHdrs(pPager);
/* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
@@ -40601,7 +44902,7 @@ SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){
**
** If the Pager.noSync flag is set, then this function is a no-op.
** Otherwise, the actions required depend on the journal-mode and the
-** device characteristics of the the file-system, as follows:
+** device characteristics of the file-system, as follows:
**
** * If the journal file is an in-memory journal file, no action need
** be taken.
@@ -40793,9 +45094,12 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
** file size will be.
*/
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
- if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){
+ if( rc==SQLITE_OK
+ && pPager->dbHintSize<pPager->dbSize
+ && (pList->pDirty || pList->pgno>pPager->dbHintSize)
+ ){
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
pPager->dbHintSize = pPager->dbSize;
}
@@ -40833,6 +45137,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
if( pgno>pPager->dbFileSize ){
pPager->dbFileSize = pgno;
}
+ pPager->aStat[PAGER_STAT_WRITE]++;
/* Update any backup objects copying the contents of this pager. */
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData);
@@ -40841,7 +45146,6 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
PAGERID(pPager), pgno, pager_pagehash(pList)));
IOTRACE(("PGOUT %p %d\n", pPager, pgno));
PAGER_INCR(sqlite3_pager_writedb_count);
- PAGER_INCR(pPager->nWrite);
}else{
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
}
@@ -40895,7 +45199,7 @@ static int subjournalPage(PgHdr *pPg){
assert( isOpen(pPager->jfd) || pagerUseWal(pPager) );
assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 );
assert( pagerUseWal(pPager)
- || pageInJournal(pPg)
+ || pageInJournal(pPager, pPg)
|| pPg->pgno>pPager->dbOrigSize
);
rc = openSubJournal(pPager);
@@ -40904,7 +45208,7 @@ static int subjournalPage(PgHdr *pPg){
** write the journal record into the file. */
if( rc==SQLITE_OK ){
void *pData = pPg->pData;
- i64 offset = pPager->nSubRec*(4+pPager->pageSize);
+ i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
char *pData2;
CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
@@ -40949,24 +45253,30 @@ static int pagerStress(void *p, PgHdr *pPg){
assert( pPg->pPager==pPager );
assert( pPg->flags&PGHDR_DIRTY );
- /* The doNotSyncSpill flag is set during times when doing a sync of
+ /* The doNotSpill NOSYNC bit is set during times when doing a sync of
** journal (and adding a new header) is not allowed. This occurs
** during calls to sqlite3PagerWrite() while trying to journal multiple
** pages belonging to the same sector.
**
- ** The doNotSpill flag inhibits all cache spilling regardless of whether
- ** or not a sync is required. This is set during a rollback.
+ ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling
+ ** regardless of whether or not a sync is required. This is set during
+ ** a rollback or by user request, respectively.
**
** Spilling is also prohibited when in an error state since that could
- ** lead to database corruption. In the current implementaton it
- ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
+ ** lead to database corruption. In the current implementation it
+ ** is impossible for sqlite3PcacheFetch() to be called with createFlag==3
** while in the error state, hence it is impossible for this routine to
** be called in the error state. Nevertheless, we include a NEVER()
** test for the error state as a safeguard against future changes.
*/
if( NEVER(pPager->errCode) ) return SQLITE_OK;
- if( pPager->doNotSpill ) return SQLITE_OK;
- if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
+ testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK );
+ testcase( pPager->doNotSpill & SPILLFLAG_OFF );
+ testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC );
+ if( pPager->doNotSpill
+ && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0
+ || (pPg->flags & PGHDR_NEED_SYNC)!=0)
+ ){
return SQLITE_OK;
}
@@ -40977,7 +45287,7 @@ static int pagerStress(void *p, PgHdr *pPg){
rc = subjournalPage(pPg);
}
if( rc==SQLITE_OK ){
- rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
+ rc = pagerWalFrames(pPager, pPg, 0, 0);
}
}else{
@@ -41056,7 +45366,7 @@ static int pagerStress(void *p, PgHdr *pPg){
**
** The flags argument is used to specify properties that affect the
** operation of the pager. It should be passed some bitwise combination
-** of the PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK flags.
+** of the PAGER_* flags.
**
** The vfsFlags parameter is a bitmask to pass to the flags parameter
** of the xOpen() method of the supplied VFS when opening files.
@@ -41087,7 +45397,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
char *zPathname = 0; /* Full path to database file */
int nPathname = 0; /* Number of bytes in zPathname */
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
- int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
const char *zUri = 0; /* URI args to copy */
@@ -41113,7 +45422,12 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
#ifndef SQLITE_OMIT_MEMORYDB
if( flags & PAGER_MEMORY ){
memDb = 1;
- zFilename = 0;
+ if( zFilename && zFilename[0] ){
+ zPathname = sqlite3DbStrDup(0, zFilename);
+ if( zPathname==0 ) return SQLITE_NOMEM;
+ nPathname = sqlite3Strlen30(zPathname);
+ zFilename = 0;
+ }
}
#endif
@@ -41124,7 +45438,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
if( zFilename && zFilename[0] ){
const char *z;
nPathname = pVfs->mxPathname+1;
- zPathname = sqlite3Malloc(nPathname*2);
+ zPathname = sqlite3DbMallocRaw(0, nPathname*2);
if( zPathname==0 ){
return SQLITE_NOMEM;
}
@@ -41136,7 +45450,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
z += sqlite3Strlen30(z)+1;
z += sqlite3Strlen30(z)+1;
}
- nUri = &z[1] - zUri;
+ nUri = (int)(&z[1] - zUri);
+ assert( nUri>=0 );
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
/* This branch is taken when the journal path required by
** the database being opened will be more than pVfs->mxPathname
@@ -41147,7 +45462,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
rc = SQLITE_CANTOPEN_BKPT;
}
if( rc!=SQLITE_OK ){
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
return rc;
}
}
@@ -41170,14 +45485,14 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
ROUND8(pVfs->szOsFile) + /* The main db file */
journalFileSize * 2 + /* The two journal files */
nPathname + 1 + nUri + /* zFilename */
- nPathname + 8 + 1 /* zJournal */
+ nPathname + 8 + 2 /* zJournal */
#ifndef SQLITE_OMIT_WAL
- + nPathname + 4 + 1 /* zWal */
+ + nPathname + 4 + 2 /* zWal */
#endif
);
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
if( !pPtr ){
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
return SQLITE_NOMEM;
}
pPager = (Pager*)(pPtr);
@@ -41193,17 +45508,17 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
assert( nPathname>0 );
pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
memcpy(pPager->zFilename, zPathname, nPathname);
- memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
+ if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
memcpy(pPager->zJournal, zPathname, nPathname);
- memcpy(&pPager->zJournal[nPathname], "-journal", 8);
+ memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+2);
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
#ifndef SQLITE_OMIT_WAL
pPager->zWal = &pPager->zJournal[nPathname+8+1];
memcpy(pPager->zWal, zPathname, nPathname);
- memcpy(&pPager->zWal[nPathname], "-wal", 4);
+ memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
#endif
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
}
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
@@ -41224,30 +45539,38 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
** + The value returned by sqlite3OsSectorSize()
** + The largest page size that can be written atomically.
*/
- if( rc==SQLITE_OK && !readOnly ){
- setSectorSize(pPager);
- assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
- if( szPageDflt<pPager->sectorSize ){
- if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
- szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
- }else{
- szPageDflt = (u32)pPager->sectorSize;
+ if( rc==SQLITE_OK ){
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ if( !readOnly ){
+ setSectorSize(pPager);
+ assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
+ if( szPageDflt<pPager->sectorSize ){
+ if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
+ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
+ }else{
+ szPageDflt = (u32)pPager->sectorSize;
+ }
}
- }
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
- {
- int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
- int ii;
- assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
- assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
- assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
- for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
- if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
- szPageDflt = ii;
+ {
+ int ii;
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+ assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
+ for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
+ if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
+ szPageDflt = ii;
+ }
}
}
- }
#endif
+ }
+ pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0);
+ if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
+ || sqlite3_uri_boolean(zFilename, "immutable", 0) ){
+ vfsFlags |= SQLITE_OPEN_READONLY;
+ goto act_like_temp_file;
+ }
}
}else{
/* If a temporary file is requested, it is not opened immediately.
@@ -41257,10 +45580,14 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
** This branch is also run for an in-memory database. An in-memory
** database is the same as a temp-file that is never written out to
** disk and uses an in-memory rollback journal.
+ **
+ ** This branch also runs for files marked as immutable.
*/
+act_like_temp_file:
tempFile = 1;
- pPager->eState = PAGER_READER;
- pPager->eLock = EXCLUSIVE_LOCK;
+ pPager->eState = PAGER_READER; /* Pretend we already have a lock */
+ pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE locking mode */
+ pPager->noLock = 1; /* Do no locking */
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
@@ -41273,27 +45600,27 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
testcase( rc!=SQLITE_OK );
}
- /* If an error occurred in either of the blocks above, free the
- ** Pager structure and close the file.
+ /* Initialize the PCache object. */
+ if( rc==SQLITE_OK ){
+ assert( nExtra<1000 );
+ nExtra = ROUND8(nExtra);
+ rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
+ !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
+ }
+
+ /* If an error occurred above, free the Pager structure and close the file.
*/
if( rc!=SQLITE_OK ){
- assert( !pPager->pTmpSpace );
sqlite3OsClose(pPager->fd);
+ sqlite3PageFree(pPager->pTmpSpace);
sqlite3_free(pPager);
return rc;
}
- /* Initialize the PCache object. */
- assert( nExtra<1000 );
- nExtra = ROUND8(nExtra);
- sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
- !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
-
PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename));
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
pPager->useJournal = (u8)useJournal;
- pPager->noReadlock = (noReadlock && readOnly) ?1:0;
/* pPager->stmtOpen = 0; */
/* pPager->stmtInUse = 0; */
/* pPager->nRef = 0; */
@@ -41302,9 +45629,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
/* pPager->nPage = 0; */
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
/* pPager->state = PAGER_UNLOCK; */
-#if 0
- assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
-#endif
/* pPager->errMask = 0; */
pPager->tempFile = (u8)tempFile;
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
@@ -41316,9 +45640,17 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
- pPager->fullSync = pPager->noSync ?0:1;
- pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
- pPager->ckptSyncFlags = pPager->syncFlags;
+ if( pPager->noSync ){
+ assert( pPager->fullSync==0 );
+ assert( pPager->syncFlags==0 );
+ assert( pPager->walSyncFlags==0 );
+ assert( pPager->ckptSyncFlags==0 );
+ }else{
+ pPager->fullSync = 1;
+ pPager->syncFlags = SQLITE_SYNC_NORMAL;
+ pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
+ pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
+ }
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@@ -41335,12 +45667,37 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
/* pPager->pBusyHandlerArg = 0; */
pPager->xReiniter = xReinit;
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
+ /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */
*ppPager = pPager;
return SQLITE_OK;
}
+/* Verify that the database file has not be deleted or renamed out from
+** under the pager. Return SQLITE_OK if the database is still were it ought
+** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
+** code from sqlite3OsAccess()) if the database has gone missing.
+*/
+static int databaseIsUnmoved(Pager *pPager){
+ int bHasMoved = 0;
+ int rc;
+
+ if( pPager->tempFile ) return SQLITE_OK;
+ if( pPager->dbSize==0 ) return SQLITE_OK;
+ assert( pPager->zFilename && pPager->zFilename[0] );
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
+ if( rc==SQLITE_NOTFOUND ){
+ /* If the HAS_MOVED file-control is unimplemented, assume that the file
+ ** has not been moved. That is the historical behavior of SQLite: prior to
+ ** version 3.8.3, it never checked */
+ rc = SQLITE_OK;
+ }else if( rc==SQLITE_OK && bHasMoved ){
+ rc = SQLITE_READONLY_DBMOVED;
+ }
+ return rc;
+}
+
/*
** This function is called after transitioning from PAGER_UNLOCK to
@@ -41406,15 +45763,17 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK && !locked ){
Pgno nPage; /* Number of pages in database file */
- /* Check the size of the database file. If it consists of 0 pages,
- ** then delete the journal file. See the header comment above for
- ** the reasoning here. Delete the obsolete journal file under
- ** a RESERVED lock to avoid race conditions and to avoid violating
- ** [H33020].
- */
rc = pagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){
- if( nPage==0 ){
+ /* If the database is zero pages in size, that means that either (1) the
+ ** journal is a remnant from a prior database with the same name where
+ ** the database file but not the journal was deleted, or (2) the initial
+ ** transaction that populates a new database is being rolled back.
+ ** In either case, the journal file can be deleted. However, take care
+ ** not to delete the journal file if it is already open due to
+ ** journal_mode=PERSIST.
+ */
+ if( nPage==0 && !jrnlOpen ){
sqlite3BeginBenignMalloc();
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
@@ -41444,7 +45803,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
*pExists = (first!=0);
}else if( rc==SQLITE_CANTOPEN ){
/* If we cannot open the rollback journal file in order to see if
- ** its has a zero header, that might be due to an I/O error, or
+ ** it has a zero header, that might be due to an I/O error, or
** it might be due to the race condition described above and in
** ticket #3883. Either way, assume that the journal is hot.
** This might be a false positive. But if it is, then the
@@ -41507,14 +45866,11 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB );
- assert( pPager->noReadlock==0 || pPager->readOnly );
- if( pPager->noReadlock==0 ){
- rc = pager_wait_on_lock(pPager, SHARED_LOCK);
- if( rc!=SQLITE_OK ){
- assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
- goto failed;
- }
+ rc = pager_wait_on_lock(pPager, SHARED_LOCK);
+ if( rc!=SQLITE_OK ){
+ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
+ goto failed;
}
/* If a journal file exists, and there is no RESERVED lock on the
@@ -41527,6 +45883,11 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
goto failed;
}
if( bHotJournal ){
+ if( pPager->readOnly ){
+ rc = SQLITE_READONLY_ROLLBACK;
+ goto failed;
+ }
+
/* Get an EXCLUSIVE lock on the database file. At this point it is
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
@@ -41624,9 +45985,11 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
);
}
- if( !pPager->tempFile
- && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0)
- ){
+ if( !pPager->tempFile && (
+ pPager->pBackup
+ || sqlite3PcachePagecount(pPager->pPCache)>0
+ || USEFETCH(pPager)
+ )){
/* The shared-lock has just been acquired on the database file
** and there are already pages in the cache (from a previous
** read or write transaction). Check to see if the database
@@ -41652,7 +46015,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
if( nPage>0 ){
IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
- if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
goto failed;
}
}else{
@@ -41661,6 +46024,16 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
pager_reset(pPager);
+
+ /* Unmap the database file. It is possible that external processes
+ ** may have truncated the database file and then extended it back
+ ** to its original size while this process was not holding a lock.
+ ** In this case there may exist a Pager.pMap mapping that appears
+ ** to be the right size but is not actually valid. Avoid this
+ ** possibility by unmapping the db here. */
+ if( USEFETCH(pPager) ){
+ sqlite3OsUnfetch(pPager->fd, 0, 0);
+ }
}
}
@@ -41702,7 +46075,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** nothing to rollback, so this routine is a no-op.
*/
static void pagerUnlockIfUnused(Pager *pPager){
- if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
+ if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
pagerUnlockAndRollback(pPager);
}
}
@@ -41730,7 +46103,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
** page is initialized to all zeros.
**
** If noContent is true, it means that we do not care about the contents
-** of the page. This occurs in two seperate scenarios:
+** of the page. This occurs in two scenarios:
**
** a) When reading a free-list leaf page from the database, and
**
@@ -41761,13 +46134,27 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
- int noContent /* Do not bother reading content from disk if true */
+ int flags /* PAGER_GET_XXX flags */
){
- int rc;
- PgHdr *pPg;
+ int rc = SQLITE_OK;
+ PgHdr *pPg = 0;
+ u32 iFrame = 0; /* Frame to read from WAL file */
+ const int noContent = (flags & PAGER_GET_NOCONTENT);
+
+ /* It is acceptable to use a read-only (mmap) page for any page except
+ ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
+ ** flag was specified by the caller. And so long as the db is not a
+ ** temporary or in-memory database. */
+ const int bMmapOk = (pgno!=1 && USEFETCH(pPager)
+ && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
+#ifdef SQLITE_HAS_CODEC
+ && pPager->xCodec==0
+#endif
+ );
assert( pPager->eState>=PAGER_READER );
assert( assert_pager_state(pPager) );
+ assert( noContent==0 || bMmapOk==0 );
if( pgno==0 ){
return SQLITE_CORRUPT_BKPT;
@@ -41778,7 +46165,48 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
if( pPager->errCode!=SQLITE_OK ){
rc = pPager->errCode;
}else{
- rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage);
+ if( bMmapOk && pagerUseWal(pPager) ){
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
+ if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ }
+
+ if( bMmapOk && iFrame==0 ){
+ void *pData = 0;
+
+ rc = sqlite3OsFetch(pPager->fd,
+ (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData
+ );
+
+ if( rc==SQLITE_OK && pData ){
+ if( pPager->eState>PAGER_READER ){
+ pPg = sqlite3PagerLookup(pPager, pgno);
+ }
+ if( pPg==0 ){
+ rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg);
+ }else{
+ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData);
+ }
+ if( pPg ){
+ assert( rc==SQLITE_OK );
+ *ppPage = pPg;
+ return SQLITE_OK;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ goto pager_acquire_err;
+ }
+ }
+
+ {
+ sqlite3_pcache_page *pBase;
+ pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
+ if( pBase==0 ){
+ rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
+ if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ }
+ pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase);
+ if( pPg==0 ) rc = SQLITE_NOMEM;
+ }
}
if( rc!=SQLITE_OK ){
@@ -41795,14 +46223,13 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
/* In this case the pcache already contains an initialized copy of
** the page. Return without further ado. */
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
- PAGER_INCR(pPager->nHit);
+ pPager->aStat[PAGER_STAT_HIT]++;
return SQLITE_OK;
}else{
/* The pager cache has created a new page. Its content needs to
** be initialized. */
- PAGER_INCR(pPager->nMiss);
pPg = *ppPage;
pPg->pPager = pPager;
@@ -41837,8 +46264,13 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
memset(pPg->pData, 0, pPager->pageSize);
IOTRACE(("ZERO %p %d\n", pPager, pgno));
}else{
+ if( pagerUseWal(pPager) && bMmapOk==0 ){
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
+ if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ }
assert( pPg->pPager==pPager );
- rc = readDbPage(pPg);
+ pPager->aStat[PAGER_STAT_MISS]++;
+ rc = readDbPage(pPg, iFrame);
if( rc!=SQLITE_OK ){
goto pager_acquire_err;
}
@@ -41871,13 +46303,12 @@ pager_acquire_err:
** has ever happened.
*/
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
- PgHdr *pPg = 0;
+ sqlite3_pcache_page *pPage;
assert( pPager!=0 );
assert( pgno!=0 );
assert( pPager->pPCache!=0 );
- assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR );
- sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
- return pPg;
+ pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0);
+ return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
}
/*
@@ -41888,12 +46319,19 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
** are released, a rollback occurs and the lock on the database is
** removed.
*/
-SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
- if( pPg ){
- Pager *pPager = pPg->pPager;
+SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){
+ Pager *pPager;
+ assert( pPg!=0 );
+ pPager = pPg->pPager;
+ if( pPg->flags & PGHDR_MMAP ){
+ pagerReleaseMapPage(pPg);
+ }else{
sqlite3PcacheRelease(pPg);
- pagerUnlockIfUnused(pPager);
}
+ pagerUnlockIfUnused(pPager);
+}
+SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
+ if( pPg ) sqlite3PagerUnrefNotNull(pPg);
}
/*
@@ -41948,13 +46386,19 @@ static int pager_open_journal(Pager *pPager){
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
(SQLITE_OPEN_MAIN_JOURNAL)
);
- #ifdef SQLITE_ENABLE_ATOMIC_WRITE
- rc = sqlite3JournalOpen(
- pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
- );
- #else
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
- #endif
+
+ /* Verify that the database still has the same name as it did when
+ ** it was originally opened. */
+ rc = databaseIsUnmoved(pPager);
+ if( rc==SQLITE_OK ){
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ rc = sqlite3JournalOpen(
+ pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
+ );
+#else
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
+#endif
+ }
}
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
}
@@ -42075,9 +46519,9 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
** of any open savepoints as appropriate.
*/
static int pager_write(PgHdr *pPg){
- void *pData = pPg->pData;
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
+ int inJournal;
/* This routine is not called unless a write-transaction has already
** been started. The journal file may or may not be open at this point.
@@ -42088,14 +46532,8 @@ static int pager_write(PgHdr *pPg){
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
-
- /* If an error has been previously detected, report the same error
- ** again. This should not happen, but the check provides robustness. */
- if( NEVER(pPager->errCode) ) return pPager->errCode;
-
- /* Higher-level routines never call this function if database is not
- ** writable. But check anyway, just for robustness. */
- if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
+ assert( pPager->errCode==0 );
+ assert( pPager->readOnly==0 );
CHECK_PAGE(pPg);
@@ -42119,7 +46557,8 @@ static int pager_write(PgHdr *pPg){
** to the journal then we can return right away.
*/
sqlite3PcacheMakeDirty(pPg);
- if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
+ inJournal = pageInJournal(pPager, pPg);
+ if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){
assert( !pagerUseWal(pPager) );
}else{
@@ -42127,7 +46566,7 @@ static int pager_write(PgHdr *pPg){
** EXCLUSIVE lock on the main database file. Write the current page to
** the transaction journal if it is not there already.
*/
- if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
+ if( !inJournal && !pagerUseWal(pPager) ){
assert( pagerUseWal(pPager)==0 );
if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
u32 cksum;
@@ -42140,7 +46579,7 @@ static int pager_write(PgHdr *pPg){
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
assert( pPager->journalHdr<=pPager->journalOff );
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
cksum = pager_cksum(pPager, (u8*)pData2);
/* Even if an IO or diskfull error occurs while journalling the
@@ -42192,7 +46631,7 @@ static int pager_write(PgHdr *pPg){
** the statement journal format differs from the standard journal format
** in that it omits the checksums and the header.
*/
- if( subjRequiresPage(pPg) ){
+ if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
rc = subjournalPage(pPg);
}
}
@@ -42206,108 +46645,120 @@ static int pager_write(PgHdr *pPg){
}
/*
-** Mark a data page as writeable. This routine must be called before
-** making changes to a page. The caller must check the return value
-** of this function and be careful not to change any page data unless
-** this routine returns SQLITE_OK.
+** This is a variant of sqlite3PagerWrite() that runs when the sector size
+** is larger than the page size. SQLite makes the (reasonable) assumption that
+** all bytes of a sector are written together by hardware. Hence, all bytes of
+** a sector need to be journalled in case of a power loss in the middle of
+** a write.
**
-** The difference between this function and pager_write() is that this
-** function also deals with the special case where 2 or more pages
-** fit on a single disk sector. In this case all co-resident pages
-** must have been written to the journal file before returning.
-**
-** If an error occurs, SQLITE_NOMEM or an IO error code is returned
-** as appropriate. Otherwise, SQLITE_OK.
+** Usually, the sector size is less than or equal to the page size, in which
+** case pages can be individually written. This routine only runs in the exceptional
+** case where the page size is smaller than the sector size.
*/
-SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
- int rc = SQLITE_OK;
-
- PgHdr *pPg = pDbPage;
- Pager *pPager = pPg->pPager;
+static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
+ int rc = SQLITE_OK; /* Return code */
+ Pgno nPageCount; /* Total number of pages in database file */
+ Pgno pg1; /* First page of the sector pPg is located on. */
+ int nPage = 0; /* Number of pages starting at pg1 to journal */
+ int ii; /* Loop counter */
+ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
+ Pager *pPager = pPg->pPager; /* The pager that owns pPg */
Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
- assert( pPager->eState>=PAGER_WRITER_LOCKED );
- assert( pPager->eState!=PAGER_ERROR );
- assert( assert_pager_state(pPager) );
-
- if( nPagePerSector>1 ){
- Pgno nPageCount; /* Total number of pages in database file */
- Pgno pg1; /* First page of the sector pPg is located on. */
- int nPage = 0; /* Number of pages starting at pg1 to journal */
- int ii; /* Loop counter */
- int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
-
- /* Set the doNotSyncSpill flag to 1. This is because we cannot allow
- ** a journal header to be written between the pages journaled by
- ** this function.
- */
- assert( !MEMDB );
- assert( pPager->doNotSyncSpill==0 );
- pPager->doNotSyncSpill++;
+ /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
+ ** a journal header to be written between the pages journaled by
+ ** this function.
+ */
+ assert( !MEMDB );
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 );
+ pPager->doNotSpill |= SPILLFLAG_NOSYNC;
- /* This trick assumes that both the page-size and sector-size are
- ** an integer power of 2. It sets variable pg1 to the identifier
- ** of the first page of the sector pPg is located on.
- */
- pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
+ /* This trick assumes that both the page-size and sector-size are
+ ** an integer power of 2. It sets variable pg1 to the identifier
+ ** of the first page of the sector pPg is located on.
+ */
+ pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
- nPageCount = pPager->dbSize;
- if( pPg->pgno>nPageCount ){
- nPage = (pPg->pgno - pg1)+1;
- }else if( (pg1+nPagePerSector-1)>nPageCount ){
- nPage = nPageCount+1-pg1;
- }else{
- nPage = nPagePerSector;
- }
- assert(nPage>0);
- assert(pg1<=pPg->pgno);
- assert((pg1+nPage)>pPg->pgno);
-
- for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
- Pgno pg = pg1+ii;
- PgHdr *pPage;
- if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
- if( pg!=PAGER_MJ_PGNO(pPager) ){
- rc = sqlite3PagerGet(pPager, pg, &pPage);
- if( rc==SQLITE_OK ){
- rc = pager_write(pPage);
- if( pPage->flags&PGHDR_NEED_SYNC ){
- needSync = 1;
- }
- sqlite3PagerUnref(pPage);
+ nPageCount = pPager->dbSize;
+ if( pPg->pgno>nPageCount ){
+ nPage = (pPg->pgno - pg1)+1;
+ }else if( (pg1+nPagePerSector-1)>nPageCount ){
+ nPage = nPageCount+1-pg1;
+ }else{
+ nPage = nPagePerSector;
+ }
+ assert(nPage>0);
+ assert(pg1<=pPg->pgno);
+ assert((pg1+nPage)>pPg->pgno);
+
+ for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
+ Pgno pg = pg1+ii;
+ PgHdr *pPage;
+ if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
+ if( pg!=PAGER_MJ_PGNO(pPager) ){
+ rc = sqlite3PagerGet(pPager, pg, &pPage);
+ if( rc==SQLITE_OK ){
+ rc = pager_write(pPage);
+ if( pPage->flags&PGHDR_NEED_SYNC ){
+ needSync = 1;
}
+ sqlite3PagerUnrefNotNull(pPage);
}
- }else if( (pPage = pager_lookup(pPager, pg))!=0 ){
- if( pPage->flags&PGHDR_NEED_SYNC ){
- needSync = 1;
- }
- sqlite3PagerUnref(pPage);
}
+ }else if( (pPage = sqlite3PagerLookup(pPager, pg))!=0 ){
+ if( pPage->flags&PGHDR_NEED_SYNC ){
+ needSync = 1;
+ }
+ sqlite3PagerUnrefNotNull(pPage);
}
+ }
- /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
- ** starting at pg1, then it needs to be set for all of them. Because
- ** writing to any of these nPage pages may damage the others, the
- ** journal file must contain sync()ed copies of all of them
- ** before any of them can be written out to the database file.
- */
- if( rc==SQLITE_OK && needSync ){
- assert( !MEMDB );
- for(ii=0; ii<nPage; ii++){
- PgHdr *pPage = pager_lookup(pPager, pg1+ii);
- if( pPage ){
- pPage->flags |= PGHDR_NEED_SYNC;
- sqlite3PagerUnref(pPage);
- }
+ /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
+ ** starting at pg1, then it needs to be set for all of them. Because
+ ** writing to any of these nPage pages may damage the others, the
+ ** journal file must contain sync()ed copies of all of them
+ ** before any of them can be written out to the database file.
+ */
+ if( rc==SQLITE_OK && needSync ){
+ assert( !MEMDB );
+ for(ii=0; ii<nPage; ii++){
+ PgHdr *pPage = sqlite3PagerLookup(pPager, pg1+ii);
+ if( pPage ){
+ pPage->flags |= PGHDR_NEED_SYNC;
+ sqlite3PagerUnrefNotNull(pPage);
}
}
+ }
- assert( pPager->doNotSyncSpill==1 );
- pPager->doNotSyncSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_NOSYNC;
+ return rc;
+}
+
+/*
+** Mark a data page as writeable. This routine must be called before
+** making changes to a page. The caller must check the return value
+** of this function and be careful not to change any page data unless
+** this routine returns SQLITE_OK.
+**
+** The difference between this function and pager_write() is that this
+** function also deals with the special case where 2 or more pages
+** fit on a single disk sector. In this case all co-resident pages
+** must have been written to the journal file before returning.
+**
+** If an error occurs, SQLITE_NOMEM or an IO error code is returned
+** as appropriate. Otherwise, SQLITE_OK.
+*/
+SQLITE_PRIVATE int sqlite3PagerWrite(PgHdr *pPg){
+ assert( (pPg->flags & PGHDR_MMAP)==0 );
+ assert( pPg->pPager->eState>=PAGER_WRITER_LOCKED );
+ assert( pPg->pPager->eState!=PAGER_ERROR );
+ assert( assert_pager_state(pPg->pPager) );
+ if( pPg->pPager->sectorSize > (u32)pPg->pPager->pageSize ){
+ return pagerWriteLargeSector(pPg);
}else{
- rc = pager_write(pDbPage);
+ return pager_write(pPg);
}
- return rc;
}
/*
@@ -42393,7 +46844,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
# define DIRECT_MODE isDirectMode
#endif
- if( !pPager->changeCountDone && pPager->dbSize>0 ){
+ if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
@@ -42422,8 +46873,14 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf);
if( rc==SQLITE_OK ){
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
+ pPager->aStat[PAGER_STAT_WRITE]++;
}
if( rc==SQLITE_OK ){
+ /* Update the pager's copy of the change-counter. Otherwise, the
+ ** next time a read transaction is opened the cache will be
+ ** flushed (as the change-counter values will not match). */
+ const void *pCopy = (const void *)&((const char *)zBuf)[24];
+ memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers));
pPager->changeCountDone = 1;
}
}else{
@@ -42444,14 +46901,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
** If successful, or if called on a pager for which it is a no-op, this
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
-SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){
+SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){
int rc = SQLITE_OK;
- if( !pPager->noSync ){
+
+ if( isOpen(pPager->fd) ){
+ void *pArg = (void*)zMaster;
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ }
+ if( rc==SQLITE_OK && !pPager->noSync ){
assert( !MEMDB );
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
- }else if( isOpen(pPager->fd) ){
- assert( !MEMDB );
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc);
}
return rc;
}
@@ -42548,9 +47008,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
}
assert( rc==SQLITE_OK );
if( ALWAYS(pList) ){
- rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
- (pPager->fullSync ? pPager->syncFlags : 0)
- );
+ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1);
}
sqlite3PagerUnref(pPageOne);
if( rc==SQLITE_OK ){
@@ -42609,38 +47067,6 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
#endif
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
- /* If this transaction has made the database smaller, then all pages
- ** being discarded by the truncation must be written to the journal
- ** file. This can only happen in auto-vacuum mode.
- **
- ** Before reading the pages with page numbers larger than the
- ** current value of Pager.dbSize, set dbSize back to the value
- ** that it took at the start of the transaction. Otherwise, the
- ** calls to sqlite3PagerGet() return zeroed pages instead of
- ** reading data from the database file.
- */
- #ifndef SQLITE_OMIT_AUTOVACUUM
- if( pPager->dbSize<pPager->dbOrigSize
- && pPager->journalMode!=PAGER_JOURNALMODE_OFF
- ){
- Pgno i; /* Iterator variable */
- const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
- const Pgno dbSize = pPager->dbSize; /* Database image size */
- pPager->dbSize = pPager->dbOrigSize;
- for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){
- if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
- PgHdr *pPage; /* Page to journal */
- rc = sqlite3PagerGet(pPager, i, &pPage);
- if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
- rc = sqlite3PagerWrite(pPage);
- sqlite3PagerUnref(pPage);
- if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
- }
- }
- pPager->dbSize = dbSize;
- }
- #endif
-
/* Write the master journal name into the journal file. If a master
** journal file name has already been written to the journal file,
** or if zMaster is NULL (no master journal), then this call is a no-op.
@@ -42668,11 +47094,14 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
goto commit_phase_one_exit;
}
sqlite3PcacheCleanAll(pPager->pPCache);
-
- /* If the file on disk is not the same size as the database image,
- ** then use pager_truncate to grow or shrink the file here.
- */
- if( pPager->dbSize!=pPager->dbFileSize ){
+
+ /* If the file on disk is smaller than the database image, use
+ ** pager_truncate to grow the file here. This can happen if the database
+ ** image was extended as part of the current transaction and then the
+ ** last page in the db image moved to the free-list. In this case the
+ ** last page is never written out to disk, leaving the database file
+ ** undersized. Fix this now if it is the case. */
+ if( pPager->dbSize>pPager->dbFileSize ){
Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
assert( pPager->eState==PAGER_WRITER_DBMOD );
rc = pager_truncate(pPager, nNew);
@@ -42681,7 +47110,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
/* Finally, sync the database file. */
if( !noSync ){
- rc = sqlite3PagerSync(pPager);
+ rc = sqlite3PagerSync(pPager, zMaster);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
@@ -42745,7 +47174,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
}
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
- rc = pager_end_transaction(pPager, pPager->setMaster);
+ rc = pager_end_transaction(pPager, pPager->setMaster, 1);
return pager_error(pPager, rc);
}
@@ -42790,11 +47219,11 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
if( pagerUseWal(pPager) ){
int rc2;
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
- rc2 = pager_end_transaction(pPager, pPager->setMaster);
+ rc2 = pager_end_transaction(pPager, pPager->setMaster, 0);
if( rc==SQLITE_OK ) rc = rc2;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
int eState = pPager->eState;
- rc = pager_end_transaction(pPager, 0);
+ rc = pager_end_transaction(pPager, 0, 0);
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
/* This can happen using journal_mode=off. Move the pager to the error
** state to indicate that the contents of the cache may not be trusted.
@@ -42809,7 +47238,10 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
}
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
- assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
+ assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT
+ || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR
+ || rc==SQLITE_CANTOPEN
+ );
/* If an error occurs during a ROLLBACK, we can no longer trust the pager
** cache. So call pager_error() on the way out to make any error persistent.
@@ -42863,16 +47295,40 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
- a[6] = pPager->nHit;
- a[7] = pPager->nMiss;
+ a[6] = pPager->aStat[PAGER_STAT_HIT];
+ a[7] = pPager->aStat[PAGER_STAT_MISS];
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
- a[10] = pPager->nWrite;
+ a[10] = pPager->aStat[PAGER_STAT_WRITE];
return a;
}
#endif
/*
+** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
+** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
+** current cache hit or miss count, according to the value of eStat. If the
+** reset parameter is non-zero, the cache hit or miss count is zeroed before
+** returning.
+*/
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+
+ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
+ || eStat==SQLITE_DBSTATUS_CACHE_MISS
+ || eStat==SQLITE_DBSTATUS_CACHE_WRITE
+ );
+
+ assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS );
+ assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE );
+ assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 );
+
+ *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT];
+ if( reset ){
+ pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
+ }
+}
+
+/*
** Return true if this is an in-memory pager.
*/
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
@@ -43017,9 +47473,16 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
/*
** Return the full pathname of the database file.
+**
+** Except, if the pager is in-memory only, then return an empty string if
+** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when
+** used to report the filename to the user, for compatibility with legacy
+** behavior. But when the Btree needs to know the filename for matching to
+** shared cache, it uses nullIfMemDb==0 so that in-memory databases can
+** participate in shared-cache.
*/
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager){
- return pPager->zFilename;
+SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
+ return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename;
}
/*
@@ -43074,7 +47537,27 @@ SQLITE_PRIVATE void sqlite3PagerSetCodec(
SQLITE_PRIVATE void *sqlite3PagerGetCodec(Pager *pPager){
return pPager->pCodec;
}
-#endif
+
+/*
+** This function is called by the wal module when writing page content
+** into the log file.
+**
+** This function returns a pointer to a buffer containing the encrypted
+** page content. If a malloc fails, this function may return NULL.
+*/
+SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
+ void *aData = 0;
+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
+ return aData;
+}
+
+/*
+** Return the current pager state
+*/
+SQLITE_PRIVATE int sqlite3PagerState(Pager *pPager){
+ return pPager->eState;
+}
+#endif /* SQLITE_HAS_CODEC */
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
@@ -43160,7 +47643,8 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
*/
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
needSyncPgno = pPg->pgno;
- assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
+ assert( pPager->journalMode==PAGER_JOURNALMODE_OFF ||
+ pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize );
assert( pPg->flags&PGHDR_DIRTY );
}
@@ -43170,7 +47654,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** for the page moved there.
*/
pPg->flags &= ~PGHDR_NEED_SYNC;
- pPgOld = pager_lookup(pPager, pgno);
+ pPgOld = sqlite3PagerLookup(pPager, pgno);
assert( !pPgOld || pPgOld->nRef==1 );
if( pPgOld ){
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
@@ -43194,7 +47678,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
if( MEMDB ){
assert( pPgOld );
sqlite3PcacheMove(pPgOld, origPgno);
- sqlite3PagerUnref(pPgOld);
+ sqlite3PagerUnrefNotNull(pPgOld);
}
if( needSyncPgno ){
@@ -43223,7 +47707,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
}
pPgHdr->flags |= PGHDR_NEED_SYNC;
sqlite3PcacheMakeDirty(pPgHdr);
- sqlite3PagerUnref(pPgHdr);
+ sqlite3PagerUnrefNotNull(pPgHdr);
}
return SQLITE_OK;
@@ -43425,6 +47909,15 @@ SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
return &pPager->pBackup;
}
+#ifndef SQLITE_OMIT_VACUUM
+/*
+** Unless this is an in-memory or temporary database, clear the pager cache.
+*/
+SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){
+ if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
+}
+#endif
+
#ifndef SQLITE_OMIT_WAL
/*
** This function is called when the user invokes "PRAGMA wal_checkpoint",
@@ -43486,7 +47979,7 @@ static int pagerOpenWal(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->pWal==0 && pPager->tempFile==0 );
- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock);
+ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
/* If the pager is already in exclusive-mode, the WAL module will use
** heap-memory for the wal-index instead of the VFS shared-memory
@@ -43501,11 +47994,12 @@ static int pagerOpenWal(Pager *pPager){
** (e.g. due to malloc() failure), return an error code.
*/
if( rc==SQLITE_OK ){
- rc = sqlite3WalOpen(pPager->pVfs,
+ rc = sqlite3WalOpen(pPager->pVfs,
pPager->fd, pPager->zWal, pPager->exclusiveMode,
pPager->journalSizeLimit, &pPager->pWal
);
}
+ pagerFixMaplimit(pPager);
return rc;
}
@@ -43596,27 +48090,27 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager){
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
pPager->pageSize, (u8*)pPager->pTmpSpace);
pPager->pWal = 0;
+ pagerFixMaplimit(pPager);
}
}
return rc;
}
-#ifdef SQLITE_HAS_CODEC
+#endif /* !SQLITE_OMIT_WAL */
+
+#ifdef SQLITE_ENABLE_ZIPVFS
/*
-** This function is called by the wal module when writing page content
-** into the log file.
-**
-** This function returns a pointer to a buffer containing the encrypted
-** page content. If a malloc fails, this function may return NULL.
+** A read-lock must be held on the pager when this function is called. If
+** the pager is in WAL mode and the WAL file currently contains one or more
+** frames, return the size in bytes of the page images stored within the
+** WAL frames. Otherwise, if this is not a WAL database or the WAL file
+** is empty, return 0.
*/
-SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
- void *aData = 0;
- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
- return aData;
+SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
+ assert( pPager->eState>=PAGER_READER );
+ return sqlite3WalFramesize(pPager->pWal);
}
-#endif /* SQLITE_HAS_CODEC */
-
-#endif /* !SQLITE_OMIT_WAL */
+#endif
#endif /* SQLITE_OMIT_DISKIO */
@@ -43766,14 +48260,15 @@ SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
** byte order of the host computer.
**
** The purpose of the wal-index is to answer this question quickly: Given
-** a page number P, return the index of the last frame for page P in the WAL,
-** or return NULL if there are no frames for page P in the WAL.
+** a page number P and a maximum frame index M, return the index of the
+** last frame in the wal before frame M for page P in the WAL, or return
+** NULL if there are no frames for page P in the WAL prior to M.
**
** The wal-index consists of a header region, followed by an one or
** more index blocks.
**
** The wal-index header contains the total number of frames within the WAL
-** in the the mxFrame field.
+** in the mxFrame field.
**
** Each index block except for the first contains information on
** HASHTABLE_NPAGE frames. The first index block contains information on
@@ -44037,13 +48532,18 @@ struct Wal {
u32 iCallback; /* Value to pass to log callback (or 0) */
i64 mxWalSize; /* Truncate WAL to this size upon reset */
int nWiData; /* Size of array apWiData */
+ int szFirstBlock; /* Size of first block written to WAL file */
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
u32 szPage; /* Database page size */
i16 readLock; /* Which read lock is being held. -1 for none */
+ u8 syncFlags; /* Flags to use to sync header writes */
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
u8 writeLock; /* True if in a write transaction */
u8 ckptLock; /* True if holding a checkpoint lock */
u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
+ u8 truncateOnCommit; /* True to truncate WAL file on commit */
+ u8 syncHeader; /* Fsync the WAL header if true */
+ u8 padToSectorBoundary; /* Pad transactions out to the next sector */
WalIndexHdr hdr; /* Wal-index header for current transaction */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
@@ -44191,7 +48691,7 @@ static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
** The argument to this macro must be of type u32. On a little-endian
** architecture, it returns the u32 value that results from interpreting
** the 4 bytes as a big-endian value. On a big-endian architecture, it
-** returns the value that would be produced by intepreting the 4 bytes
+** returns the value that would be produced by interpreting the 4 bytes
** of the input value as a little-endian integer.
*/
#define BYTESWAP32(x) ( \
@@ -44605,7 +49105,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
assert( idx <= HASHTABLE_NSLOT/2 + 1 );
/* If this is the first entry to be added to this hash-table, zero the
- ** entire hash table and aPgno[] array before proceding.
+ ** entire hash table and aPgno[] array before proceeding.
*/
if( idx==1 ){
int nByte = (int)((u8 *)&aHash[HASHTABLE_NSLOT] - (u8 *)&aPgno[1]);
@@ -44716,6 +49216,7 @@ static int walIndexRecover(Wal *pWal){
int szPage; /* Page size according to the log */
u32 magic; /* Magic value read from WAL header */
u32 version; /* Magic value read from WAL header */
+ int isValid; /* True if this frame is valid */
/* Read in the WAL header. */
rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
@@ -44774,14 +49275,14 @@ static int walIndexRecover(Wal *pWal){
for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
u32 pgno; /* Database page number for frame */
u32 nTruncate; /* dbsize field from frame header */
- int isValid; /* True if this frame is valid */
/* Read and decode the next log frame. */
+ iFrame++;
rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
if( rc!=SQLITE_OK ) break;
isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
if( !isValid ) break;
- rc = walIndexAppend(pWal, ++iFrame, pgno);
+ rc = walIndexAppend(pWal, iFrame, pgno);
if( rc!=SQLITE_OK ) break;
/* If nTruncate is non-zero, this is a commit record. */
@@ -44815,6 +49316,7 @@ finished:
pInfo->nBackfill = 0;
pInfo->aReadMark[0] = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
/* If more than one frame was recovered from the log file, report an
** event via sqlite3_log(). This is to help with identifying performance
@@ -44822,8 +49324,9 @@ finished:
** checkpointing the log file.
*/
if( pWal->hdr.nPage ){
- sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s",
- pWal->hdr.nPage, pWal->zWalName
+ sqlite3_log(SQLITE_NOTICE_RECOVER_WAL,
+ "recovered %d frames from WAL file %s",
+ pWal->hdr.mxFrame, pWal->zWalName
);
}
}
@@ -44904,6 +49407,8 @@ SQLITE_PRIVATE int sqlite3WalOpen(
pRet->readLock = -1;
pRet->mxWalSize = mxWalSize;
pRet->zWalName = zWalName;
+ pRet->syncHeader = 1;
+ pRet->padToSectorBoundary = 1;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
/* Open file handle on the write-ahead log file. */
@@ -44918,6 +49423,11 @@ SQLITE_PRIVATE int sqlite3WalOpen(
sqlite3OsClose(pRet->pWalFd);
sqlite3_free(pRet);
}else{
+ int iDC = sqlite3OsDeviceCharacteristics(pDbFd);
+ if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
+ if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
+ pRet->padToSectorBoundary = 0;
+ }
*ppWal = pRet;
WALTRACE(("WAL%d: opened\n", pRet));
}
@@ -45253,7 +49763,7 @@ static int walPagesize(Wal *pWal){
** database file.
**
** This routine uses and updates the nBackfill field of the wal-index header.
-** This is the only routine tha will increase the value of nBackfill.
+** This is the only routine that will increase the value of nBackfill.
** (A WAL reset or recovery will revert nBackfill to zero, but not increase
** its value.)
**
@@ -45308,7 +49818,7 @@ static int walCheckpoint(
assert( y<=pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
- pInfo->aReadMark[i] = READMARK_NOT_USED;
+ pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
@@ -45330,17 +49840,18 @@ static int walCheckpoint(
rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
}
- /* If the database file may grow as a result of this checkpoint, hint
- ** about the eventual size of the db file to the VFS layer.
+ /* If the database may grow as a result of this checkpoint, hint
+ ** about the eventual size of the db file to the VFS layer.
*/
if( rc==SQLITE_OK ){
i64 nReq = ((i64)mxPage * szPage);
rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
if( rc==SQLITE_OK && nSize<nReq ){
- sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
+ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
}
}
+
/* Iterate through the contents of the WAL, copying data to the db file. */
while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
i64 iOffset;
@@ -45405,6 +49916,24 @@ static int walCheckpoint(
}
/*
+** If the WAL file is currently larger than nMax bytes in size, truncate
+** it to exactly nMax bytes. If an error occurs while doing so, ignore it.
+*/
+static void walLimitSize(Wal *pWal, i64 nMax){
+ i64 sz;
+ int rx;
+ sqlite3BeginBenignMalloc();
+ rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
+ if( rx==SQLITE_OK && (sz > nMax ) ){
+ rx = sqlite3OsTruncate(pWal->pWalFd, nMax);
+ }
+ sqlite3EndBenignMalloc();
+ if( rx ){
+ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
+ }
+}
+
+/*
** Close a connection to a log file.
*/
SQLITE_PRIVATE int sqlite3WalClose(
@@ -45434,14 +49963,33 @@ SQLITE_PRIVATE int sqlite3WalClose(
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
);
if( rc==SQLITE_OK ){
- isDelete = 1;
+ int bPersist = -1;
+ sqlite3OsFileControlHint(
+ pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist
+ );
+ if( bPersist!=1 ){
+ /* Try to delete the WAL file if the checkpoint completed and
+ ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal
+ ** mode (!bPersist) */
+ isDelete = 1;
+ }else if( pWal->mxWalSize>=0 ){
+ /* Try to truncate the WAL file to zero bytes if the checkpoint
+ ** completed and fsynced (rc==SQLITE_OK) and we are in persistent
+ ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a
+ ** non-negative value (pWal->mxWalSize>=0). Note that we truncate
+ ** to zero bytes as truncating to the journal_size_limit might
+ ** leave a corrupt WAL file on disk. */
+ walLimitSize(pWal, 0);
+ }
}
}
walIndexClose(pWal, isDelete);
sqlite3OsClose(pWal->pWalFd);
if( isDelete ){
+ sqlite3BeginBenignMalloc();
sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
+ sqlite3EndBenignMalloc();
}
WALTRACE(("WAL%p: closed\n", pWal));
sqlite3_free((void *)pWal->apWiData);
@@ -45519,7 +50067,7 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){
** wal-index from the WAL before returning.
**
** Set *pChanged to 1 if the wal-index header value in pWal->hdr is
-** changed by this opertion. If pWal->hdr is unchanged, set *pChanged
+** changed by this operation. If pWal->hdr is unchanged, set *pChanged
** to 0.
**
** If the wal-index header is successfully read, return SQLITE_OK.
@@ -45665,8 +50213,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
** is more of a scheduler yield than an actual delay. But on the 10th
** an subsequent retries, the delays start becoming longer and longer,
- ** so that on the 100th (and last) RETRY we delay for 21 milliseconds.
- ** The total delay time before giving up is less than 1 second.
+ ** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
+ ** The total delay time before giving up is less than 10 seconds.
*/
if( cnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
@@ -45674,7 +50222,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */
+ if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
sqlite3OsSleep(pWal->pVfs, nDelay);
}
@@ -45723,7 +50271,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** may have been appended to the log before READ_LOCK(0) was obtained.
** When holding READ_LOCK(0), the reader ignores the entire log file,
** which implies that the database file contains a trustworthy
- ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from
+ ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
** happening, this is usually correct.
**
** However, if frames have been appended to the log (or if the log
@@ -45858,19 +50406,17 @@ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
}
/*
-** Read a page from the WAL, if it is present in the WAL and if the
-** current read transaction is configured to use the WAL.
+** Search the wal file for page pgno. If found, set *piRead to the frame that
+** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
+** to zero.
**
-** The *pInWal is set to 1 if the requested page is in the WAL and
-** has been loaded. Or *pInWal is set to 0 if the page was not in
-** the WAL and needs to be read out of the database.
+** Return SQLITE_OK if successful, or an error code if an error occurs. If an
+** error does occur, the final value of *piRead is undefined.
*/
-SQLITE_PRIVATE int sqlite3WalRead(
+SQLITE_PRIVATE int sqlite3WalFindFrame(
Wal *pWal, /* WAL handle */
Pgno pgno, /* Database page number to read data for */
- int *pInWal, /* OUT: True if data is read from WAL */
- int nOut, /* Size of buffer pOut in bytes */
- u8 *pOut /* Buffer to write page data to */
+ u32 *piRead /* OUT: Frame number (or zero) */
){
u32 iRead = 0; /* If !=0, WAL frame to return data from */
u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */
@@ -45886,7 +50432,7 @@ SQLITE_PRIVATE int sqlite3WalRead(
** WAL were empty.
*/
if( iLast==0 || pWal->readLock==0 ){
- *pInWal = 0;
+ *piRead = 0;
return SQLITE_OK;
}
@@ -45931,7 +50477,7 @@ SQLITE_PRIVATE int sqlite3WalRead(
for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
u32 iFrame = aHash[iKey] + iZero;
if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){
- assert( iFrame>iRead );
+ /* assert( iFrame>iRead ); -- not true if there is corruption */
iRead = iFrame;
}
if( (nCollide--)==0 ){
@@ -45957,26 +50503,31 @@ SQLITE_PRIVATE int sqlite3WalRead(
}
#endif
- /* If iRead is non-zero, then it is the log frame number that contains the
- ** required page. Read and return data from the log file.
- */
- if( iRead ){
- int sz;
- i64 iOffset;
- sz = pWal->hdr.szPage;
- sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
- testcase( sz<=32768 );
- testcase( sz>=65536 );
- iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
- *pInWal = 1;
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
- return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset);
- }
-
- *pInWal = 0;
+ *piRead = iRead;
return SQLITE_OK;
}
+/*
+** Read the contents of frame iRead from the wal file into buffer pOut
+** (which is nOut bytes in size). Return SQLITE_OK if successful, or an
+** error code otherwise.
+*/
+SQLITE_PRIVATE int sqlite3WalReadFrame(
+ Wal *pWal, /* WAL handle */
+ u32 iRead, /* Frame to read */
+ int nOut, /* Size of buffer pOut in bytes */
+ u8 *pOut /* Buffer to write page data to */
+){
+ int sz;
+ i64 iOffset;
+ sz = pWal->hdr.szPage;
+ sz = (sz&0xfe00) + ((sz&0x0001)<<16);
+ testcase( sz<=32768 );
+ testcase( sz>=65536 );
+ iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
+ /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
+ return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
+}
/*
** Return the size of the database in pages (or zero, if unknown).
@@ -46029,7 +50580,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
- rc = SQLITE_BUSY;
+ rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
@@ -46043,6 +50594,7 @@ SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){
if( pWal->writeLock ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
+ pWal->truncateOnCommit = 0;
}
return SQLITE_OK;
}
@@ -46088,9 +50640,8 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *p
assert( walFramePgno(pWal, iFrame)!=1 );
rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
}
- walCleanupHash(pWal);
+ if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
}
- assert( rc==SQLITE_OK );
return rc;
}
@@ -46139,6 +50690,7 @@ SQLITE_PRIVATE int sqlite3WalSavepointUndo(Wal *pWal, u32 *aWalData){
return rc;
}
+
/*
** This function is called just before writing a set of frames to the log
** file (see sqlite3WalFrames()). It checks to see if, instead of appending
@@ -46176,30 +50728,14 @@ static int walRestartLog(Wal *pWal){
int i; /* Loop counter */
u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
- /* Limit the size of WAL file if the journal_size_limit PRAGMA is
- ** set to a non-negative value. Log errors encountered
- ** during the truncation attempt. */
- if( pWal->mxWalSize>=0 ){
- i64 sz;
- int rx;
- sqlite3BeginBenignMalloc();
- rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
- if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
- rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
- }
- sqlite3EndBenignMalloc();
- if( rx ){
- sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
- }
- }
-
pWal->nCkpt++;
pWal->hdr.mxFrame = 0;
sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
aSalt[1] = salt1;
walIndexWriteHdr(pWal);
pInfo->nBackfill = 0;
- for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ pInfo->aReadMark[1] = 0;
+ for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
assert( pInfo->aReadMark[0]==0 );
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
}else if( rc!=SQLITE_BUSY ){
@@ -46221,6 +50757,74 @@ static int walRestartLog(Wal *pWal){
return rc;
}
+/*
+** Information about the current state of the WAL file and where
+** the next fsync should occur - passed from sqlite3WalFrames() into
+** walWriteToLog().
+*/
+typedef struct WalWriter {
+ Wal *pWal; /* The complete WAL information */
+ sqlite3_file *pFd; /* The WAL file to which we write */
+ sqlite3_int64 iSyncPoint; /* Fsync at this offset */
+ int syncFlags; /* Flags for the fsync */
+ int szPage; /* Size of one page */
+} WalWriter;
+
+/*
+** Write iAmt bytes of content into the WAL file beginning at iOffset.
+** Do a sync when crossing the p->iSyncPoint boundary.
+**
+** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
+** first write the part before iSyncPoint, then sync, then write the
+** rest.
+*/
+static int walWriteToLog(
+ WalWriter *p, /* WAL to write to */
+ void *pContent, /* Content to be written */
+ int iAmt, /* Number of bytes to write */
+ sqlite3_int64 iOffset /* Start writing at this offset */
+){
+ int rc;
+ if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
+ int iFirstAmt = (int)(p->iSyncPoint - iOffset);
+ rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
+ if( rc ) return rc;
+ iOffset += iFirstAmt;
+ iAmt -= iFirstAmt;
+ pContent = (void*)(iFirstAmt + (char*)pContent);
+ assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
+ rc = sqlite3OsSync(p->pFd, p->syncFlags & SQLITE_SYNC_MASK);
+ if( iAmt==0 || rc ) return rc;
+ }
+ rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
+ return rc;
+}
+
+/*
+** Write out a single frame of the WAL
+*/
+static int walWriteOneFrame(
+ WalWriter *p, /* Where to write the frame */
+ PgHdr *pPage, /* The page of the frame to be written */
+ int nTruncate, /* The commit flag. Usually 0. >0 for commit */
+ sqlite3_int64 iOffset /* Byte offset at which to write */
+){
+ int rc; /* Result code from subfunctions */
+ void *pData; /* Data actually written */
+ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
+#if defined(SQLITE_HAS_CODEC)
+ if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
+#else
+ pData = pPage->pData;
+#endif
+ walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
+ rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
+ if( rc ) return rc;
+ /* Write the page data */
+ rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
+ return rc;
+}
+
/*
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
@@ -46235,14 +50839,20 @@ SQLITE_PRIVATE int sqlite3WalFrames(
){
int rc; /* Used to catch return codes */
u32 iFrame; /* Next frame address */
- u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
PgHdr *p; /* Iterator to run through pList with. */
PgHdr *pLast = 0; /* Last frame in list */
- int nLast = 0; /* Number of extra copies of last page */
+ int nExtra = 0; /* Number of extra copies of last page */
+ int szFrame; /* The size of a single frame */
+ i64 iOffset; /* Next byte to write in WAL file */
+ WalWriter w; /* The writer */
assert( pList );
assert( pWal->writeLock );
+ /* If this frame set completes a transaction, then nTruncate>0. If
+ ** nTruncate==0 then this frame set does not complete the transaction. */
+ assert( (isCommit!=0)==(nTruncate!=0) );
+
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
{ int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
@@ -46270,7 +50880,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
sqlite3Put4byte(&aWalHdr[8], szPage);
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
- sqlite3_randomness(8, pWal->hdr.aSalt);
+ if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
@@ -46280,77 +50890,89 @@ SQLITE_PRIVATE int sqlite3WalFrames(
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
pWal->hdr.aFrameCksum[0] = aCksum[0];
pWal->hdr.aFrameCksum[1] = aCksum[1];
+ pWal->truncateOnCommit = 1;
rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
if( rc!=SQLITE_OK ){
return rc;
}
+
+ /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
+ ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
+ ** an out-of-order write following a WAL restart could result in
+ ** database corruption. See the ticket:
+ **
+ ** http://localhost:591/sqlite/info/ff5be73dee
+ */
+ if( pWal->syncHeader && sync_flags ){
+ rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
+ if( rc ) return rc;
+ }
}
assert( (int)pWal->szPage==szPage );
- /* Write the log file. */
- for(p=pList; p; p=p->pDirty){
- u32 nDbsize; /* Db-size field for frame header */
- i64 iOffset; /* Write offset in log file */
- void *pData;
-
- iOffset = walFrameOffset(++iFrame, szPage);
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
-
- /* Populate and write the frame header */
- nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0;
-#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
-#else
- pData = p->pData;
-#endif
- walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame);
- rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ /* Setup information needed to write frames into the WAL */
+ w.pWal = pWal;
+ w.pFd = pWal->pWalFd;
+ w.iSyncPoint = 0;
+ w.syncFlags = sync_flags;
+ w.szPage = szPage;
+ iOffset = walFrameOffset(iFrame+1, szPage);
+ szFrame = szPage + WAL_FRAME_HDRSIZE;
- /* Write the page data */
- rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset+sizeof(aFrame));
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ /* Write all frames into the log file exactly once */
+ for(p=pList; p; p=p->pDirty){
+ int nDbSize; /* 0 normally. Positive == commit flag */
+ iFrame++;
+ assert( iOffset==walFrameOffset(iFrame, szPage) );
+ nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
+ rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
+ if( rc ) return rc;
pLast = p;
+ iOffset += szFrame;
}
- /* Sync the log file if the 'isSync' flag was specified. */
- if( sync_flags ){
- i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd);
- i64 iOffset = walFrameOffset(iFrame+1, szPage);
-
- assert( isCommit );
- assert( iSegment>0 );
-
- iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment);
- while( iOffset<iSegment ){
- void *pData;
-#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM;
-#else
- pData = pLast->pData;
-#endif
- walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
- rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- iOffset += WAL_FRAME_HDRSIZE;
- rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset);
- if( rc!=SQLITE_OK ){
- return rc;
+ /* If this is the end of a transaction, then we might need to pad
+ ** the transaction and/or sync the WAL file.
+ **
+ ** Padding and syncing only occur if this set of frames complete a
+ ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
+ ** or synchronous==OFF, then no padding or syncing are needed.
+ **
+ ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
+ ** needed and only the sync is done. If padding is needed, then the
+ ** final frame is repeated (with its commit mark) until the next sector
+ ** boundary is crossed. Only the part of the WAL prior to the last
+ ** sector boundary is synced; the part of the last frame that extends
+ ** past the sector boundary is written after the sync.
+ */
+ if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
+ if( pWal->padToSectorBoundary ){
+ int sectorSize = sqlite3SectorSize(pWal->pWalFd);
+ w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
+ while( iOffset<w.iSyncPoint ){
+ rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
+ if( rc ) return rc;
+ iOffset += szFrame;
+ nExtra++;
}
- nLast++;
- iOffset += szPage;
+ }else{
+ rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
}
+ }
- rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
+ /* If this frame set completes the first transaction in the WAL and
+ ** if PRAGMA journal_size_limit is set, then truncate the WAL to the
+ ** journal size limit, if possible.
+ */
+ if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
+ i64 sz = pWal->mxWalSize;
+ if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
+ sz = walFrameOffset(iFrame+nExtra+1, szPage);
+ }
+ walLimitSize(pWal, sz);
+ pWal->truncateOnCommit = 0;
}
/* Append data to the wal-index. It is not necessary to lock the
@@ -46363,9 +50985,9 @@ SQLITE_PRIVATE int sqlite3WalFrames(
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
- while( nLast>0 && rc==SQLITE_OK ){
+ while( rc==SQLITE_OK && nExtra>0 ){
iFrame++;
- nLast--;
+ nExtra--;
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
}
@@ -46451,6 +51073,9 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
/* Read the wal-index header. */
if( rc==SQLITE_OK ){
rc = walIndexReadHdr(pWal, &isChanged);
+ if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
+ sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
+ }
}
/* Copy data from the log to the database file. */
@@ -46570,6 +51195,18 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
}
+#ifdef SQLITE_ENABLE_ZIPVFS
+/*
+** If the argument is not NULL, it points to a Wal object that holds a
+** read-lock. This function returns the database page-size if it is known,
+** or zero if it is not (or if pWal is NULL).
+*/
+SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
+ assert( pWal==0 || pWal->readLock>=0 );
+ return (pWal ? pWal->szPage : 0);
+}
+#endif
+
#endif /* #ifndef SQLITE_OMIT_WAL */
/************** End of wal.c *************************************************/
@@ -46604,7 +51241,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file implements a external (disk-based) database using BTrees.
+** This file implements an external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
@@ -46651,13 +51288,13 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
**
** OFFSET SIZE DESCRIPTION
** 0 16 Header string: "SQLite format 3\000"
-** 16 2 Page size in bytes.
+** 16 2 Page size in bytes. (1 means 65536)
** 18 1 File format write version
** 19 1 File format read version
** 20 1 Bytes of unused space at the end of each page
-** 21 1 Max embedded payload fraction
-** 22 1 Min embedded payload fraction
-** 23 1 Min leaf payload fraction
+** 21 1 Max embedded payload fraction (must be 64)
+** 22 1 Min embedded payload fraction (must be 32)
+** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
** 28 4 Reserved for future use
** 32 4 First freelist page
@@ -46671,9 +51308,10 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
** 60 4 User version
** 64 4 Incremental vacuum mode
-** 68 4 unused
-** 72 4 unused
-** 76 4 unused
+** 68 4 Application-ID
+** 72 20 unused
+** 92 4 The version-valid-for number
+** 96 4 SQLITE_VERSION_NUMBER
**
** All of the integer values are big-endian (most significant byte first).
**
@@ -46729,7 +51367,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
**
** The flags define the format of this btree page. The leaf flag means that
** this page has no children. The zerodata flag means that this page carries
-** only keys and no data. The intkey flag means that the key is a integer
+** only keys and no data. The intkey flag means that the key is an integer
** which is stored in the key size entry of the cell header rather than in
** the payload area.
**
@@ -46866,23 +51504,26 @@ typedef struct BtLock BtLock;
struct MemPage {
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
- u8 intKey; /* True if intkey flag is set */
- u8 leaf; /* True if leaf flag is set */
- u8 hasData; /* True if this page stores data */
+ u8 intKey; /* True if table b-trees. False for index b-trees */
+ u8 intKeyLeaf; /* True if the leaf of an intKey table */
+ u8 noPayload; /* True if internal intKey page (thus w/o data) */
+ u8 leaf; /* True if a leaf page */
u8 hdrOffset; /* 100 for page 1. 0 otherwise */
u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
+ u8 max1bytePayload; /* min(maxLocal,127) */
u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
u16 cellOffset; /* Index in aData of first cell pointer */
u16 nFree; /* Number of free bytes on the page */
u16 nCell; /* Number of cells on this page, local and ovfl */
u16 maskPage; /* Mask for page offset */
- struct _OvflCell { /* Cells that will not fit on aData[] */
- u8 *pCell; /* Pointers to the body of the overflow cell */
- u16 idx; /* Insert this cell before idx-th non-overflow cell */
- } aOvfl[5];
+ u16 aiOvfl[5]; /* Insert the i-th overflow cell before the aiOvfl-th
+ ** non-overflow cell */
+ u8 *apOvfl[5]; /* Pointers to the body of overflow cells */
BtShared *pBt; /* Pointer to BtShared that this page is part of */
u8 *aData; /* Pointer to disk image of the page data */
+ u8 *aDataEnd; /* One byte past the end of usable data */
+ u8 *aCellIdx; /* The cell index area */
DbPage *pDbPage; /* Pager page handle */
Pgno pgno; /* Page number for this page */
};
@@ -46962,7 +51603,7 @@ struct Btree {
/*
** An instance of this object represents a single database file.
**
-** A single database file can be in use as the same time by two
+** A single database file can be in use at the same time by two
** or more database connections. When two or more connections are
** sharing the same database file, each connection has it own
** private Btree object for the file and each of those Btrees points
@@ -46999,17 +51640,15 @@ struct BtShared {
sqlite3 *db; /* Database connection currently using this Btree */
BtCursor *pCursor; /* A list of all open cursors */
MemPage *pPage1; /* First page of the database */
- u8 readOnly; /* True if the underlying file is readonly */
- u8 pageSizeFixed; /* True if the page size can no longer be changed */
- u8 secureDelete; /* True if secure_delete is enabled */
- u8 initiallyEmpty; /* Database is empty at start of transaction */
u8 openFlags; /* Flags to sqlite3BtreeOpen() */
#ifndef SQLITE_OMIT_AUTOVACUUM
u8 autoVacuum; /* True if auto-vacuum is enabled */
u8 incrVacuum; /* True if incr-vacuum is enabled */
+ u8 bDoTruncate; /* True to truncate db on commit */
#endif
u8 inTransaction; /* Transaction state */
- u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
+ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
+ u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
@@ -47027,25 +51666,32 @@ struct BtShared {
BtShared *pNext; /* Next on a list of sharable BtShared structs */
BtLock *pLock; /* List of locks held on this shared-btree struct */
Btree *pWriter; /* Btree with currently open write transaction */
- u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
- u8 isPending; /* If waiting for read-locks to clear */
#endif
- u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
+ u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
};
/*
+** Allowed values for BtShared.btsFlags
+*/
+#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */
+#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */
+#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */
+#define BTS_INITIALLY_EMPTY 0x0008 /* Database was empty at trans start */
+#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */
+#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */
+#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */
+
+/*
** An instance of the following structure is used to hold information
** about a cell. The parseCellPtr() function fills in this structure
** based on information extract from the raw disk page.
*/
typedef struct CellInfo CellInfo;
struct CellInfo {
- i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
- u8 *pCell; /* Pointer to the start of cell content */
- u32 nData; /* Number of bytes of data */
- u32 nPayload; /* Total amount of payload */
- u16 nHeader; /* Size of the cell content header in bytes */
- u16 nLocal; /* Amount of payload held locally */
+ i64 nKey; /* The key for INTKEY tables, or nPayload otherwise */
+ u8 *pPayload; /* Pointer to the start of payload */
+ u32 nPayload; /* Bytes of payload */
+ u16 nLocal; /* Amount of payload held locally, not on overflow */
u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
u16 nSize; /* Size of the cell content on the main b-tree page */
};
@@ -47068,48 +51714,64 @@ struct CellInfo {
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
**
-** A single database file can shared by two more database connections,
+** A single database file can be shared by two more database connections,
** but cursors cannot be shared. Each cursor is associated with a
** particular database connection identified BtCursor.pBtree.db.
**
** Fields in this structure are accessed under the BtShared.mutex
** found at self->pBt->mutex.
+**
+** skipNext meaning:
+** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op.
+** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op.
+** eState==FAULT: Cursor fault with skipNext as error code.
*/
struct BtCursor {
Btree *pBtree; /* The Btree to which this cursor belongs */
BtShared *pBt; /* The BtShared this cursor points to */
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
- Pgno pgnoRoot; /* The root page of this tree */
- sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
+ Pgno *aOverflow; /* Cache of overflow page locations */
CellInfo info; /* A parse of the cell we are pointing at */
- i64 nKey; /* Size of pKey, or last integer key */
- void *pKey; /* Saved key that was cursor's last known position */
- int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
- u8 wrFlag; /* True if writable */
- u8 atLast; /* Cursor pointing to the last entry */
- u8 validNKey; /* True if info.nKey is valid */
+ i64 nKey; /* Size of pKey, or last integer key */
+ void *pKey; /* Saved key that was cursor last known position */
+ Pgno pgnoRoot; /* The root page of this tree */
+ int nOvflAlloc; /* Allocated size of aOverflow[] array */
+ int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
+ ** Error code if eState==CURSOR_FAULT */
+ u8 curFlags; /* zero or more BTCF_* flags defined below */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
-#ifndef SQLITE_OMIT_INCRBLOB
- Pgno *aOverflow; /* Cache of overflow page locations */
- u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
-#endif
+ u8 hints; /* As configured by CursorSetHints() */
i16 iPage; /* Index of current page in apPage */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
};
/*
+** Legal values for BtCursor.curFlags
+*/
+#define BTCF_WriteFlag 0x01 /* True if a write cursor */
+#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
+#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
+#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
+#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
+
+/*
** Potential values for BtCursor.eState.
**
-** CURSOR_VALID:
-** Cursor points to a valid entry. getPayload() etc. may be called.
-**
** CURSOR_INVALID:
** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
+** CURSOR_VALID:
+** Cursor points to a valid entry. getPayload() etc. may be called.
+**
+** CURSOR_SKIPNEXT:
+** Cursor is valid except that the Cursor.skipNext field is non-zero
+** indicating that the next sqlite3BtreeNext() or sqlite3BtreePrevious()
+** operation should be a no-op.
+**
** CURSOR_REQUIRESEEK:
** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
@@ -47118,16 +51780,17 @@ struct BtCursor {
** seek the cursor to the saved position.
**
** CURSOR_FAULT:
-** A unrecoverable error (an I/O error or a malloc failure) has occurred
+** An unrecoverable error (an I/O error or a malloc failure) has occurred
** on a different connection that shares the BtShared cache with this
** cursor. The error has left the cache in an inconsistent state.
** Do nothing else with this cursor. Any attempt to use the cursor
-** should return the error code stored in BtCursor.skip
+** should return the error code stored in BtCursor.skipNext
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
-#define CURSOR_REQUIRESEEK 2
-#define CURSOR_FAULT 3
+#define CURSOR_SKIPNEXT 2
+#define CURSOR_REQUIRESEEK 3
+#define CURSOR_FAULT 4
/*
** The database page the PENDING_BYTE occupies. This page is never used.
@@ -47215,21 +51878,29 @@ struct BtCursor {
/*
** This structure is passed around through all the sanity checking routines
** in order to keep track of some global state information.
+**
+** The aRef[] array is allocated so that there is 1 bit for each page in
+** the database. As the integrity-check proceeds, for each page used in
+** the database the corresponding bit is set. This allows integrity-check to
+** detect pages that are used twice and orphaned pages (both of which
+** indicate corruption).
*/
typedef struct IntegrityCk IntegrityCk;
struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
+ u8 *aPgRef; /* 1 bit per page in the db (see above) */
Pgno nPage; /* Number of pages in the database */
- int *anRef; /* Number of times each page is referenced */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
int mallocFailed; /* A memory allocation error has occurred */
+ const char *zPfx; /* Error message prefix */
+ int v1, v2; /* Values for up to two %d fields in zPfx */
StrAccum errMsg; /* Accumulate the error message text here */
};
/*
-** Read or write a two- and four-byte big-endian integer values.
+** Routines to read or write a two- and four-byte big-endian integer values.
*/
#define get2byte(x) ((x)[0]<<8 | (x)[1])
#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
@@ -47260,7 +51931,7 @@ static void lockBtreeMutex(Btree *p){
** Release the BtShared mutex associated with B-Tree handle p and
** clear the p->locked boolean.
*/
-static void unlockBtreeMutex(Btree *p){
+static void SQLITE_NOINLINE unlockBtreeMutex(Btree *p){
BtShared *pBt = p->pBt;
assert( p->locked==1 );
assert( sqlite3_mutex_held(pBt->mutex) );
@@ -47271,6 +51942,9 @@ static void unlockBtreeMutex(Btree *p){
p->locked = 0;
}
+/* Forward reference */
+static void SQLITE_NOINLINE btreeLockCarefully(Btree *p);
+
/*
** Enter a mutex on the given BTree object.
**
@@ -47288,8 +51962,6 @@ static void unlockBtreeMutex(Btree *p){
** subsequent Btrees that desire a lock.
*/
SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){
- Btree *pLater;
-
/* Some basic sanity checking on the Btree. The list of Btrees
** connected by pNext and pPrev should be in sorted order by
** Btree.pBt value. All elements of the list should belong to
@@ -47314,9 +51986,20 @@ SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){
if( !p->sharable ) return;
p->wantToLock++;
if( p->locked ) return;
+ btreeLockCarefully(p);
+}
+
+/* This is a helper function for sqlite3BtreeLock(). By moving
+** complex, but seldom used logic, out of sqlite3BtreeLock() and
+** into this routine, we avoid unnecessary stack pointer changes
+** and thus help the sqlite3BtreeLock() routine to run much faster
+** in the common case.
+*/
+static void SQLITE_NOINLINE btreeLockCarefully(Btree *p){
+ Btree *pLater;
/* In most cases, we should be able to acquire the lock we
- ** want without having to go throught the ascending lock
+ ** want without having to go through the ascending lock
** procedure that follows. Just be sure not to block.
*/
if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
@@ -47346,6 +52029,7 @@ SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){
}
}
+
/*
** Exit the recursive mutex on a Btree.
*/
@@ -47521,7 +52205,7 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file implements a external (disk-based) database using BTrees.
+** This file implements an external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
@@ -47554,6 +52238,25 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */
*/
#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
+/*
+** Values passed as the 5th argument to allocateBtreePage()
+*/
+#define BTALLOC_ANY 0 /* Allocate any page */
+#define BTALLOC_EXACT 1 /* Allocate exact page if possible */
+#define BTALLOC_LE 2 /* Allocate any page <= the parameter */
+
+/*
+** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not
+** defined, or 0 if it is. For example:
+**
+** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum);
+*/
+#ifndef SQLITE_OMIT_AUTOVACUUM
+#define IfNotOmitAV(expr) (expr)
+#else
+#define IfNotOmitAV(expr) 0
+#endif
+
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** A list of BtShared objects that are eligible for participation
@@ -47654,7 +52357,7 @@ static int hasSharedCacheTableLock(
** the correct locks are held. So do not bother - just return true.
** This case does not come up very often anyhow.
*/
- if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){
+ if( isIndex && (!pSchema || (pSchema->schemaFlags&DB_SchemaLoaded)==0) ){
return 1;
}
@@ -47754,7 +52457,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
/* If some other connection is holding an exclusive lock, the
** requested lock may not be obtained.
*/
- if( pBt->pWriter!=p && pBt->isExclusive ){
+ if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){
sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);
return SQLITE_LOCKED_SHAREDCACHE;
}
@@ -47775,7 +52478,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
sqlite3ConnectionBlocked(p->db, pIter->pBtree->db);
if( eLock==WRITE_LOCK ){
assert( p==pBt->pWriter );
- pBt->isPending = 1;
+ pBt->btsFlags |= BTS_PENDING;
}
return SQLITE_LOCKED_SHAREDCACHE;
}
@@ -47863,7 +52566,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
** the setSharedCacheTableLock() procedure) held by Btree object p.
**
** This function assumes that Btree p has an open read or write
-** transaction. If it does not, then the BtShared.isPending variable
+** transaction. If it does not, then the BTS_PENDING flag
** may be incorrectly cleared.
*/
static void clearAllSharedCacheTableLocks(Btree *p){
@@ -47876,7 +52579,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){
while( *ppIter ){
BtLock *pLock = *ppIter;
- assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );
+ assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
assert( pLock->pBtree->inTrans>=pLock->eLock );
if( pLock->pBtree==p ){
*ppIter = pLock->pNext;
@@ -47889,22 +52592,21 @@ static void clearAllSharedCacheTableLocks(Btree *p){
}
}
- assert( pBt->isPending==0 || pBt->pWriter );
+ assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter );
if( pBt->pWriter==p ){
pBt->pWriter = 0;
- pBt->isExclusive = 0;
- pBt->isPending = 0;
+ pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
}else if( pBt->nTransaction==2 ){
/* This function is called when Btree p is concluding its
** transaction. If there currently exists a writer, and p is not
** that writer, then the number of locks held by connections other
** than the writer must be about to drop to zero. In this case
- ** set the isPending flag to 0.
+ ** set the BTS_PENDING flag to 0.
**
- ** If there is not currently a writer, then BtShared.isPending must
+ ** If there is not currently a writer, then BTS_PENDING must
** be zero already. So this next line is harmless in that case.
*/
- pBt->isPending = 0;
+ pBt->btsFlags &= ~BTS_PENDING;
}
}
@@ -47916,8 +52618,7 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){
if( pBt->pWriter==p ){
BtLock *pLock;
pBt->pWriter = 0;
- pBt->isExclusive = 0;
- pBt->isPending = 0;
+ pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
assert( pLock->eLock==READ_LOCK || pLock->pBtree==p );
pLock->eLock = READ_LOCK;
@@ -47940,16 +52641,11 @@ static int cursorHoldsMutex(BtCursor *p){
}
#endif
-
-#ifndef SQLITE_OMIT_INCRBLOB
/*
-** Invalidate the overflow page-list cache for cursor pCur, if any.
+** Invalidate the overflow cache of the cursor passed as the first argument.
+** on the shared btree structure pBt.
*/
-static void invalidateOverflowCache(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
- sqlite3_free(pCur->aOverflow);
- pCur->aOverflow = 0;
-}
+#define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl)
/*
** Invalidate the overflow page-list cache for all cursors opened
@@ -47963,6 +52659,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){
}
}
+#ifndef SQLITE_OMIT_INCRBLOB
/*
** This function is called before modifying the contents of a table
** to invalidate any incrblob cursors that are open on the
@@ -47985,16 +52682,16 @@ static void invalidateIncrblobCursors(
BtShared *pBt = pBtree->pBt;
assert( sqlite3BtreeHoldsMutex(pBtree) );
for(p=pBt->pCursor; p; p=p->pNext){
- if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){
+ if( (p->curFlags & BTCF_Incrblob)!=0
+ && (isClearTable || p->info.nKey==iRow)
+ ){
p->eState = CURSOR_INVALID;
}
}
}
#else
- /* Stub functions when INCRBLOB is omitted */
- #define invalidateOverflowCache(x)
- #define invalidateAllOverflowCache(x)
+ /* Stub function when INCRBLOB is omitted */
#define invalidateIncrblobCursors(x,y,z)
#endif /* SQLITE_OMIT_INCRBLOB */
@@ -48070,6 +52767,19 @@ static void btreeClearHasContent(BtShared *pBt){
}
/*
+** Release all of the apPage[] pages for a cursor.
+*/
+static void btreeReleaseAllCursorPages(BtCursor *pCur){
+ int i;
+ for(i=0; i<=pCur->iPage; i++){
+ releasePage(pCur->apPage[i]);
+ pCur->apPage[i] = 0;
+ }
+ pCur->iPage = -1;
+}
+
+
+/*
** Save the current cursor position in the variables BtCursor.nKey
** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
**
@@ -48093,7 +52803,7 @@ static int saveCursorPosition(BtCursor *pCur){
** data.
*/
if( 0==pCur->apPage[0]->intKey ){
- void *pKey = sqlite3Malloc( (int)pCur->nKey );
+ void *pKey = sqlite3Malloc( pCur->nKey );
if( pKey ){
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
if( rc==SQLITE_OK ){
@@ -48108,12 +52818,7 @@ static int saveCursorPosition(BtCursor *pCur){
assert( !pCur->apPage[0]->intKey || !pCur->pKey );
if( rc==SQLITE_OK ){
- int i;
- for(i=0; i<=pCur->iPage; i++){
- releasePage(pCur->apPage[i]);
- pCur->apPage[i] = 0;
- }
- pCur->iPage = -1;
+ btreeReleaseAllCursorPages(pCur);
pCur->eState = CURSOR_REQUIRESEEK;
}
@@ -48121,24 +52826,55 @@ static int saveCursorPosition(BtCursor *pCur){
return rc;
}
+/* Forward reference */
+static int SQLITE_NOINLINE saveCursorsOnList(BtCursor*,Pgno,BtCursor*);
+
/*
** Save the positions of all cursors (except pExcept) that are open on
-** the table with root-page iRoot. Usually, this is called just before cursor
-** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+** the table with root-page iRoot. "Saving the cursor position" means that
+** the location in the btree is remembered in such a way that it can be
+** moved back to the same spot after the btree has been modified. This
+** routine is called just before cursor pExcept is used to modify the
+** table, for example in BtreeDelete() or BtreeInsert().
+**
+** Implementation note: This routine merely checks to see if any cursors
+** need to be saved. It calls out to saveCursorsOnList() in the (unusual)
+** event that cursors are in need to being saved.
*/
static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
BtCursor *p;
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pExcept==0 || pExcept->pBt==pBt );
for(p=pBt->pCursor; p; p=p->pNext){
- if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
- p->eState==CURSOR_VALID ){
- int rc = saveCursorPosition(p);
- if( SQLITE_OK!=rc ){
- return rc;
+ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ) break;
+ }
+ return p ? saveCursorsOnList(p, iRoot, pExcept) : SQLITE_OK;
+}
+
+/* This helper routine to saveAllCursors does the actual work of saving
+** the cursors if and when a cursor is found that actually requires saving.
+** The common case is that no cursors need to be saved, so this routine is
+** broken out from its caller to avoid unnecessary stack pointer movement.
+*/
+static int SQLITE_NOINLINE saveCursorsOnList(
+ BtCursor *p, /* The first cursor that needs saving */
+ Pgno iRoot, /* Only save cursor with this iRoot. Save all if zero */
+ BtCursor *pExcept /* Do not save this cursor */
+){
+ do{
+ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){
+ if( p->eState==CURSOR_VALID ){
+ int rc = saveCursorPosition(p);
+ if( SQLITE_OK!=rc ){
+ return rc;
+ }
+ }else{
+ testcase( p->iPage>0 );
+ btreeReleaseAllCursorPages(p);
}
}
- }
+ p = p->pNext;
+ }while( p );
return SQLITE_OK;
}
@@ -48166,19 +52902,26 @@ static int btreeMoveto(
){
int rc; /* Status code */
UnpackedRecord *pIdxKey; /* Unpacked index key */
- char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
+ char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
+ char *pFree = 0;
if( pKey ){
assert( nKey==(i64)(int)nKey );
- pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey,
- aSpace, sizeof(aSpace));
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
+ );
if( pIdxKey==0 ) return SQLITE_NOMEM;
+ sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
+ if( pIdxKey->nField==0 ){
+ sqlite3DbFree(pCur->pKeyInfo->db, pFree);
+ return SQLITE_CORRUPT_BKPT;
+ }
}else{
pIdxKey = 0;
}
rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
- if( pKey ){
- sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
+ if( pFree ){
+ sqlite3DbFree(pCur->pKeyInfo->db, pFree);
}
return rc;
}
@@ -48203,6 +52946,9 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
+ if( pCur->skipNext && pCur->eState==CURSOR_VALID ){
+ pCur->eState = CURSOR_SKIPNEXT;
+ }
}
return rc;
}
@@ -48213,25 +52959,48 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
SQLITE_OK)
/*
-** Determine whether or not a cursor has moved from the position it
-** was last placed at. Cursors can move when the row they are pointing
-** at is deleted out from under them.
+** Determine whether or not a cursor has moved from the position where
+** it was last placed, or has been invalidated for any other reason.
+** Cursors can move when the row they are pointing at is deleted out
+** from under them, for example. Cursor might also move if a btree
+** is rebalanced.
+**
+** Calling this routine with a NULL cursor pointer returns false.
+**
+** Use the separate sqlite3BtreeCursorRestore() routine to restore a cursor
+** back to where it ought to be if this routine returns true.
+*/
+SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur){
+ return pCur->eState!=CURSOR_VALID;
+}
+
+/*
+** This routine restores a cursor back to its original position after it
+** has been moved by some outside activity (such as a btree rebalance or
+** a row having been deleted out from under the cursor).
**
-** This routine returns an error code if something goes wrong. The
-** integer *pHasMoved is set to one if the cursor has moved and 0 if not.
+** On success, the *pDifferentRow parameter is false if the cursor is left
+** pointing at exactly the same row. *pDifferntRow is the row the cursor
+** was pointing to has been deleted, forcing the cursor to point to some
+** nearby row.
+**
+** This routine should only be called for a cursor that just returned
+** TRUE from sqlite3BtreeCursorHasMoved().
*/
-SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
+SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow){
int rc;
+ assert( pCur!=0 );
+ assert( pCur->eState!=CURSOR_VALID );
rc = restoreCursorPosition(pCur);
if( rc ){
- *pHasMoved = 1;
+ *pDifferentRow = 1;
return rc;
}
- if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){
- *pHasMoved = 1;
+ if( pCur->eState!=CURSOR_VALID || NEVER(pCur->skipNext!=0) ){
+ *pDifferentRow = 1;
}else{
- *pHasMoved = 0;
+ *pDifferentRow = 0;
}
return SQLITE_OK;
}
@@ -48367,7 +53136,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
** This routine works only for pages that do not contain overflow cells.
*/
#define findCell(P,I) \
- ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
+ ((P)->aData + ((P)->maskPage & get2byte(&(P)->aCellIdx[2*(I)])))
#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
@@ -48380,12 +53149,10 @@ static u8 *findOverflowCell(MemPage *pPage, int iCell){
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
for(i=pPage->nOverflow-1; i>=0; i--){
int k;
- struct _OvflCell *pOvfl;
- pOvfl = &pPage->aOvfl[i];
- k = pOvfl->idx;
+ k = pPage->aiOvfl[i];
if( k<=iCell ){
if( k==iCell ){
- return pOvfl->pCell;
+ return pPage->apOvfl[i];
}
iCell--;
}
@@ -48398,46 +53165,44 @@ static u8 *findOverflowCell(MemPage *pPage, int iCell){
** are two versions of this function. btreeParseCell() takes a
** cell index as the second argument and btreeParseCellPtr()
** takes a pointer to the body of the cell as its second argument.
-**
-** Within this file, the parseCell() macro can be called instead of
-** btreeParseCellPtr(). Using some compilers, this will be faster.
*/
static void btreeParseCellPtr(
MemPage *pPage, /* Page containing the cell */
u8 *pCell, /* Pointer to the cell text. */
CellInfo *pInfo /* Fill in this structure */
){
- u16 n; /* Number bytes in cell content header */
+ u8 *pIter; /* For scanning through pCell */
u32 nPayload; /* Number of bytes of cell payload */
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
-
- pInfo->pCell = pCell;
assert( pPage->leaf==0 || pPage->leaf==1 );
- n = pPage->childPtrSize;
- assert( n==4-4*pPage->leaf );
- if( pPage->intKey ){
- if( pPage->hasData ){
- n += getVarint32(&pCell[n], nPayload);
- }else{
- nPayload = 0;
- }
- n += getVarint(&pCell[n], (u64*)&pInfo->nKey);
- pInfo->nData = nPayload;
+ if( pPage->intKeyLeaf ){
+ assert( pPage->childPtrSize==0 );
+ pIter = pCell + getVarint32(pCell, nPayload);
+ pIter += getVarint(pIter, (u64*)&pInfo->nKey);
+ }else if( pPage->noPayload ){
+ assert( pPage->childPtrSize==4 );
+ pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey);
+ pInfo->nPayload = 0;
+ pInfo->nLocal = 0;
+ pInfo->iOverflow = 0;
+ pInfo->pPayload = 0;
+ return;
}else{
- pInfo->nData = 0;
- n += getVarint32(&pCell[n], nPayload);
+ pIter = pCell + pPage->childPtrSize;
+ pIter += getVarint32(pIter, nPayload);
pInfo->nKey = nPayload;
}
pInfo->nPayload = nPayload;
- pInfo->nHeader = n;
+ pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
testcase( nPayload==pPage->maxLocal+1 );
- if( likely(nPayload<=pPage->maxLocal) ){
+ if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
- if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4;
+ pInfo->nSize = nPayload + (u16)(pIter - pCell);
+ if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
pInfo->iOverflow = 0;
}else{
@@ -48464,18 +53229,16 @@ static void btreeParseCellPtr(
}else{
pInfo->nLocal = (u16)minLocal;
}
- pInfo->iOverflow = (u16)(pInfo->nLocal + n);
+ pInfo->iOverflow = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell);
pInfo->nSize = pInfo->iOverflow + 4;
}
}
-#define parseCell(pPage, iCell, pInfo) \
- btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo))
static void btreeParseCell(
MemPage *pPage, /* Page containing the cell */
int iCell, /* The cell index. First cell is 0 */
CellInfo *pInfo /* Fill in this structure */
){
- parseCell(pPage, iCell, pInfo);
+ btreeParseCellPtr(pPage, findCell(pPage, iCell), pInfo);
}
/*
@@ -48485,8 +53248,9 @@ static void btreeParseCell(
** the space used by the cell pointer.
*/
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
- u8 *pIter = &pCell[pPage->childPtrSize];
- u32 nSize;
+ u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */
+ u8 *pEnd; /* End mark for a varint */
+ u32 nSize; /* Size value to return */
#ifdef SQLITE_DEBUG
/* The value returned by this function should always be the same as
@@ -48497,26 +53261,34 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
btreeParseCellPtr(pPage, pCell, &debuginfo);
#endif
+ if( pPage->noPayload ){
+ pEnd = &pIter[9];
+ while( (*pIter++)&0x80 && pIter<pEnd );
+ assert( pPage->childPtrSize==4 );
+ return (u16)(pIter - pCell);
+ }
+ nSize = *pIter;
+ if( nSize>=0x80 ){
+ pEnd = &pIter[9];
+ nSize &= 0x7f;
+ do{
+ nSize = (nSize<<7) | (*++pIter & 0x7f);
+ }while( *(pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
if( pPage->intKey ){
- u8 *pEnd;
- if( pPage->hasData ){
- pIter += getVarint32(pIter, nSize);
- }else{
- nSize = 0;
- }
-
/* pIter now points at the 64-bit integer key value, a variable length
** integer. The following block moves pIter to point at the first byte
** past the end of the key value. */
pEnd = &pIter[9];
while( (*pIter++)&0x80 && pIter<pEnd );
- }else{
- pIter += getVarint32(pIter, nSize);
}
-
testcase( nSize==pPage->maxLocal );
testcase( nSize==pPage->maxLocal+1 );
- if( nSize>pPage->maxLocal ){
+ if( nSize<=pPage->maxLocal ){
+ nSize += (u32)(pIter - pCell);
+ if( nSize<4 ) nSize = 4;
+ }else{
int minLocal = pPage->minLocal;
nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
testcase( nSize==pPage->maxLocal );
@@ -48524,16 +53296,9 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
if( nSize>pPage->maxLocal ){
nSize = minLocal;
}
- nSize += 4;
- }
- nSize += (u32)(pIter - pCell);
-
- /* The minimum size of any cell is 4 bytes. */
- if( nSize<4 ){
- nSize = 4;
+ nSize += 4 + (u16)(pIter - pCell);
}
-
- assert( nSize==debuginfo.nSize );
+ assert( nSize==debuginfo.nSize || CORRUPT_DB );
return (u16)nSize;
}
@@ -48556,7 +53321,6 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
if( *pRC ) return;
assert( pCell!=0 );
btreeParseCellPtr(pPage, pCell, &info);
- assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
if( info.iOverflow ){
Pgno ovfl = get4byte(&pCell[info.iOverflow]);
ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC);
@@ -48573,7 +53337,7 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
*/
static int defragmentPage(MemPage *pPage){
int i; /* Loop counter */
- int pc; /* Address of a i-th cell */
+ int pc; /* Address of the i-th cell */
int hdr; /* Offset to the page header */
int size; /* Size of a cell */
int usableSize; /* Number of usable bytes on a page */
@@ -48664,7 +53428,6 @@ static int defragmentPage(MemPage *pPage){
static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */
u8 * const data = pPage->aData; /* Local cache of pPage->aData */
- int nFrag; /* Number of fragmented bytes on pPage */
int top; /* First byte of cell content area */
int gap; /* First byte of gap between cell pointers and cell content */
int rc; /* Integer return code */
@@ -48679,25 +53442,26 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
usableSize = pPage->pBt->usableSize;
assert( nByte < usableSize-8 );
- nFrag = data[hdr+7];
assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
gap = pPage->cellOffset + 2*pPage->nCell;
- top = get2byteNotZero(&data[hdr+5]);
- if( gap>top ) return SQLITE_CORRUPT_BKPT;
+ assert( gap<=65536 );
+ top = get2byte(&data[hdr+5]);
+ if( gap>top ){
+ if( top==0 ){
+ top = 65536;
+ }else{
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
+
+ /* If there is enough space between gap and top for one more cell pointer
+ ** array entry offset, and if the freelist is not empty, then search the
+ ** freelist looking for a free slot big enough to satisfy the request.
+ */
testcase( gap+2==top );
testcase( gap+1==top );
testcase( gap==top );
-
- if( nFrag>=60 ){
- /* Always defragment highly fragmented pages */
- rc = defragmentPage(pPage);
- if( rc ) return rc;
- top = get2byteNotZero(&data[hdr+5]);
- }else if( gap+2<=top ){
- /* Search the freelist looking for a free slot big enough to satisfy
- ** the request. The allocation is made from the first free slot in
- ** the list that is large enough to accomadate it.
- */
+ if( gap+2<=top && (data[hdr+1] || data[hdr+2]) ){
int pc, addr;
for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
int size; /* Size of the free slot */
@@ -48710,10 +53474,11 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
testcase( x==4 );
testcase( x==3 );
if( x<4 ){
+ if( data[hdr+7]>=60 ) goto defragment_page;
/* Remove the slot from the free-list. Update the number of
** fragmented bytes within the page. */
memcpy(&data[addr], &data[pc], 2);
- data[hdr+7] = (u8)(nFrag + x);
+ data[hdr+7] += (u8)x;
}else if( size+pc > usableSize ){
return SQLITE_CORRUPT_BKPT;
}else{
@@ -48727,11 +53492,13 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
}
}
- /* Check to make sure there is enough space in the gap to satisfy
- ** the allocation. If not, defragment.
+ /* The request could not be fulfilled using a freelist slot. Check
+ ** to see if defragmentation is necessary.
*/
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
+defragment_page:
+ testcase( pPage->nCell==0 );
rc = defragmentPage(pPage);
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
@@ -48754,90 +53521,100 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
/*
** Return a section of the pPage->aData to the freelist.
-** The first byte of the new free block is pPage->aDisk[start]
-** and the size of the block is "size" bytes.
-**
-** Most of the effort here is involved in coalesing adjacent
-** free blocks into a single big free block.
-*/
-static int freeSpace(MemPage *pPage, int start, int size){
- int addr, pbegin, hdr;
- int iLast; /* Largest possible freeblock offset */
- unsigned char *data = pPage->aData;
+** The first byte of the new free block is pPage->aData[iStart]
+** and the size of the block is iSize bytes.
+**
+** Adjacent freeblocks are coalesced.
+**
+** Note that even though the freeblock list was checked by btreeInitPage(),
+** that routine will not detect overlap between cells or freeblocks. Nor
+** does it detect cells or freeblocks that encrouch into the reserved bytes
+** at the end of the page. So do additional corruption checks inside this
+** routine and return SQLITE_CORRUPT if any problems are found.
+*/
+static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
+ u16 iPtr; /* Address of ptr to next freeblock */
+ u16 iFreeBlk; /* Address of the next freeblock */
+ u8 hdr; /* Page header size. 0 or 100 */
+ u8 nFrag = 0; /* Reduction in fragmentation */
+ u16 iOrigSize = iSize; /* Original value of iSize */
+ u32 iLast = pPage->pBt->usableSize-4; /* Largest possible freeblock offset */
+ u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */
+ unsigned char *data = pPage->aData; /* Page content */
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
- assert( start>=pPage->hdrOffset+6+pPage->childPtrSize );
- assert( (start + size) <= (int)pPage->pBt->usableSize );
+ assert( iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
+ assert( iEnd <= pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- assert( size>=0 ); /* Minimum cell size is 4 */
+ assert( iSize>=4 ); /* Minimum cell size is 4 */
+ assert( iStart<=iLast );
- if( pPage->pBt->secureDelete ){
- /* Overwrite deleted information with zeros when the secure_delete
- ** option is enabled */
- memset(&data[start], 0, size);
+ /* Overwrite deleted information with zeros when the secure_delete
+ ** option is enabled */
+ if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){
+ memset(&data[iStart], 0, iSize);
}
- /* Add the space back into the linked list of freeblocks. Note that
- ** even though the freeblock list was checked by btreeInitPage(),
- ** btreeInitPage() did not detect overlapping cells or
- ** freeblocks that overlapped cells. Nor does it detect when the
- ** cell content area exceeds the value in the page header. If these
- ** situations arise, then subsequent insert operations might corrupt
- ** the freelist. So we do need to check for corruption while scanning
- ** the freelist.
+ /* The list of freeblocks must be in ascending order. Find the
+ ** spot on the list where iStart should be inserted.
*/
hdr = pPage->hdrOffset;
- addr = hdr + 1;
- iLast = pPage->pBt->usableSize - 4;
- assert( start<=iLast );
- while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){
- if( pbegin<addr+4 ){
- return SQLITE_CORRUPT_BKPT;
+ iPtr = hdr + 1;
+ if( data[iPtr+1]==0 && data[iPtr]==0 ){
+ iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
+ }else{
+ while( (iFreeBlk = get2byte(&data[iPtr]))>0 && iFreeBlk<iStart ){
+ if( iFreeBlk<iPtr+4 ) return SQLITE_CORRUPT_BKPT;
+ iPtr = iFreeBlk;
}
- addr = pbegin;
- }
- if( pbegin>iLast ){
- return SQLITE_CORRUPT_BKPT;
- }
- assert( pbegin>addr || pbegin==0 );
- put2byte(&data[addr], start);
- put2byte(&data[start], pbegin);
- put2byte(&data[start+2], size);
- pPage->nFree = pPage->nFree + (u16)size;
-
- /* Coalesce adjacent free blocks */
- addr = hdr + 1;
- while( (pbegin = get2byte(&data[addr]))>0 ){
- int pnext, psize, x;
- assert( pbegin>addr );
- assert( pbegin <= (int)pPage->pBt->usableSize-4 );
- pnext = get2byte(&data[pbegin]);
- psize = get2byte(&data[pbegin+2]);
- if( pbegin + psize + 3 >= pnext && pnext>0 ){
- int frag = pnext - (pbegin+psize);
- if( (frag<0) || (frag>(int)data[hdr+7]) ){
- return SQLITE_CORRUPT_BKPT;
- }
- data[hdr+7] -= (u8)frag;
- x = get2byte(&data[pnext]);
- put2byte(&data[pbegin], x);
- x = pnext + get2byte(&data[pnext+2]) - pbegin;
- put2byte(&data[pbegin+2], x);
- }else{
- addr = pbegin;
+ if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT;
+ assert( iFreeBlk>iPtr || iFreeBlk==0 );
+
+ /* At this point:
+ ** iFreeBlk: First freeblock after iStart, or zero if none
+ ** iPtr: The address of a pointer iFreeBlk
+ **
+ ** Check to see if iFreeBlk should be coalesced onto the end of iStart.
+ */
+ if( iFreeBlk && iEnd+3>=iFreeBlk ){
+ nFrag = iFreeBlk - iEnd;
+ if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_BKPT;
+ iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
+ iSize = iEnd - iStart;
+ iFreeBlk = get2byte(&data[iFreeBlk]);
}
+
+ /* If iPtr is another freeblock (that is, if iPtr is not the freelist
+ ** pointer in the page header) then check to see if iStart should be
+ ** coalesced onto the end of iPtr.
+ */
+ if( iPtr>hdr+1 ){
+ int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
+ if( iPtrEnd+3>=iStart ){
+ if( iPtrEnd>iStart ) return SQLITE_CORRUPT_BKPT;
+ nFrag += iStart - iPtrEnd;
+ iSize = iEnd - iPtr;
+ iStart = iPtr;
+ }
+ }
+ if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_BKPT;
+ data[hdr+7] -= nFrag;
+ }
+ if( iStart==get2byte(&data[hdr+5]) ){
+ /* The new freeblock is at the beginning of the cell content area,
+ ** so just extend the cell content area rather than create another
+ ** freelist entry */
+ if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_BKPT;
+ put2byte(&data[hdr+1], iFreeBlk);
+ put2byte(&data[hdr+5], iEnd);
+ }else{
+ /* Insert the new freeblock into the freelist */
+ put2byte(&data[iPtr], iStart);
+ put2byte(&data[iStart], iFreeBlk);
+ put2byte(&data[iStart+2], iSize);
}
-
- /* If the cell content area begins with a freeblock, remove it. */
- if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){
- int top;
- pbegin = get2byte(&data[hdr+1]);
- memcpy(&data[hdr+1], &data[pbegin], 2);
- top = get2byte(&data[hdr+5]) + get2byte(&data[pbegin+2]);
- put2byte(&data[hdr+5], top);
- }
- assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ pPage->nFree += iOrigSize;
return SQLITE_OK;
}
@@ -48864,17 +53641,20 @@ static int decodeFlags(MemPage *pPage, int flagByte){
pBt = pPage->pBt;
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
pPage->intKey = 1;
- pPage->hasData = pPage->leaf;
+ pPage->intKeyLeaf = pPage->leaf;
+ pPage->noPayload = !pPage->leaf;
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==PTF_ZERODATA ){
pPage->intKey = 0;
- pPage->hasData = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->noPayload = 0;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
return SQLITE_CORRUPT_BKPT;
}
+ pPage->max1bytePayload = pBt->max1bytePayload;
return SQLITE_OK;
}
@@ -48917,6 +53697,8 @@ static int btreeInitPage(MemPage *pPage){
pPage->nOverflow = 0;
usableSize = pBt->usableSize;
pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
+ pPage->aDataEnd = &data[usableSize];
+ pPage->aCellIdx = &data[cellOffset];
top = get2byteNotZero(&data[hdr+5]);
pPage->nCell = get2byte(&data[hdr+3]);
if( pPage->nCell>MX_CELL(pBt) ){
@@ -48970,7 +53752,7 @@ static int btreeInitPage(MemPage *pPage){
size = get2byte(&data[pc+2]);
if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){
/* Free blocks must be in ascending order. And the last byte of
- ** the free-block must lie on the database page. */
+ ** the free-block must lie on the database page. */
return SQLITE_CORRUPT_BKPT;
}
nFree = nFree + size;
@@ -49008,18 +53790,19 @@ static void zeroPage(MemPage *pPage, int flags){
assert( sqlite3PagerGetData(pPage->pDbPage) == data );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pBt->mutex) );
- if( pBt->secureDelete ){
+ if( pBt->btsFlags & BTS_SECURE_DELETE ){
memset(&data[hdr], 0, pBt->usableSize - hdr);
}
data[hdr] = (char)flags;
- first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
+ first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
memset(&data[hdr+1], 0, 4);
data[hdr+7] = 0;
put2byte(&data[hdr+5], pBt->usableSize);
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
- pPage->hdrOffset = hdr;
pPage->cellOffset = first;
+ pPage->aDataEnd = &data[pBt->usableSize];
+ pPage->aCellIdx = &data[first];
pPage->nOverflow = 0;
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
@@ -49057,13 +53840,14 @@ static int btreeGetPage(
BtShared *pBt, /* The btree */
Pgno pgno, /* Number of the page to fetch */
MemPage **ppPage, /* Return the page in this parameter */
- int noContent /* Do not load page content if true */
+ int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
){
int rc;
DbPage *pDbPage;
+ assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY );
assert( sqlite3_mutex_held(pBt->mutex) );
- rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);
+ rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, flags);
if( rc ) return rc;
*ppPage = btreePageFromDbPage(pDbPage, pgno, pBt);
return SQLITE_OK;
@@ -49094,7 +53878,7 @@ static Pgno btreePagecount(BtShared *pBt){
SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){
assert( sqlite3BtreeHoldsMutex(p) );
assert( ((p->pBt->nPage)&0x8000000)==0 );
- return (int)btreePagecount(p->pBt);
+ return btreePagecount(p->pBt);
}
/*
@@ -49106,18 +53890,20 @@ SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){
** may remain unchanged, or it may be set to an invalid value.
*/
static int getAndInitPage(
- BtShared *pBt, /* The database file */
- Pgno pgno, /* Number of the page to get */
- MemPage **ppPage /* Write the page pointer here */
+ BtShared *pBt, /* The database file */
+ Pgno pgno, /* Number of the page to get */
+ MemPage **ppPage, /* Write the page pointer here */
+ int bReadonly /* PAGER_GET_READONLY or 0 */
){
int rc;
assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( bReadonly==PAGER_GET_READONLY || bReadonly==0 );
if( pgno>btreePagecount(pBt) ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, pgno, ppPage, 0);
- if( rc==SQLITE_OK ){
+ rc = btreeGetPage(pBt, pgno, ppPage, bReadonly);
+ if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
rc = btreeInitPage(*ppPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
@@ -49138,10 +53924,11 @@ static void releasePage(MemPage *pPage){
if( pPage ){
assert( pPage->aData );
assert( pPage->pBt );
+ assert( pPage->pDbPage!=0 );
assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- sqlite3PagerUnref(pPage->pDbPage);
+ sqlite3PagerUnrefNotNull(pPage->pDbPage);
}
}
@@ -49194,11 +53981,8 @@ static int btreeInvokeBusyHandler(void *pArg){
** If zFilename is ":memory:" then an in-memory database is created
** that is automatically destroyed when it is closed.
**
-** The "flags" parameter is a bitmask that might contain bits
-** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK
-** bit is also set if the SQLITE_NoReadlock flags is set in db->flags.
-** These flags are passed through into sqlite3PagerOpen() and must
-** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK.
+** The "flags" parameter is a bitmask that might contain bits like
+** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY.
**
** If the database is already opened in the same database connection
** and we are in shared cache mode, then the open will fail with an
@@ -49231,7 +54015,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
const int isMemdb = 0;
#else
const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
- || (isTempDb && sqlite3TempInMemory(db));
+ || (isTempDb && sqlite3TempInMemory(db))
+ || (vfsFlags & SQLITE_OPEN_MEMORY)!=0;
#endif
assert( db!=0 );
@@ -49245,9 +54030,6 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */
assert( (flags & BTREE_SINGLE)==0 || isTempDb );
- if( db->flags & SQLITE_NoReadlock ){
- flags |= BTREE_NO_READLOCK;
- }
if( isMemdb ){
flags |= BTREE_MEMORY;
}
@@ -49270,24 +54052,36 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
** If this Btree is a candidate for shared cache, try to find an
** existing BtShared object that we can share with
*/
- if( isMemdb==0 && isTempDb==0 ){
+ if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
int nFullPathname = pVfs->mxPathname+1;
char *zFullPathname = sqlite3Malloc(nFullPathname);
- sqlite3_mutex *mutexShared;
+ MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
p->sharable = 1;
if( !zFullPathname ){
sqlite3_free(p);
return SQLITE_NOMEM;
}
- sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
+ if( isMemdb ){
+ memcpy(zFullPathname, zFilename, sqlite3Strlen30(zFilename)+1);
+ }else{
+ rc = sqlite3OsFullPathname(pVfs, zFilename,
+ nFullPathname, zFullPathname);
+ if( rc ){
+ sqlite3_free(zFullPathname);
+ sqlite3_free(p);
+ return rc;
+ }
+ }
+#if SQLITE_THREADSAFE
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
sqlite3_mutex_enter(mutexOpen);
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(mutexShared);
+#endif
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
assert( pBt->nRef>0 );
- if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
+ if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager, 0))
&& sqlite3PagerVfs(pBt->pPager)==pVfs ){
int iDb;
for(iDb=db->nDb-1; iDb>=0; iDb--){
@@ -49340,6 +54134,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
EXTRA_SIZE, flags, vfsFlags, pageReinit);
if( rc==SQLITE_OK ){
+ sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap);
rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
}
if( rc!=SQLITE_OK ){
@@ -49352,9 +54147,9 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
pBt->pCursor = 0;
pBt->pPage1 = 0;
- pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
+ if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY;
#ifdef SQLITE_SECURE_DELETE
- pBt->secureDelete = 1;
+ pBt->btsFlags |= BTS_SECURE_DELETE;
#endif
pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
@@ -49375,7 +54170,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
nReserve = 0;
}else{
nReserve = zDbHeader[20];
- pBt->pageSizeFixed = 1;
+ pBt->btsFlags |= BTS_PAGESIZE_FIXED;
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0);
pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0);
@@ -49390,9 +54185,9 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
/* Add the new BtShared object to the linked list sharable BtShareds.
*/
if( p->sharable ){
- sqlite3_mutex *mutexShared;
+ MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
pBt->nRef = 1;
- mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
if( pBt->mutex==0 ){
@@ -49474,12 +54269,12 @@ btree_open_out:
*/
static int removeFromSharingList(BtShared *pBt){
#ifndef SQLITE_OMIT_SHARED_CACHE
- sqlite3_mutex *pMaster;
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; )
BtShared *pList;
int removed = 0;
assert( sqlite3_mutex_notheld(pBt->mutex) );
- pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
sqlite3_mutex_enter(pMaster);
pBt->nRef--;
if( pBt->nRef<=0 ){
@@ -49508,11 +54303,32 @@ static int removeFromSharingList(BtShared *pBt){
/*
** Make sure pBt->pTmpSpace points to an allocation of
-** MX_CELL_SIZE(pBt) bytes.
+** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child
+** pointer.
*/
static void allocateTempSpace(BtShared *pBt){
if( !pBt->pTmpSpace ){
pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+
+ /* One of the uses of pBt->pTmpSpace is to format cells before
+ ** inserting them into a leaf page (function fillInCell()). If
+ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
+ ** by the various routines that manipulate binary cells. Which
+ ** can mean that fillInCell() only initializes the first 2 or 3
+ ** bytes of pTmpSpace, but that the first 4 bytes are copied from
+ ** it into a database page. This is not actually a problem, but it
+ ** does cause a valgrind error when the 1 or 2 bytes of unitialized
+ ** data is passed to system call write(). So to avoid this error,
+ ** zero the first 4 bytes of temp space here.
+ **
+ ** Also: Provide four bytes of initialized space before the
+ ** beginning of pTmpSpace as an area available to prepend the
+ ** left-child pointer to the beginning of a cell.
+ */
+ if( pBt->pTmpSpace ){
+ memset(pBt->pTmpSpace, 0, 8);
+ pBt->pTmpSpace += 4;
+ }
}
}
@@ -49520,8 +54336,11 @@ static void allocateTempSpace(BtShared *pBt){
** Free the pBt->pTmpSpace allocation
*/
static void freeTempSpace(BtShared *pBt){
- sqlite3PageFree( pBt->pTmpSpace);
- pBt->pTmpSpace = 0;
+ if( pBt->pTmpSpace ){
+ pBt->pTmpSpace -= 4;
+ sqlite3PageFree(pBt->pTmpSpace);
+ pBt->pTmpSpace = 0;
+ }
}
/*
@@ -49547,7 +54366,7 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
** The call to sqlite3BtreeRollback() drops any table-locks held by
** this handle.
*/
- sqlite3BtreeRollback(p);
+ sqlite3BtreeRollback(p, SQLITE_OK, 0);
sqlite3BtreeLeave(p);
/* If there are still other outstanding references to the shared-btree
@@ -49606,6 +54425,21 @@ SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
return SQLITE_OK;
}
+#if SQLITE_MAX_MMAP_SIZE>0
+/*
+** Change the limit on the amount of the database file that may be
+** memory mapped.
+*/
+SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree *p, sqlite3_int64 szMmap){
+ BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
+ sqlite3PagerSetMmapLimit(pBt->pPager, szMmap);
+ sqlite3BtreeLeave(p);
+ return SQLITE_OK;
+}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
+
/*
** Change the way data is synced to disk in order to increase or decrease
** how well the database resists damage due to OS crashes and power
@@ -49615,17 +54449,14 @@ SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
** probability of damage to near zero but with a write performance reduction.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(
+SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(
Btree *p, /* The btree to set the safety level on */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int fullSync, /* PRAGMA fullfsync. */
- int ckptFullSync /* PRAGMA checkpoint_fullfync */
+ unsigned pgFlags /* Various PAGER_* flags */
){
BtShared *pBt = p->pBt;
assert( sqlite3_mutex_held(p->db->mutex) );
- assert( level>=1 && level<=3 );
sqlite3BtreeEnter(p);
- sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
+ sqlite3PagerSetFlags(pBt->pPager, pgFlags);
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -49663,7 +54494,7 @@ SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree *p){
** If parameter nReserve is less than zero, then the number of reserved
** bytes per page is left unchanged.
**
-** If the iFix!=0 then the pageSizeFixed flag is set so that the page size
+** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
** and autovacuum mode can no longer be changed.
*/
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
@@ -49671,7 +54502,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve,
BtShared *pBt = p->pBt;
assert( nReserve>=-1 && nReserve<=255 );
sqlite3BtreeEnter(p);
- if( pBt->pageSizeFixed ){
+ if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
sqlite3BtreeLeave(p);
return SQLITE_READONLY;
}
@@ -49688,7 +54519,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve,
}
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
pBt->usableSize = pBt->pageSize - (u16)nReserve;
- if( iFix ) pBt->pageSizeFixed = 1;
+ if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED;
sqlite3BtreeLeave(p);
return rc;
}
@@ -49700,6 +54531,24 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG)
+/*
+** This function is similar to sqlite3BtreeGetReserve(), except that it
+** may only be called if it is guaranteed that the b-tree mutex is already
+** held.
+**
+** This is useful in one special case in the backup API code where it is
+** known that the shared b-tree mutex is held, but the mutex on the
+** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
+** were to be called, it might collide with some other operation on the
+** database handle that owns *p, causing undefined behavior.
+*/
+SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ return p->pBt->pageSize - p->pBt->usableSize;
+}
+#endif /* SQLITE_HAS_CODEC || SQLITE_DEBUG */
+
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
/*
** Return the number of bytes of space at the end of every page that
@@ -49728,8 +54577,8 @@ SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
}
/*
-** Set the secureDelete flag if newFlag is 0 or 1. If newFlag is -1,
-** then make no changes. Always return the value of the secureDelete
+** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1. If newFlag is -1,
+** then make no changes. Always return the value of the BTS_SECURE_DELETE
** setting after the change.
*/
SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
@@ -49737,9 +54586,10 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
if( p==0 ) return 0;
sqlite3BtreeEnter(p);
if( newFlag>=0 ){
- p->pBt->secureDelete = (newFlag!=0) ? 1 : 0;
+ p->pBt->btsFlags &= ~BTS_SECURE_DELETE;
+ if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE;
}
- b = p->pBt->secureDelete;
+ b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0;
sqlite3BtreeLeave(p);
return b;
}
@@ -49760,7 +54610,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
u8 av = (u8)autoVacuum;
sqlite3BtreeEnter(p);
- if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){
+ if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){
rc = SQLITE_READONLY;
}else{
pBt->autoVacuum = av ?1:0;
@@ -49834,14 +54684,14 @@ static int lockBtree(BtShared *pBt){
#ifdef SQLITE_OMIT_WAL
if( page1[18]>1 ){
- pBt->readOnly = 1;
+ pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>1 ){
goto page1_init_failed;
}
#else
if( page1[18]>2 ){
- pBt->readOnly = 1;
+ pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>2 ){
goto page1_init_failed;
@@ -49855,7 +54705,7 @@ static int lockBtree(BtShared *pBt){
** may not be the latest version - there may be a newer one in the log
** file.
*/
- if( page1[19]==2 && pBt->doNotUseWAL==0 ){
+ if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
int isOpen = 0;
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
if( rc!=SQLITE_OK ){
@@ -49932,6 +54782,11 @@ static int lockBtree(BtShared *pBt){
pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
pBt->maxLeaf = (u16)(pBt->usableSize - 35);
pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
+ if( pBt->maxLocal>127 ){
+ pBt->max1bytePayload = 127;
+ }else{
+ pBt->max1bytePayload = (u8)pBt->maxLocal;
+ }
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
pBt->pPage1 = pPage1;
pBt->nPage = nPage;
@@ -49943,6 +54798,30 @@ page1_init_failed:
return rc;
}
+#ifndef NDEBUG
+/*
+** Return the number of cursors open on pBt. This is for use
+** in assert() expressions, so it is only compiled if NDEBUG is not
+** defined.
+**
+** Only write cursors are counted if wrOnly is true. If wrOnly is
+** false then all cursors are counted.
+**
+** For the purposes of this routine, a cursor is any cursor that
+** is capable of reading or writing to the database. Cursors that
+** have been tripped into the CURSOR_FAULT state are not counted.
+*/
+static int countValidCursors(BtShared *pBt, int wrOnly){
+ BtCursor *pCur;
+ int r = 0;
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0)
+ && pCur->eState!=CURSOR_FAULT ) r++;
+ }
+ return r;
+}
+#endif
+
/*
** If there are no outstanding cursors and we are not in the middle
** of a transaction but there is a read lock on the database, then
@@ -49953,13 +54832,13 @@ page1_init_failed:
*/
static void unlockBtreeIfUnused(BtShared *pBt){
assert( sqlite3_mutex_held(pBt->mutex) );
- assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE );
+ assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE );
if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){
- assert( pBt->pPage1->aData );
+ MemPage *pPage1 = pBt->pPage1;
+ assert( pPage1->aData );
assert( sqlite3PagerRefcount(pBt->pPager)==1 );
- assert( pBt->pPage1->aData );
- releasePage(pBt->pPage1);
pBt->pPage1 = 0;
+ releasePage(pPage1);
}
}
@@ -49995,7 +54874,7 @@ static int newDatabase(BtShared *pBt){
data[23] = 32;
memset(&data[24], 0, 100-24);
zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
- pBt->pageSizeFixed = 1;
+ pBt->btsFlags |= BTS_PAGESIZE_FIXED;
#ifndef SQLITE_OMIT_AUTOVACUUM
assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 );
assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 );
@@ -50008,6 +54887,20 @@ static int newDatabase(BtShared *pBt){
}
/*
+** Initialize the first page of the database file (creating a database
+** consisting of a single page and no schema objects). Return SQLITE_OK
+** if successful, or an SQLite error code otherwise.
+*/
+SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){
+ int rc;
+ sqlite3BtreeEnter(p);
+ p->pBt->nPage = 0;
+ rc = newDatabase(p->pBt);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
+
+/*
** Attempt to start a new transaction. A write-transaction
** is started if the second argument is nonzero, otherwise a read-
** transaction. If the second argument is 2 or more and exclusive
@@ -50057,9 +54950,10 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
goto trans_begun;
}
+ assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
/* Write transactions are not possible on a read-only database */
- if( pBt->readOnly && wrflag ){
+ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
rc = SQLITE_READONLY;
goto trans_begun;
}
@@ -50069,7 +54963,9 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
** on this shared-btree structure and a second write transaction is
** requested, return SQLITE_LOCKED.
*/
- if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){
+ if( (wrflag && pBt->inTransaction==TRANS_WRITE)
+ || (pBt->btsFlags & BTS_PENDING)!=0
+ ){
pBlock = pBt->pWriter->db;
}else if( wrflag>1 ){
BtLock *pIter;
@@ -50093,7 +54989,8 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
if( SQLITE_OK!=rc ) goto trans_begun;
- pBt->initiallyEmpty = (u8)(pBt->nPage==0);
+ pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
+ if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
do {
/* Call lockBtree() until either pBt->pPage1 is populated or
** lockBtree() returns something other than SQLITE_OK. lockBtree()
@@ -50105,7 +55002,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );
if( rc==SQLITE_OK && wrflag ){
- if( pBt->readOnly ){
+ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
rc = SQLITE_READONLY;
}else{
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
@@ -50126,7 +55023,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
pBt->nTransaction++;
#ifndef SQLITE_OMIT_SHARED_CACHE
if( p->sharable ){
- assert( p->lock.pBtree==p && p->lock.iTable==1 );
+ assert( p->lock.pBtree==p && p->lock.iTable==1 );
p->lock.eLock = READ_LOCK;
p->lock.pNext = pBt->pLock;
pBt->pLock = &p->lock;
@@ -50142,7 +55039,8 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
#ifndef SQLITE_OMIT_SHARED_CACHE
assert( !pBt->pWriter );
pBt->pWriter = p;
- pBt->isExclusive = (u8)(wrflag>1);
+ pBt->btsFlags &= ~BTS_EXCLUSIVE;
+ if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE;
#endif
/* If the db-size header field is incorrect (as it may be if an old
@@ -50254,11 +55152,12 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
if( eType==PTRMAP_OVERFLOW1 ){
CellInfo info;
btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow ){
- if( iFrom==get4byte(&pCell[info.iOverflow]) ){
- put4byte(&pCell[info.iOverflow], iTo);
- break;
- }
+ if( info.iOverflow
+ && pCell+info.iOverflow+3<=pPage->aData+pPage->maskPage
+ && iFrom==get4byte(&pCell[info.iOverflow])
+ ){
+ put4byte(&pCell[info.iOverflow], iTo);
+ break;
}
}else{
if( get4byte(pCell)==iFrom ){
@@ -50368,24 +55267,23 @@ static int relocatePage(
static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
/*
-** Perform a single step of an incremental-vacuum. If successful,
-** return SQLITE_OK. If there is no work to do (and therefore no
-** point in calling this function again), return SQLITE_DONE.
+** Perform a single step of an incremental-vacuum. If successful, return
+** SQLITE_OK. If there is no work to do (and therefore no point in
+** calling this function again), return SQLITE_DONE. Or, if an error
+** occurs, return some other error code.
**
-** More specificly, this function attempts to re-organize the
-** database so that the last page of the file currently in use
-** is no longer in use.
+** More specifically, this function attempts to re-organize the database so
+** that the last page of the file currently in use is no longer in use.
**
-** If the nFin parameter is non-zero, this function assumes
-** that the caller will keep calling incrVacuumStep() until
-** it returns SQLITE_DONE or an error, and that nFin is the
-** number of pages the database file will contain after this
-** process is complete. If nFin is zero, it is assumed that
-** incrVacuumStep() will be called a finite amount of times
-** which may or may not empty the freelist. A full autovacuum
-** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0.
+** Parameter nFin is the number of pages that this database would contain
+** were this function called until it returns SQLITE_DONE.
+**
+** If the bCommit parameter is non-zero, this function assumes that the
+** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE
+** or an error. bCommit is passed true for an auto-vacuum-on-commit
+** operation, or false for an incremental vacuum.
*/
-static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
+static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
Pgno nFreeList; /* Number of pages still on the free-list */
int rc;
@@ -50410,15 +55308,15 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
}
if( eType==PTRMAP_FREEPAGE ){
- if( nFin==0 ){
+ if( bCommit==0 ){
/* Remove the page from the files free-list. This is not required
- ** if nFin is non-zero. In that case, the free-list will be
+ ** if bCommit is non-zero. In that case, the free-list will be
** truncated to zero after this function returns, so it doesn't
** matter if it still contains some garbage entries.
*/
Pgno iFreePg;
MemPage *pFreePg;
- rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1);
+ rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -50428,34 +55326,37 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
} else {
Pgno iFreePg; /* Index of free page to move pLastPg to */
MemPage *pLastPg;
+ u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */
+ Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */
rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
if( rc!=SQLITE_OK ){
return rc;
}
- /* If nFin is zero, this loop runs exactly once and page pLastPg
+ /* If bCommit is zero, this loop runs exactly once and page pLastPg
** is swapped with the first free page pulled off the free list.
**
- ** On the other hand, if nFin is greater than zero, then keep
+ ** On the other hand, if bCommit is greater than zero, then keep
** looping until a free-page located within the first nFin pages
** of the file is found.
*/
+ if( bCommit==0 ){
+ eMode = BTALLOC_LE;
+ iNear = nFin;
+ }
do {
MemPage *pFreePg;
- rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0);
+ rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
if( rc!=SQLITE_OK ){
releasePage(pLastPg);
return rc;
}
releasePage(pFreePg);
- }while( nFin!=0 && iFreePg>nFin );
+ }while( bCommit && iFreePg>nFin );
assert( iFreePg<iLastPg );
- rc = sqlite3PagerWrite(pLastPg->pDbPage);
- if( rc==SQLITE_OK ){
- rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
- }
+ rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, bCommit);
releasePage(pLastPg);
if( rc!=SQLITE_OK ){
return rc;
@@ -50463,30 +55364,40 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
}
}
- if( nFin==0 ){
- iLastPg--;
- while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){
- if( PTRMAP_ISPAGE(pBt, iLastPg) ){
- MemPage *pPg;
- rc = btreeGetPage(pBt, iLastPg, &pPg, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- rc = sqlite3PagerWrite(pPg->pDbPage);
- releasePage(pPg);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }
+ if( bCommit==0 ){
+ do {
iLastPg--;
- }
- sqlite3PagerTruncateImage(pBt->pPager, iLastPg);
+ }while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) );
+ pBt->bDoTruncate = 1;
pBt->nPage = iLastPg;
}
return SQLITE_OK;
}
/*
+** The database opened by the first argument is an auto-vacuum database
+** nOrig pages in size containing nFree free pages. Return the expected
+** size of the database in pages following an auto-vacuum operation.
+*/
+static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){
+ int nEntry; /* Number of entries on one ptrmap page */
+ Pgno nPtrmap; /* Number of PtrMap pages to be freed */
+ Pgno nFin; /* Return value */
+
+ nEntry = pBt->usableSize/5;
+ nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
+ nFin = nOrig - nFree - nPtrmap;
+ if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
+ nFin--;
+ }
+ while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
+ nFin--;
+ }
+
+ return nFin;
+}
+
+/*
** A write-transaction must be opened before calling this function.
** It performs a single unit of work towards an incremental vacuum.
**
@@ -50503,11 +55414,24 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
if( !pBt->autoVacuum ){
rc = SQLITE_DONE;
}else{
- invalidateAllOverflowCache(pBt);
- rc = incrVacuumStep(pBt, 0, btreePagecount(pBt));
- if( rc==SQLITE_OK ){
- rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
- put4byte(&pBt->pPage1->aData[28], pBt->nPage);
+ Pgno nOrig = btreePagecount(pBt);
+ Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
+ Pgno nFin = finalDbSize(pBt, nOrig, nFree);
+
+ if( nOrig<nFin ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else if( nFree>0 ){
+ rc = saveAllCursors(pBt, 0, 0);
+ if( rc==SQLITE_OK ){
+ invalidateAllOverflowCache(pBt);
+ rc = incrVacuumStep(pBt, nFin, nOrig, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
+ put4byte(&pBt->pPage1->aData[28], pBt->nPage);
+ }
+ }else{
+ rc = SQLITE_DONE;
}
}
sqlite3BtreeLeave(p);
@@ -50516,7 +55440,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
/*
** This routine is called prior to sqlite3PagerCommit when a transaction
-** is commited for an auto-vacuum database.
+** is committed for an auto-vacuum database.
**
** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages
** the database file should be truncated to during the commit process.
@@ -50534,9 +55458,7 @@ static int autoVacuumCommit(BtShared *pBt){
if( !pBt->incrVacuum ){
Pgno nFin; /* Number of pages in database after autovacuuming */
Pgno nFree; /* Number of pages on the freelist initially */
- Pgno nPtrmap; /* Number of PtrMap pages to be freed */
Pgno iFree; /* The next page to be freed */
- int nEntry; /* Number of entries on one ptrmap page */
Pgno nOrig; /* Database size before freeing */
nOrig = btreePagecount(pBt);
@@ -50549,26 +55471,20 @@ static int autoVacuumCommit(BtShared *pBt){
}
nFree = get4byte(&pBt->pPage1->aData[36]);
- nEntry = pBt->usableSize/5;
- nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
- nFin = nOrig - nFree - nPtrmap;
- if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
- nFin--;
- }
- while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
- nFin--;
- }
+ nFin = finalDbSize(pBt, nOrig, nFree);
if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT;
-
+ if( nFin<nOrig ){
+ rc = saveAllCursors(pBt, 0, 0);
+ }
for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){
- rc = incrVacuumStep(pBt, nFin, iFree);
+ rc = incrVacuumStep(pBt, nFin, iFree, 1);
}
if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[32], 0);
put4byte(&pBt->pPage1->aData[36], 0);
put4byte(&pBt->pPage1->aData[28], nFin);
- sqlite3PagerTruncateImage(pBt->pPager, nFin);
+ pBt->bDoTruncate = 1;
pBt->nPage = nFin;
}
if( rc!=SQLITE_OK ){
@@ -50576,7 +55492,7 @@ static int autoVacuumCommit(BtShared *pBt){
}
}
- assert( nRef==sqlite3PagerRefcount(pPager) );
+ assert( nRef>=sqlite3PagerRefcount(pPager) );
return rc;
}
@@ -50623,6 +55539,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
return rc;
}
}
+ if( pBt->bDoTruncate ){
+ sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
+ }
#endif
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
sqlite3BtreeLeave(p);
@@ -50636,10 +55555,13 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
*/
static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3 *db = p->db;
assert( sqlite3BtreeHoldsMutex(p) );
- btreeClearHasContent(pBt);
- if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ pBt->bDoTruncate = 0;
+#endif
+ if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){
/* If there are other active statements that belong to this database
** handle, downgrade to a read-only transaction. The other statements
** may still be reading from the database. */
@@ -50713,6 +55635,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
return rc;
}
pBt->inTransaction = TRANS_READ;
+ btreeClearHasContent(pBt);
}
btreeEndTransaction(p);
@@ -50734,88 +55657,94 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Btree *p){
return rc;
}
-#ifndef NDEBUG
-/*
-** Return the number of write-cursors open on this handle. This is for use
-** in assert() expressions, so it is only compiled if NDEBUG is not
-** defined.
-**
-** For the purposes of this routine, a write-cursor is any cursor that
-** is capable of writing to the databse. That means the cursor was
-** originally opened for writing and the cursor has not be disabled
-** by having its state changed to CURSOR_FAULT.
-*/
-static int countWriteCursors(BtShared *pBt){
- BtCursor *pCur;
- int r = 0;
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++;
- }
- return r;
-}
-#endif
-
/*
** This routine sets the state to CURSOR_FAULT and the error
-** code to errCode for every cursor on BtShared that pBtree
-** references.
-**
-** Every cursor is tripped, including cursors that belong
-** to other database connections that happen to be sharing
-** the cache with pBtree.
-**
-** This routine gets called when a rollback occurs.
-** All cursors using the same cache must be tripped
-** to prevent them from trying to use the btree after
-** the rollback. The rollback may have deleted tables
-** or moved root pages, so it is not sufficient to
-** save the state of the cursor. The cursor must be
-** invalidated.
-*/
-SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
+** code to errCode for every cursor on any BtShared that pBtree
+** references. Or if the writeOnly flag is set to 1, then only
+** trip write cursors and leave read cursors unchanged.
+**
+** Every cursor is a candidate to be tripped, including cursors
+** that belong to other database connections that happen to be
+** sharing the cache with pBtree.
+**
+** This routine gets called when a rollback occurs. If the writeOnly
+** flag is true, then only write-cursors need be tripped - read-only
+** cursors save their current positions so that they may continue
+** following the rollback. Or, if writeOnly is false, all cursors are
+** tripped. In general, writeOnly is false if the transaction being
+** rolled back modified the database schema. In this case b-tree root
+** pages may be moved or deleted from the database altogether, making
+** it unsafe for read cursors to continue.
+**
+** If the writeOnly flag is true and an error is encountered while
+** saving the current position of a read-only cursor, all cursors,
+** including all read-cursors are tripped.
+**
+** SQLITE_OK is returned if successful, or if an error occurs while
+** saving a cursor position, an SQLite error code.
+*/
+SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
BtCursor *p;
- sqlite3BtreeEnter(pBtree);
- for(p=pBtree->pBt->pCursor; p; p=p->pNext){
- int i;
- sqlite3BtreeClearCursor(p);
- p->eState = CURSOR_FAULT;
- p->skipNext = errCode;
- for(i=0; i<=p->iPage; i++){
- releasePage(p->apPage[i]);
- p->apPage[i] = 0;
+ int rc = SQLITE_OK;
+
+ assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
+ if( pBtree ){
+ sqlite3BtreeEnter(pBtree);
+ for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+ int i;
+ if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
+ if( p->eState==CURSOR_VALID ){
+ rc = saveCursorPosition(p);
+ if( rc!=SQLITE_OK ){
+ (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0);
+ break;
+ }
+ }
+ }else{
+ sqlite3BtreeClearCursor(p);
+ p->eState = CURSOR_FAULT;
+ p->skipNext = errCode;
+ }
+ for(i=0; i<=p->iPage; i++){
+ releasePage(p->apPage[i]);
+ p->apPage[i] = 0;
+ }
}
+ sqlite3BtreeLeave(pBtree);
}
- sqlite3BtreeLeave(pBtree);
+ return rc;
}
/*
-** Rollback the transaction in progress. All cursors will be
-** invalided by this operation. Any attempt to use a cursor
-** that was open at the beginning of this operation will result
-** in an error.
+** Rollback the transaction in progress.
+**
+** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
+** Only write cursors are tripped if writeOnly is true but all cursors are
+** tripped if writeOnly is false. Any attempt to use
+** a tripped cursor will result in an error.
**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
-SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p){
+SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
int rc;
BtShared *pBt = p->pBt;
MemPage *pPage1;
+ assert( writeOnly==1 || writeOnly==0 );
+ assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK );
sqlite3BtreeEnter(p);
- rc = saveAllCursors(pBt, 0, 0);
-#ifndef SQLITE_OMIT_SHARED_CACHE
- if( rc!=SQLITE_OK ){
- /* This is a horrible situation. An IO or malloc() error occurred whilst
- ** trying to save cursor positions. If this is an automatic rollback (as
- ** the result of a constraint, malloc() failure or IO error) then
- ** the cache may be internally inconsistent (not contain valid trees) so
- ** we cannot simply return the error to the caller. Instead, abort
- ** all queries that may be using any of the cursors that failed to save.
- */
- sqlite3BtreeTripAllCursors(p, rc);
+ if( tripCode==SQLITE_OK ){
+ rc = tripCode = saveAllCursors(pBt, 0, 0);
+ if( rc ) writeOnly = 0;
+ }else{
+ rc = SQLITE_OK;
+ }
+ if( tripCode ){
+ int rc2 = sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
+ assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) );
+ if( rc2!=SQLITE_OK ) rc = rc2;
}
-#endif
btreeIntegrity(p);
if( p->inTrans==TRANS_WRITE ){
@@ -50838,8 +55767,9 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p){
pBt->nPage = nPage;
releasePage(pPage1);
}
- assert( countWriteCursors(pBt)==0 );
+ assert( countValidCursors(pBt, 1)==0 );
pBt->inTransaction = TRANS_READ;
+ btreeClearHasContent(pBt);
}
btreeEndTransaction(p);
@@ -50848,7 +55778,7 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p){
}
/*
-** Start a statement subtransaction. The subtransaction can can be rolled
+** Start a statement subtransaction. The subtransaction can be rolled
** back independently of the main transaction. You must start a transaction
** before starting a subtransaction. The subtransaction is ended automatically
** if the main transaction commits or rolls back.
@@ -50870,7 +55800,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p);
assert( p->inTrans==TRANS_WRITE );
- assert( pBt->readOnly==0 );
+ assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( iStatement>0 );
assert( iStatement>p->db->nSavepoint );
assert( pBt->inTransaction==TRANS_WRITE );
@@ -50905,7 +55835,9 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
sqlite3BtreeEnter(p);
rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
if( rc==SQLITE_OK ){
- if( iSavepoint<0 && pBt->initiallyEmpty ) pBt->nPage = 0;
+ if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){
+ pBt->nPage = 0;
+ }
rc = newDatabase(pBt);
pBt->nPage = get4byte(28 + pBt->pPage1->aData);
@@ -50975,11 +55907,16 @@ static int btreeCursor(
assert( wrFlag==0 || p->inTrans==TRANS_WRITE );
assert( pBt->pPage1 && pBt->pPage1->aData );
- if( NEVER(wrFlag && pBt->readOnly) ){
+ if( NEVER(wrFlag && (pBt->btsFlags & BTS_READ_ONLY)!=0) ){
return SQLITE_READONLY;
}
+ if( wrFlag ){
+ allocateTempSpace(pBt);
+ if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM;
+ }
if( iTable==1 && btreePagecount(pBt)==0 ){
- return SQLITE_EMPTY;
+ assert( wrFlag==0 );
+ iTable = 0;
}
/* Now that no other errors can occur, finish filling in the BtCursor
@@ -50989,14 +55926,14 @@ static int btreeCursor(
pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
pCur->pBt = pBt;
- pCur->wrFlag = (u8)wrFlag;
+ assert( wrFlag==0 || wrFlag==BTCF_WriteFlag );
+ pCur->curFlags = wrFlag;
pCur->pNext = pBt->pCursor;
if( pCur->pNext ){
pCur->pNext->pPrev = pCur;
}
pBt->pCursor = pCur;
pCur->eState = CURSOR_INVALID;
- pCur->cachedRowid = 0;
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3BtreeCursor(
@@ -51038,36 +55975,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){
}
/*
-** Set the cached rowid value of every cursor in the same database file
-** as pCur and having the same root page number as pCur. The value is
-** set to iRowid.
-**
-** Only positive rowid values are considered valid for this cache.
-** The cache is initialized to zero, indicating an invalid cache.
-** A btree will work fine with zero or negative rowids. We just cannot
-** cache zero or negative rowids, which means tables that use zero or
-** negative rowids might run a little slower. But in practice, zero
-** or negative rowids are very uncommon so this should not be a problem.
-*/
-SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){
- BtCursor *p;
- for(p=pCur->pBt->pCursor; p; p=p->pNext){
- if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid;
- }
- assert( pCur->cachedRowid==iRowid );
-}
-
-/*
-** Return the cached rowid for the given cursor. A negative or zero
-** return value indicates that the rowid cache is invalid and should be
-** ignored. If the rowid cache has never before been set, then a
-** zero is returned.
-*/
-SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){
- return pCur->cachedRowid;
-}
-
-/*
** Close a cursor. The read lock on the database file is released
** when the last cursor is closed.
*/
@@ -51090,7 +55997,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
releasePage(pCur->apPage[i]);
}
unlockBtreeIfUnused(pBt);
- invalidateOverflowCache(pCur);
+ sqlite3DbFree(pBtree->db, pCur->aOverflow);
/* sqlite3_free(pCur); */
sqlite3BtreeLeave(pBtree);
}
@@ -51109,7 +56016,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
** compiler to crash when getCellInfo() is implemented as a macro.
** But there is a measureable speed advantage to using the macro on gcc
** (when less compiler optimizations like -Os or -O0 are used and the
-** compiler is not doing agressive inlining.) So we use a real function
+** compiler is not doing aggressive inlining.) So we use a real function
** for MSVC and a macro for everything else. Ticket #2457.
*/
#ifndef NDEBUG
@@ -51118,7 +56025,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
int iPage = pCur->iPage;
memset(&info, 0, sizeof(info));
btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
- assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
+ assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
}
#else
#define assertCellInfo(x)
@@ -51129,7 +56036,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
if( pCur->info.nSize==0 ){
int iPage = pCur->iPage;
btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
- pCur->validNKey = 1;
+ pCur->curFlags |= BTCF_ValidNKey;
}else{
assertCellInfo(pCur);
}
@@ -51139,8 +56046,8 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
#define getCellInfo(pCur) \
if( pCur->info.nSize==0 ){ \
int iPage = pCur->iPage; \
- btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
- pCur->validNKey = 1; \
+ btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
+ pCur->curFlags |= BTCF_ValidNKey; \
}else{ \
assertCellInfo(pCur); \
}
@@ -51171,13 +56078,9 @@ SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor *pCur){
*/
SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
assert( cursorHoldsMutex(pCur) );
- assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
- if( pCur->eState!=CURSOR_VALID ){
- *pSize = 0;
- }else{
- getCellInfo(pCur);
- *pSize = pCur->info.nKey;
- }
+ assert( pCur->eState==CURSOR_VALID );
+ getCellInfo(pCur);
+ *pSize = pCur->info.nKey;
return SQLITE_OK;
}
@@ -51196,8 +56099,9 @@ SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
+ assert( pCur->apPage[pCur->iPage]->intKeyLeaf==1 );
getCellInfo(pCur);
- *pSize = pCur->info.nData;
+ *pSize = pCur->info.nPayload;
return SQLITE_OK;
}
@@ -51261,7 +56165,7 @@ static int getOverflowPage(
assert( next==0 || rc==SQLITE_DONE );
if( rc==SQLITE_OK ){
- rc = btreeGetPage(pBt, ovfl, &pPage, 0);
+ rc = btreeGetPage(pBt, ovfl, &pPage, (ppPage==0) ? PAGER_GET_READONLY : 0);
assert( rc==SQLITE_OK || pPage==0 );
if( rc==SQLITE_OK ){
next = get4byte(pPage->aData);
@@ -51311,10 +56215,12 @@ static int copyPayload(
/*
** This function is used to read or overwrite payload information
-** for the entry that the pCur cursor is pointing to. If the eOp
-** parameter is 0, this is a read operation (data copied into
-** buffer pBuf). If it is non-zero, a write (data copied from
-** buffer pBuf).
+** for the entry that the pCur cursor is pointing to. The eOp
+** argument is interpreted as follows:
+**
+** 0: The operation is a read. Populate the overflow cache.
+** 1: The operation is a write. Populate the overflow cache.
+** 2: The operation is a read. Do not populate the overflow cache.
**
** A total of "amt" bytes are read or written beginning at "offset".
** Data is read to or from the buffer pBuf.
@@ -51322,11 +56228,11 @@ static int copyPayload(
** The content being read or written might appear on the main page
** or be scattered out on multiple overflow pages.
**
-** If the BtCursor.isIncrblobHandle flag is set, and the current
-** cursor entry uses one or more overflow pages, this function
-** allocates space for and lazily popluates the overflow page-list
-** cache array (BtCursor.aOverflow). Subsequent calls use this
-** cache to make seeking to the supplied offset more efficient.
+** If the current cursor entry uses one or more overflow pages and the
+** eOp argument is not 2, this function may allocate space for and lazily
+** populates the overflow page-list cache array (BtCursor.aOverflow).
+** Subsequent calls use this cache to make seeking to the supplied offset
+** more efficient.
**
** Once an overflow page-list cache has been allocated, it may be
** invalidated if some other cursor writes to the same table, or if
@@ -51346,23 +56252,28 @@ static int accessPayload(
){
unsigned char *aPayload;
int rc = SQLITE_OK;
- u32 nKey;
int iIdx = 0;
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ unsigned char * const pBufStart = pBuf;
+ int bEnd; /* True if reading to end of data */
+#endif
assert( pPage );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
assert( cursorHoldsMutex(pCur) );
+ assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
getCellInfo(pCur);
- aPayload = pCur->info.pCell + pCur->info.nHeader;
- nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey);
+ aPayload = pCur->info.pPayload;
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ bEnd = offset+amt==pCur->info.nPayload;
+#endif
+ assert( offset+amt <= pCur->info.nPayload );
- if( NEVER(offset+amt > nKey+pCur->info.nData)
- || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
- ){
+ if( &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){
/* Trying to read or write past the end of the data is an error */
return SQLITE_CORRUPT_BKPT;
}
@@ -51373,7 +56284,7 @@ static int accessPayload(
if( a+offset>pCur->info.nLocal ){
a = pCur->info.nLocal - offset;
}
- rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage);
+ rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage);
offset = 0;
pBuf += a;
amt -= a;
@@ -51387,21 +56298,30 @@ static int accessPayload(
nextPage = get4byte(&aPayload[pCur->info.nLocal]);
-#ifndef SQLITE_OMIT_INCRBLOB
- /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[]
- ** has not been allocated, allocate it now. The array is sized at
- ** one entry for each overflow page in the overflow chain. The
- ** page number of the first overflow page is stored in aOverflow[0],
- ** etc. A value of 0 in the aOverflow[] array means "not yet known"
- ** (the cache is lazily populated).
+ /* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
+ ** Except, do not allocate aOverflow[] for eOp==2.
+ **
+ ** The aOverflow[] array is sized at one entry for each overflow page
+ ** in the overflow chain. The page number of the first overflow page is
+ ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array
+ ** means "not yet known" (the cache is lazily populated).
*/
- if( pCur->isIncrblobHandle && !pCur->aOverflow ){
+ if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
- pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl);
- /* nOvfl is always positive. If it were zero, fetchPayload would have
- ** been used instead of this routine. */
- if( ALWAYS(nOvfl) && !pCur->aOverflow ){
- rc = SQLITE_NOMEM;
+ if( nOvfl>pCur->nOvflAlloc ){
+ Pgno *aNew = (Pgno*)sqlite3DbRealloc(
+ pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno)
+ );
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pCur->nOvflAlloc = nOvfl*2;
+ pCur->aOverflow = aNew;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
+ pCur->curFlags |= BTCF_ValidOvfl;
}
}
@@ -51409,22 +56329,21 @@ static int accessPayload(
** entry for the first required overflow page is valid, skip
** directly to it.
*/
- if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){
+ if( (pCur->curFlags & BTCF_ValidOvfl)!=0
+ && pCur->aOverflow[offset/ovflSize]
+ ){
iIdx = (offset/ovflSize);
nextPage = pCur->aOverflow[iIdx];
offset = (offset%ovflSize);
}
-#endif
for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){
-#ifndef SQLITE_OMIT_INCRBLOB
/* If required, populate the overflow page-list cache. */
- if( pCur->aOverflow ){
+ if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage);
pCur->aOverflow[iIdx] = nextPage;
}
-#endif
if( offset>=ovflSize ){
/* The only reason to read this page is to obtain the page
@@ -51432,33 +56351,78 @@ static int accessPayload(
** data is not required. So first try to lookup the overflow
** page-list cache, if any, then fall back to the getOverflowPage()
** function.
+ **
+ ** Note that the aOverflow[] array must be allocated because eOp!=2
+ ** here. If eOp==2, then offset==0 and this branch is never taken.
*/
-#ifndef SQLITE_OMIT_INCRBLOB
- if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){
+ assert( eOp!=2 );
+ assert( pCur->curFlags & BTCF_ValidOvfl );
+ if( pCur->aOverflow[iIdx+1] ){
nextPage = pCur->aOverflow[iIdx+1];
- } else
-#endif
+ }else{
rc = getOverflowPage(pBt, nextPage, 0, &nextPage);
+ }
offset -= ovflSize;
}else{
/* Need to read this page properly. It contains some of the
** range of data that is being read (eOp==0) or written (eOp!=0).
*/
- DbPage *pDbPage;
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ sqlite3_file *fd;
+#endif
int a = amt;
- rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
- if( rc==SQLITE_OK ){
- aPayload = sqlite3PagerGetData(pDbPage);
- nextPage = get4byte(aPayload);
- if( a + offset > ovflSize ){
- a = ovflSize - offset;
+ if( a + offset > ovflSize ){
+ a = ovflSize - offset;
+ }
+
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ /* If all the following are true:
+ **
+ ** 1) this is a read operation, and
+ ** 2) data is required from the start of this overflow page, and
+ ** 3) the database is file-backed, and
+ ** 4) there is no open write-transaction, and
+ ** 5) the database is not a WAL database,
+ ** 6) all data from the page is being read.
+ ** 7) at least 4 bytes have already been read into the output buffer
+ **
+ ** then data can be read directly from the database file into the
+ ** output buffer, bypassing the page-cache altogether. This speeds
+ ** up loading large records that span many overflow pages.
+ */
+ if( (eOp&0x01)==0 /* (1) */
+ && offset==0 /* (2) */
+ && (bEnd || a==ovflSize) /* (6) */
+ && pBt->inTransaction==TRANS_READ /* (4) */
+ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
+ && pBt->pPage1->aData[19]==0x01 /* (5) */
+ && &pBuf[-4]>=pBufStart /* (7) */
+ ){
+ u8 aSave[4];
+ u8 *aWrite = &pBuf[-4];
+ assert( aWrite>=pBufStart ); /* hence (7) */
+ memcpy(aSave, aWrite, 4);
+ rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
+ nextPage = get4byte(aWrite);
+ memcpy(aWrite, aSave, 4);
+ }else
+#endif
+
+ {
+ DbPage *pDbPage;
+ rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage,
+ ((eOp&0x01)==0 ? PAGER_GET_READONLY : 0)
+ );
+ if( rc==SQLITE_OK ){
+ aPayload = sqlite3PagerGetData(pDbPage);
+ nextPage = get4byte(aPayload);
+ rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage);
+ sqlite3PagerUnref(pDbPage);
+ offset = 0;
}
- rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
- sqlite3PagerUnref(pDbPage);
- offset = 0;
- amt -= a;
- pBuf += a;
}
+ amt -= a;
+ pBuf += a;
}
}
}
@@ -51471,7 +56435,7 @@ static int accessPayload(
/*
** Read part of the key associated with cursor pCur. Exactly
-** "amt" bytes will be transfered into pBuf[]. The transfer
+** "amt" bytes will be transferred into pBuf[]. The transfer
** begins at "offset".
**
** The caller must ensure that pCur is pointing to a valid row
@@ -51521,10 +56485,10 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p
/*
** Return a pointer to payload information from the entry that the
** pCur cursor is pointing to. The pointer is to the beginning of
-** the key if skipKey==0 and it points to the beginning of data if
-** skipKey==1. The number of bytes of available key/data is written
-** into *pAmt. If *pAmt==0, then the value returned will not be
-** a valid pointer.
+** the key if index btrees (pPage->intKey==0) and is the data for
+** table btrees (pPage->intKey==1). The number of bytes of available
+** key/data is written into *pAmt. If *pAmt==0, then the value
+** returned will not be a valid pointer.
**
** This routine is an optimization. It is common for the entire key
** and data to fit on the local page and for there to be no overflow
@@ -51537,41 +56501,18 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p
** page of the database. The data might change or move the next time
** any btree routine is called.
*/
-static const unsigned char *fetchPayload(
+static const void *fetchPayload(
BtCursor *pCur, /* Cursor pointing to entry to read from */
- int *pAmt, /* Write the number of available bytes here */
- int skipKey /* read beginning at data if this is true */
+ u32 *pAmt /* Write the number of available bytes here */
){
- unsigned char *aPayload;
- MemPage *pPage;
- u32 nKey;
- u32 nLocal;
-
assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
assert( pCur->eState==CURSOR_VALID );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( cursorHoldsMutex(pCur) );
- pPage = pCur->apPage[pCur->iPage];
- assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
- if( NEVER(pCur->info.nSize==0) ){
- btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage],
- &pCur->info);
- }
- aPayload = pCur->info.pCell;
- aPayload += pCur->info.nHeader;
- if( pPage->intKey ){
- nKey = 0;
- }else{
- nKey = (int)pCur->info.nKey;
- }
- if( skipKey ){
- aPayload += nKey;
- nLocal = pCur->info.nLocal - nKey;
- }else{
- nLocal = pCur->info.nLocal;
- assert( nLocal<=nKey );
- }
- *pAmt = nLocal;
- return aPayload;
+ assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ assert( pCur->info.nSize>0 );
+ *pAmt = pCur->info.nLocal;
+ return (void*)pCur->info.pPayload;
}
@@ -51589,23 +56530,11 @@ static const unsigned char *fetchPayload(
** These routines is used to get quick access to key and data
** in the common case where no overflow pages are used.
*/
-SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
- const void *p = 0;
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- assert( cursorHoldsMutex(pCur) );
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- p = (const void*)fetchPayload(pCur, pAmt, 0);
- }
- return p;
+SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){
+ return fetchPayload(pCur, pAmt);
}
-SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
- const void *p = 0;
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- assert( cursorHoldsMutex(pCur) );
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- p = (const void*)fetchPayload(pCur, pAmt, 1);
- }
- return p;
+SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){
+ return fetchPayload(pCur, pAmt);
}
@@ -51627,24 +56556,26 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
+ assert( pCur->iPage>=0 );
if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, newPgno, &pNewPage);
+ rc = getAndInitPage(pBt, newPgno, &pNewPage,
+ (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
if( rc ) return rc;
pCur->apPage[i+1] = pNewPage;
pCur->aiIdx[i+1] = 0;
pCur->iPage++;
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){
return SQLITE_CORRUPT_BKPT;
}
return SQLITE_OK;
}
-#ifndef NDEBUG
+#if 0
/*
** Page pParent is an internal (non-leaf) tree page. This function
** asserts that page number iChild is the left-child if the iIdx'th
@@ -51677,15 +56608,25 @@ static void moveToParent(BtCursor *pCur){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage>0 );
assert( pCur->apPage[pCur->iPage] );
+
+ /* UPDATE: It is actually possible for the condition tested by the assert
+ ** below to be untrue if the database file is corrupt. This can occur if
+ ** one cursor has modified page pParent while a reference to it is held
+ ** by a second cursor. Which can only happen if a single page is linked
+ ** into more than one b-tree structure in a corrupt database. */
+#if 0
assertParentIndex(
pCur->apPage[pCur->iPage-1],
pCur->aiIdx[pCur->iPage-1],
pCur->apPage[pCur->iPage]->pgno
);
+#endif
+ testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell );
+
releasePage(pCur->apPage[pCur->iPage]);
pCur->iPage--;
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
}
/*
@@ -51712,8 +56653,6 @@ static void moveToParent(BtCursor *pCur){
static int moveToRoot(BtCursor *pCur){
MemPage *pRoot;
int rc = SQLITE_OK;
- Btree *p = pCur->pBtree;
- BtShared *pBt = p->pBt;
assert( cursorHoldsMutex(pCur) );
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
@@ -51728,52 +56667,51 @@ static int moveToRoot(BtCursor *pCur){
}
if( pCur->iPage>=0 ){
- int i;
- for(i=1; i<=pCur->iPage; i++){
- releasePage(pCur->apPage[i]);
- }
- pCur->iPage = 0;
+ while( pCur->iPage ) releasePage(pCur->apPage[pCur->iPage--]);
+ }else if( pCur->pgnoRoot==0 ){
+ pCur->eState = CURSOR_INVALID;
+ return SQLITE_OK;
}else{
- rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]);
+ rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
+ (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
return rc;
}
pCur->iPage = 0;
-
- /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
- ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
- ** NULL, the caller expects a table b-tree. If this is not the case,
- ** return an SQLITE_CORRUPT error. */
- assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 );
- if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){
- return SQLITE_CORRUPT_BKPT;
- }
}
-
- /* Assert that the root page is of the correct type. This must be the
- ** case as the call to this function that loaded the root-page (either
- ** this call or a previous invocation) would have detected corruption
- ** if the assumption were not true, and it is not possible for the flags
- ** byte to have been modified while this cursor is holding a reference
- ** to the page. */
pRoot = pCur->apPage[0];
assert( pRoot->pgno==pCur->pgnoRoot );
- assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey );
+
+ /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
+ ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
+ ** NULL, the caller expects a table b-tree. If this is not the case,
+ ** return an SQLITE_CORRUPT error.
+ **
+ ** Earlier versions of SQLite assumed that this test could not fail
+ ** if the root page was already loaded when this function was called (i.e.
+ ** if pCur->iPage>=0). But this is not so if the database is corrupted
+ ** in such a way that page pRoot is linked into a second b-tree table
+ ** (or the freelist). */
+ assert( pRoot->intKey==1 || pRoot->intKey==0 );
+ if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
+ return SQLITE_CORRUPT_BKPT;
+ }
pCur->aiIdx[0] = 0;
pCur->info.nSize = 0;
- pCur->atLast = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
- if( pRoot->nCell==0 && !pRoot->leaf ){
+ if( pRoot->nCell>0 ){
+ pCur->eState = CURSOR_VALID;
+ }else if( !pRoot->leaf ){
Pgno subpage;
if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT;
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
pCur->eState = CURSOR_VALID;
rc = moveToChild(pCur, subpage);
}else{
- pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
+ pCur->eState = CURSOR_INVALID;
}
return rc;
}
@@ -51817,17 +56755,16 @@ static int moveToRightmost(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
- while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
+ while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->aiIdx[pCur->iPage] = pPage->nCell;
rc = moveToChild(pCur, pgno);
+ if( rc ) return rc;
}
- if( rc==SQLITE_OK ){
- pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
- }
- return rc;
+ pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
+ assert( pCur->info.nSize==0 );
+ assert( (pCur->curFlags & BTCF_ValidNKey)==0 );
+ return SQLITE_OK;
}
/* Move the cursor to the first entry in the table. Return SQLITE_OK
@@ -51842,7 +56779,7 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
if( pCur->eState==CURSOR_INVALID ){
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->apPage[pCur->iPage]->nCell>0 );
@@ -51864,7 +56801,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
/* If the cursor already points to the last entry, this is a no-op. */
- if( CURSOR_VALID==pCur->eState && pCur->atLast ){
+ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
#ifdef SQLITE_DEBUG
/* This block serves to assert() that the cursor really does point
** to the last entry in the b-tree. */
@@ -51881,13 +56818,18 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
if( CURSOR_INVALID==pCur->eState ){
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
- pCur->atLast = rc==SQLITE_OK ?1:0;
+ if( rc==SQLITE_OK ){
+ pCur->curFlags |= BTCF_AtLast;
+ }else{
+ pCur->curFlags &= ~BTCF_AtLast;
+ }
+
}
}
return rc;
@@ -51929,6 +56871,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
int *pRes /* Write search results here */
){
int rc;
+ RecordCompare xRecordCompare;
assert( cursorHoldsMutex(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
@@ -51937,37 +56880,48 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
/* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */
- if( pCur->eState==CURSOR_VALID && pCur->validNKey
+ if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
&& pCur->apPage[0]->intKey
){
if( pCur->info.nKey==intKey ){
*pRes = 0;
return SQLITE_OK;
}
- if( pCur->atLast && pCur->info.nKey<intKey ){
+ if( (pCur->curFlags & BTCF_AtLast)!=0 && pCur->info.nKey<intKey ){
*pRes = -1;
return SQLITE_OK;
}
}
+ if( pIdxKey ){
+ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
+ pIdxKey->errCode = 0;
+ assert( pIdxKey->default_rc==1
+ || pIdxKey->default_rc==0
+ || pIdxKey->default_rc==-1
+ );
+ }else{
+ xRecordCompare = 0; /* All keys are integers */
+ }
+
rc = moveToRoot(pCur);
if( rc ){
return rc;
}
- assert( pCur->apPage[pCur->iPage] );
- assert( pCur->apPage[pCur->iPage]->isInit );
- assert( pCur->apPage[pCur->iPage]->nCell>0 || pCur->eState==CURSOR_INVALID );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage] );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit );
+ assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 );
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
assert( pCur->apPage[0]->intKey || pIdxKey );
for(;;){
- int lwr, upr, idx;
+ int lwr, upr, idx, c;
Pgno chldPg;
MemPage *pPage = pCur->apPage[pCur->iPage];
- int c;
+ u8 *pCell; /* Pointer to current cell in pPage */
/* pPage->nCell must be greater than zero. If this is the root-page
** the cursor would have been INVALID above and this for(;;) loop
@@ -51979,35 +56933,47 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
assert( pPage->intKey==(pIdxKey==0) );
lwr = 0;
upr = pPage->nCell-1;
- if( biasRight ){
- pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
- }else{
- pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
- }
- for(;;){
- u8 *pCell; /* Pointer to current cell in pPage */
-
- assert( idx==pCur->aiIdx[pCur->iPage] );
- pCur->info.nSize = 0;
- pCell = findCell(pPage, idx) + pPage->childPtrSize;
- if( pPage->intKey ){
+ assert( biasRight==0 || biasRight==1 );
+ idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( xRecordCompare==0 ){
+ for(;;){
i64 nCellKey;
- if( pPage->hasData ){
- u32 dummy;
- pCell += getVarint32(pCell, dummy);
+ pCell = findCell(pPage, idx) + pPage->childPtrSize;
+ if( pPage->intKeyLeaf ){
+ while( 0x80 <= *(pCell++) ){
+ if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
+ }
}
getVarint(pCell, (u64*)&nCellKey);
- if( nCellKey==intKey ){
- c = 0;
- }else if( nCellKey<intKey ){
- c = -1;
+ if( nCellKey<intKey ){
+ lwr = idx+1;
+ if( lwr>upr ){ c = -1; break; }
+ }else if( nCellKey>intKey ){
+ upr = idx-1;
+ if( lwr>upr ){ c = +1; break; }
}else{
- assert( nCellKey>intKey );
- c = +1;
+ assert( nCellKey==intKey );
+ pCur->curFlags |= BTCF_ValidNKey;
+ pCur->info.nKey = nCellKey;
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( !pPage->leaf ){
+ lwr = idx;
+ goto moveto_next_layer;
+ }else{
+ *pRes = 0;
+ rc = SQLITE_OK;
+ goto moveto_finish;
+ }
}
- pCur->validNKey = 1;
- pCur->info.nKey = nCellKey;
- }else{
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
+ }
+ }else{
+ for(;;){
+ int nCell;
+ pCell = findCell(pPage, idx) + pPage->childPtrSize;
+
/* The maximum supported page-size is 65536 bytes. This means that
** the maximum number of record bytes stored on an index B-Tree
** page is less than 16384 bytes and may be stored as a 2-byte
@@ -52016,18 +56982,20 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
** stored entirely within the b-tree page by inspecting the first
** 2 bytes of the cell.
*/
- int nCell = pCell[0];
- if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){
+ nCell = pCell[0];
+ if( nCell<=pPage->max1bytePayload ){
/* This branch runs if the record-size field of the cell is a
** single byte varint and the record fits entirely on the main
** b-tree page. */
- c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
+ testcase( pCell+nCell+1==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
}else if( !(pCell[1] & 0x80)
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
){
/* The record-size field is a 2 byte varint and the record
** fits entirely on the main b-tree page. */
- c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
+ testcase( pCell+nCell+2==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
}else{
/* The record flows over onto one or more overflow pages. In
** this case the whole cell needs to be parsed, a buffer allocated
@@ -52042,58 +57010,58 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
rc = SQLITE_NOMEM;
goto moveto_finish;
}
- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2);
if( rc ){
sqlite3_free(pCellKey);
goto moveto_finish;
}
- c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
+ c = xRecordCompare(nCell, pCellKey, pIdxKey);
sqlite3_free(pCellKey);
}
- }
- if( c==0 ){
- if( pPage->intKey && !pPage->leaf ){
- lwr = idx;
- upr = lwr - 1;
- break;
+ assert(
+ (pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
+ && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
+ );
+ if( c<0 ){
+ lwr = idx+1;
+ }else if( c>0 ){
+ upr = idx-1;
}else{
+ assert( c==0 );
*pRes = 0;
rc = SQLITE_OK;
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( pIdxKey->errCode ) rc = SQLITE_CORRUPT;
goto moveto_finish;
}
+ if( lwr>upr ) break;
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
}
- if( c<0 ){
- lwr = idx+1;
- }else{
- upr = idx-1;
- }
- if( lwr>upr ){
- break;
- }
- pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
}
- assert( lwr==upr+1 );
+ assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
assert( pPage->isInit );
if( pPage->leaf ){
- chldPg = 0;
- }else if( lwr>=pPage->nCell ){
- chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- }else{
- chldPg = get4byte(findCell(pPage, lwr));
- }
- if( chldPg==0 ){
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
*pRes = c;
rc = SQLITE_OK;
goto moveto_finish;
}
+moveto_next_layer:
+ if( lwr>=pPage->nCell ){
+ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ }else{
+ chldPg = get4byte(findCell(pPage, lwr));
+ }
pCur->aiIdx[pCur->iPage] = (u16)lwr;
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
rc = moveToChild(pCur, chldPg);
- if( rc ) goto moveto_finish;
+ if( rc ) break;
}
moveto_finish:
+ pCur->info.nSize = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
return rc;
}
@@ -52118,43 +57086,67 @@ SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor *pCur){
** successful then set *pRes=0. If the cursor
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
-*/
-SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+**
+** The main entry point is sqlite3BtreeNext(). That routine is optimized
+** for the common case of merely incrementing the cell counter BtCursor.aiIdx
+** to the next cell on the current page. The (slower) btreeNext() helper
+** routine is called when it is necessary to move to a different page or
+** to restore the cursor.
+**
+** The calling function will set *pRes to 0 or 1. The initial *pRes value
+** will be 1 if the cursor being stepped corresponds to an SQL index and
+** if this routine could have been skipped if that SQL index had been
+** a unique index. Otherwise the caller will have set *pRes to zero.
+** Zero is the common case. The btree implementation is free to use the
+** initial *pRes value as a hint to improve performance, but the current
+** SQLite btree implementation does not. (Note that the comdb2 btree
+** implementation does use this hint, however.)
+*/
+static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){
int rc;
int idx;
MemPage *pPage;
assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- assert( pRes!=0 );
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext>0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ assert( *pRes==0 );
+ if( pCur->eState!=CURSOR_VALID ){
+ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
+ rc = restoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext>0 ){
+ pCur->skipNext = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
idx = ++pCur->aiIdx[pCur->iPage];
assert( pPage->isInit );
- assert( idx<=pPage->nCell );
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ /* If the database file is corrupt, it is possible for the value of idx
+ ** to be invalid here. This can only occur if a second cursor modifies
+ ** the page while cursor pCur is holding a reference to it. Which can
+ ** only happen if the database is corrupt in such a way as to link the
+ ** page into more than one b-tree structure. */
+ testcase( idx>pPage->nCell );
+
if( idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
if( rc ) return rc;
- rc = moveToLeftmost(pCur);
- *pRes = 0;
- return rc;
+ return moveToLeftmost(pCur);
}
do{
if( pCur->iPage==0 ){
@@ -52165,58 +57157,97 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
moveToParent(pCur);
pPage = pCur->apPage[pCur->iPage];
}while( pCur->aiIdx[pCur->iPage]>=pPage->nCell );
- *pRes = 0;
if( pPage->intKey ){
- rc = sqlite3BtreeNext(pCur, pRes);
+ return sqlite3BtreeNext(pCur, pRes);
}else{
- rc = SQLITE_OK;
+ return SQLITE_OK;
}
- return rc;
}
+ if( pPage->leaf ){
+ return SQLITE_OK;
+ }else{
+ return moveToLeftmost(pCur);
+ }
+}
+SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+ MemPage *pPage;
+ assert( cursorHoldsMutex(pCur) );
+ assert( pRes!=0 );
+ assert( *pRes==0 || *pRes==1 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ pCur->info.nSize = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
*pRes = 0;
+ if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur, pRes);
+ pPage = pCur->apPage[pCur->iPage];
+ if( (++pCur->aiIdx[pCur->iPage])>=pPage->nCell ){
+ pCur->aiIdx[pCur->iPage]--;
+ return btreeNext(pCur, pRes);
+ }
if( pPage->leaf ){
return SQLITE_OK;
+ }else{
+ return moveToLeftmost(pCur);
}
- rc = moveToLeftmost(pCur);
- return rc;
}
-
/*
** Step the cursor to the back to the previous entry in the database. If
** successful then set *pRes=0. If the cursor
** was already pointing to the first entry in the database before
** this routine was called, then set *pRes=1.
-*/
-SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+**
+** The main entry point is sqlite3BtreePrevious(). That routine is optimized
+** for the common case of merely decrementing the cell counter BtCursor.aiIdx
+** to the previous cell on the current page. The (slower) btreePrevious()
+** helper routine is called when it is necessary to move to a different page
+** or to restore the cursor.
+**
+** The calling function will set *pRes to 0 or 1. The initial *pRes value
+** will be 1 if the cursor being stepped corresponds to an SQL index and
+** if this routine could have been skipped if that SQL index had been
+** a unique index. Otherwise the caller will have set *pRes to zero.
+** Zero is the common case. The btree implementation is free to use the
+** initial *pRes value as a hint to improve performance, but the current
+** SQLite btree implementation does not. (Note that the comdb2 btree
+** implementation does use this hint, however.)
+*/
+static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage;
assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- pCur->atLast = 0;
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext<0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( pRes!=0 );
+ assert( *pRes==0 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 );
+ assert( pCur->info.nSize==0 );
+ if( pCur->eState!=CURSOR_VALID ){
+ rc = restoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext<0 ){
+ pCur->skipNext = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
assert( pPage->isInit );
if( !pPage->leaf ){
int idx = pCur->aiIdx[pCur->iPage];
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
- if( rc ){
- return rc;
- }
+ if( rc ) return rc;
rc = moveToRightmost(pCur);
}else{
while( pCur->aiIdx[pCur->iPage]==0 ){
@@ -52227,8 +57258,8 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
}
moveToParent(pCur);
}
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ assert( pCur->info.nSize==0 );
+ assert( (pCur->curFlags & (BTCF_ValidNKey|BTCF_ValidOvfl))==0 );
pCur->aiIdx[pCur->iPage]--;
pPage = pCur->apPage[pCur->iPage];
@@ -52238,9 +57269,25 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
rc = SQLITE_OK;
}
}
- *pRes = 0;
return rc;
}
+SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+ assert( cursorHoldsMutex(pCur) );
+ assert( pRes!=0 );
+ assert( *pRes==0 || *pRes==1 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ *pRes = 0;
+ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey);
+ pCur->info.nSize = 0;
+ if( pCur->eState!=CURSOR_VALID
+ || pCur->aiIdx[pCur->iPage]==0
+ || pCur->apPage[pCur->iPage]->leaf==0
+ ){
+ return btreePrevious(pCur, pRes);
+ }
+ pCur->aiIdx[pCur->iPage]--;
+ return SQLITE_OK;
+}
/*
** Allocate a new page from the database file.
@@ -52254,21 +57301,23 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
** an error. *ppPage and *pPgno are undefined in the event of an error.
** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned.
**
-** If the "nearby" parameter is not 0, then a (feeble) effort is made to
+** If the "nearby" parameter is not 0, then an effort is made to
** locate a page close to the page number "nearby". This can be used in an
** attempt to keep related pages close to each other in the database file,
** which in turn can make database access faster.
**
-** If the "exact" parameter is not 0, and the page-number nearby exists
-** anywhere on the free-list, then it is guarenteed to be returned. This
-** is only used by auto-vacuum databases when allocating a new table.
+** If the eMode parameter is BTALLOC_EXACT and the nearby page exists
+** anywhere on the free-list, then it is guaranteed to be returned. If
+** eMode is BTALLOC_LT then the page returned will be less than or equal
+** to nearby if any such page exists. If eMode is BTALLOC_ANY then there
+** are no restrictions on which page is returned.
*/
static int allocateBtreePage(
- BtShared *pBt,
- MemPage **ppPage,
- Pgno *pPgno,
- Pgno nearby,
- u8 exact
+ BtShared *pBt, /* The btree */
+ MemPage **ppPage, /* Store pointer to the allocated page here */
+ Pgno *pPgno, /* Store the page number here */
+ Pgno nearby, /* Search for a page near this one */
+ u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */
){
MemPage *pPage1;
int rc;
@@ -52279,6 +57328,7 @@ static int allocateBtreePage(
Pgno mxPage; /* Total size of the database file */
assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
mxPage = btreePagecount(pBt);
n = get4byte(&pPage1->aData[36]);
@@ -52291,21 +57341,24 @@ static int allocateBtreePage(
Pgno iTrunk;
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
- /* If the 'exact' parameter was true and a query of the pointer-map
+ /* If eMode==BTALLOC_EXACT and a query of the pointer-map
** shows that the page 'nearby' is somewhere on the free-list, then
** the entire-list will be searched for that page.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( exact && nearby<=mxPage ){
- u8 eType;
- assert( nearby>0 );
- assert( pBt->autoVacuum );
- rc = ptrmapGet(pBt, nearby, &eType, 0);
- if( rc ) return rc;
- if( eType==PTRMAP_FREEPAGE ){
- searchList = 1;
+ if( eMode==BTALLOC_EXACT ){
+ if( nearby<=mxPage ){
+ u8 eType;
+ assert( nearby>0 );
+ assert( pBt->autoVacuum );
+ rc = ptrmapGet(pBt, nearby, &eType, 0);
+ if( rc ) return rc;
+ if( eType==PTRMAP_FREEPAGE ){
+ searchList = 1;
+ }
}
- *pPgno = nearby;
+ }else if( eMode==BTALLOC_LE ){
+ searchList = 1;
}
#endif
@@ -52318,7 +57371,8 @@ static int allocateBtreePage(
/* The code within this loop is run only once if the 'searchList' variable
** is not true. Otherwise, it runs once for each trunk-page on the
- ** free-list until the page 'nearby' is located.
+ ** free-list until the page 'nearby' is located (eMode==BTALLOC_EXACT)
+ ** or until a page less than 'nearby' is located (eMode==BTALLOC_LT)
*/
do {
pPrevTrunk = pTrunk;
@@ -52337,6 +57391,8 @@ static int allocateBtreePage(
pTrunk = 0;
goto end_allocate_page;
}
+ assert( pTrunk!=0 );
+ assert( pTrunk->aData!=0 );
k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */
if( k==0 && !searchList ){
@@ -52358,11 +57414,13 @@ static int allocateBtreePage(
rc = SQLITE_CORRUPT_BKPT;
goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
- }else if( searchList && nearby==iTrunk ){
+ }else if( searchList
+ && (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE))
+ ){
/* The list is being searched and this trunk page is the page
** to allocate, regardless of whether it has leaves.
*/
- assert( *pPgno==iTrunk );
+ *pPgno = iTrunk;
*ppPage = pTrunk;
searchList = 0;
rc = sqlite3PagerWrite(pTrunk->pDbPage);
@@ -52425,14 +57483,24 @@ static int allocateBtreePage(
unsigned char *aData = pTrunk->aData;
if( nearby>0 ){
u32 i;
- int dist;
closest = 0;
- dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
- for(i=1; i<k; i++){
- int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
- if( d2<dist ){
- closest = i;
- dist = d2;
+ if( eMode==BTALLOC_LE ){
+ for(i=0; i<k; i++){
+ iPage = get4byte(&aData[8+i*4]);
+ if( iPage<=nearby ){
+ closest = i;
+ break;
+ }
+ }
+ }else{
+ int dist;
+ dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
+ for(i=1; i<k; i++){
+ int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
+ if( d2<dist ){
+ closest = i;
+ dist = d2;
+ }
}
}
}else{
@@ -52446,7 +57514,9 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iPage==mxPage );
- if( !searchList || iPage==nearby ){
+ if( !searchList
+ || (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
+ ){
int noContent;
*pPgno = iPage;
TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
@@ -52458,7 +57528,7 @@ static int allocateBtreePage(
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
- noContent = !btreeGetHasContent(pBt, *pPgno);
+ noContent = !btreeGetHasContent(pBt, *pPgno)? PAGER_GET_NOCONTENT : 0;
rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
@@ -52473,8 +57543,26 @@ static int allocateBtreePage(
pPrevTrunk = 0;
}while( searchList );
}else{
- /* There are no pages on the freelist, so create a new page at the
- ** end of the file */
+ /* There are no pages on the freelist, so append a new page to the
+ ** database image.
+ **
+ ** Normally, new pages allocated by this block can be requested from the
+ ** pager layer with the 'no-content' flag set. This prevents the pager
+ ** from trying to read the pages content from disk. However, if the
+ ** current transaction has already run one or more incremental-vacuum
+ ** steps, then the page we are about to allocate may contain content
+ ** that is required in the event of a rollback. In this case, do
+ ** not set the no-content flag. This causes the pager to load and journal
+ ** the current page content before overwriting it.
+ **
+ ** Note that the pager will not actually attempt to load or journal
+ ** content for any page that really does lie past the end of the database
+ ** file on disk. So the effects of disabling the no-content optimization
+ ** here are confined to those pages that lie between the end of the
+ ** database image and the end of the database file.
+ */
+ int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate))? PAGER_GET_NOCONTENT:0;
+
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc;
pBt->nPage++;
@@ -52489,7 +57577,7 @@ static int allocateBtreePage(
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1);
+ rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
@@ -52503,7 +57591,7 @@ static int allocateBtreePage(
*pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, *pPgno, ppPage, 1);
+ rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
@@ -52520,6 +57608,7 @@ end_allocate_page:
if( rc==SQLITE_OK ){
if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
releasePage(*ppPage);
+ *ppPage = 0;
return SQLITE_CORRUPT_BKPT;
}
(*ppPage)->isInit = 0;
@@ -52567,7 +57656,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
nFree = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], nFree+1);
- if( pBt->secureDelete ){
+ if( pBt->btsFlags & BTS_SECURE_DELETE ){
/* If the secure_delete option is enabled, then
** always fully overwrite deleted information with zeros.
*/
@@ -52628,7 +57717,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
if( rc==SQLITE_OK ){
put4byte(&pTrunk->aData[4], nLeaf+1);
put4byte(&pTrunk->aData[8+nLeaf*4], iPage);
- if( pPage && !pBt->secureDelete ){
+ if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){
sqlite3PagerDontWrite(pPage->pDbPage);
}
rc = btreeSetHasContent(pBt, iPage);
@@ -52671,9 +57760,15 @@ static void freePage(MemPage *pPage, int *pRC){
}
/*
-** Free any overflow pages associated with the given Cell.
+** Free any overflow pages associated with the given Cell. Write the
+** local Cell size (the number of bytes on the original page, omitting
+** overflow) into *pnSize.
*/
-static int clearCell(MemPage *pPage, unsigned char *pCell){
+static int clearCell(
+ MemPage *pPage, /* The page that contains the Cell */
+ unsigned char *pCell, /* First byte of the Cell */
+ u16 *pnSize /* Write the size of the Cell here */
+){
BtShared *pBt = pPage->pBt;
CellInfo info;
Pgno ovflPgno;
@@ -52683,9 +57778,13 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
btreeParseCellPtr(pPage, pCell, &info);
+ *pnSize = info.nSize;
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
+ if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
+ return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
+ }
ovflPgno = get4byte(&pCell[info.iOverflow]);
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
@@ -52763,7 +57862,6 @@ static int fillInCell(
BtShared *pBt = pPage->pBt;
Pgno pgnoOvfl = 0;
int nHeader;
- CellInfo info;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@@ -52773,23 +57871,17 @@ static int fillInCell(
|| sqlite3PagerIswriteable(pPage->pDbPage) );
/* Fill in the header. */
- nHeader = 0;
- if( !pPage->leaf ){
- nHeader += 4;
- }
- if( pPage->hasData ){
- nHeader += putVarint(&pCell[nHeader], nData+nZero);
+ nHeader = pPage->childPtrSize;
+ nPayload = nData + nZero;
+ if( pPage->intKeyLeaf ){
+ nHeader += putVarint32(&pCell[nHeader], nPayload);
}else{
- nData = nZero = 0;
+ assert( nData==0 );
+ assert( nZero==0 );
}
nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey);
- btreeParseCellPtr(pPage, pCell, &info);
- assert( info.nHeader==nHeader );
- assert( info.nKey==nKey );
- assert( info.nData==(u32)(nData+nZero) );
- /* Fill in the payload */
- nPayload = nData + nZero;
+ /* Fill in the payload size */
if( pPage->intKey ){
pSrc = pData;
nSrc = nData;
@@ -52798,15 +57890,55 @@ static int fillInCell(
if( NEVER(nKey>0x7fffffff || pKey==0) ){
return SQLITE_CORRUPT_BKPT;
}
- nPayload += (int)nKey;
+ nPayload = (int)nKey;
pSrc = pKey;
nSrc = (int)nKey;
}
- *pnSize = info.nSize;
- spaceLeft = info.nLocal;
+ if( nPayload<=pPage->maxLocal ){
+ n = nHeader + nPayload;
+ testcase( n==3 );
+ testcase( n==4 );
+ if( n<4 ) n = 4;
+ *pnSize = n;
+ spaceLeft = nPayload;
+ pPrior = pCell;
+ }else{
+ int mn = pPage->minLocal;
+ n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4);
+ testcase( n==pPage->maxLocal );
+ testcase( n==pPage->maxLocal+1 );
+ if( n > pPage->maxLocal ) n = mn;
+ spaceLeft = n;
+ *pnSize = n + nHeader + 4;
+ pPrior = &pCell[nHeader+n];
+ }
pPayload = &pCell[nHeader];
- pPrior = &pCell[info.iOverflow];
+ /* At this point variables should be set as follows:
+ **
+ ** nPayload Total payload size in bytes
+ ** pPayload Begin writing payload here
+ ** spaceLeft Space available at pPayload. If nPayload>spaceLeft,
+ ** that means content must spill into overflow pages.
+ ** *pnSize Size of the local cell (not counting overflow pages)
+ ** pPrior Where to write the pgno of the first overflow page
+ **
+ ** Use a call to btreeParseCellPtr() to verify that the values above
+ ** were computed correctly.
+ */
+#if SQLITE_DEBUG
+ {
+ CellInfo info;
+ btreeParseCellPtr(pPage, pCell, &info);
+ assert( nHeader=(int)(info.pPayload - pCell) );
+ assert( info.nKey==nKey );
+ assert( *pnSize == info.nSize );
+ assert( spaceLeft == info.nLocal );
+ assert( pPrior == &pCell[info.iOverflow] );
+ }
+#endif
+
+ /* Write the payload into the local Cell and any extra into overflow pages */
while( nPayload>0 ){
if( spaceLeft==0 ){
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -52828,7 +57960,7 @@ static int fillInCell(
** If this is the first overflow page, then write a partial entry
** to the pointer-map. If we write nothing to this pointer-map slot,
** then the optimistic overflow chain processing in clearCell()
- ** may misinterpret the uninitialised values and delete the
+ ** may misinterpret the uninitialized values and delete the
** wrong pages from the database.
*/
if( pBt->autoVacuum && rc==SQLITE_OK ){
@@ -52906,7 +58038,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
u32 pc; /* Offset to cell content of cell being deleted */
u8 *data; /* pPage->aData */
u8 *ptr; /* Used to move bytes around within data[] */
- u8 *endPtr; /* End of loop */
int rc; /* The return code */
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
@@ -52917,7 +58048,7 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
data = pPage->aData;
- ptr = &data[pPage->cellOffset + 2*idx];
+ ptr = &pPage->aCellIdx[2*idx];
pc = get2byte(ptr);
hdr = pPage->hdrOffset;
testcase( pc==get2byte(&data[hdr+5]) );
@@ -52931,13 +58062,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
*pRC = rc;
return;
}
- endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
- assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
- while( ptr<endPtr ){
- *(u16*)ptr = *(u16*)&ptr[2];
- ptr += 2;
- }
pPage->nCell--;
+ memmove(ptr, ptr+2, 2*(pPage->nCell - idx));
put2byte(&data[hdr+3], pPage->nCell);
pPage->nFree += 2;
}
@@ -52949,15 +58075,10 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** If the cell content will fit on the page, then put it there. If it
** will not fit, then make a copy of the cell content into pTemp if
** pTemp is not null. Regardless of pTemp, allocate a new entry
-** in pPage->aOvfl[] and make it point to the cell content (either
+** in pPage->apOvfl[] and make it point to the cell content (either
** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
-**
-** If nSkip is non-zero, then do not copy the first nSkip bytes of the
-** cell. The caller will overwrite them after this function returns. If
-** nSkip is non-zero, then pCell may not point to an invalid memory location
-** (but pCell+nSkip is always valid).
*/
static void insertCell(
MemPage *pPage, /* Page into which we are copying */
@@ -52974,16 +58095,14 @@ static void insertCell(
int ins; /* Index in data[] where new cell pointer is inserted */
int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
- u8 *ptr; /* Used for moving information around in data[] */
- u8 *endPtr; /* End of the loop */
-
- int nSkip = (iChild ? 4 : 0);
if( *pRC ) return;
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
- assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
- assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
+ assert( MX_CELL(pPage->pBt)<=10921 );
+ assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
+ assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
+ assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
/* The cell should normally be sized correctly. However, when moving a
** malformed cell from a leaf page to an interior page, if the cell size
@@ -52993,16 +58112,16 @@ static void insertCell(
assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
- memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
+ memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
if( iChild ){
put4byte(pCell, iChild);
}
j = pPage->nOverflow++;
- assert( j<(int)(sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0])) );
- pPage->aOvfl[j].pCell = pCell;
- pPage->aOvfl[j].idx = (u16)i;
+ assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) );
+ pPage->apOvfl[j] = pCell;
+ pPage->aiOvfl[j] = (u16)i;
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
@@ -53022,17 +58141,11 @@ static void insertCell(
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
- memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
+ memcpy(&data[idx], pCell, sz);
if( iChild ){
put4byte(&data[idx], iChild);
}
- ptr = &data[end];
- endPtr = &data[ins];
- assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
- while( ptr>endPtr ){
- *(u16*)ptr = *(u16*)&ptr[-2];
- ptr -= 2;
- }
+ memmove(&data[ins+2], &data[ins], end-ins);
put2byte(&data[ins], idx);
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -53051,7 +58164,7 @@ static void insertCell(
** The cells are guaranteed to fit on the page.
*/
static void assemblePage(
- MemPage *pPage, /* The page to be assemblied */
+ MemPage *pPage, /* The page to be assembled */
int nCell, /* The number of cells to add to this page */
u8 **apCell, /* Pointers to cell bodies */
u16 *aSize /* Sizes of the cells */
@@ -53073,7 +58186,7 @@ static void assemblePage(
assert( pPage->nCell==0 );
assert( get2byteNotZero(&data[hdr+5])==nUsable );
- pCellptr = &data[pPage->cellOffset + nCell*2];
+ pCellptr = &pPage->aCellIdx[nCell*2];
cellbody = nUsable;
for(i=nCell-1; i>=0; i--){
u16 sz = aSize[i];
@@ -53139,7 +58252,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
assert( pPage->nOverflow==1 );
/* This error condition is now caught prior to reaching this function */
- if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT;
+ if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT;
/* Allocate a new page. This page will become the right-sibling of
** pPage. Make the parent page writable, so that the new divider cell
@@ -53150,7 +58263,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
if( rc==SQLITE_OK ){
u8 *pOut = &pSpace[4];
- u8 *pCell = pPage->aOvfl[0].pCell;
+ u8 *pCell = pPage->apOvfl[0];
u16 szCell = cellSizePtr(pPage, pCell);
u8 *pStop;
@@ -53260,7 +58373,7 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
** map entries are also updated so that the parent page is page pTo.
**
** If pFrom is currently carrying any overflow cells (entries in the
-** MemPage.aOvfl[] array), they are not copied to pTo.
+** MemPage.apOvfl[] array), they are not copied to pTo.
**
** Before returning, page pTo is reinitialized using btreeInitPage().
**
@@ -53349,11 +58462,15 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){
** If aOvflSpace is set to a null pointer, this function returns
** SQLITE_NOMEM.
*/
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", off)
+#endif
static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
- int isRoot /* True if pParent is a root-page */
+ int isRoot, /* True if pParent is a root-page */
+ int bBulk /* True if this call is part of a bulk load */
){
BtShared *pBt; /* The whole database */
int nCell = 0; /* Number of cells in apCell[] */
@@ -53397,7 +58514,7 @@ static int balance_nonroot(
** is called (indirectly) from sqlite3BtreeDelete().
*/
assert( pParent->nOverflow==0 || pParent->nOverflow==1 );
- assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx );
+ assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );
if( !aOvflSpace ){
return SQLITE_NOMEM;
@@ -53417,18 +58534,19 @@ static int balance_nonroot(
i = pParent->nOverflow + pParent->nCell;
if( i<2 ){
nxDiv = 0;
- nOld = i+1;
}else{
- nOld = 3;
+ assert( bBulk==0 || bBulk==1 );
if( iParentIdx==0 ){
nxDiv = 0;
}else if( iParentIdx==i ){
- nxDiv = i-2;
+ nxDiv = i-2+bBulk;
}else{
+ assert( bBulk==0 );
nxDiv = iParentIdx-1;
}
- i = 2;
+ i = 2-bBulk;
}
+ nOld = i+1;
if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){
pRight = &pParent->aData[pParent->hdrOffset+8];
}else{
@@ -53436,7 +58554,7 @@ static int balance_nonroot(
}
pgno = get4byte(pRight);
while( 1 ){
- rc = getAndInitPage(pBt, pgno, &apOld[i]);
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0);
if( rc ){
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
@@ -53444,8 +58562,8 @@ static int balance_nonroot(
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
if( (i--)==0 ) break;
- if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){
- apDiv[i] = pParent->aOvfl[0].pCell;
+ if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){
+ apDiv[i] = pParent->apOvfl[0];
pgno = get4byte(apDiv[i]);
szNew[i] = cellSizePtr(pParent, apDiv[i]);
pParent->nOverflow = 0;
@@ -53461,13 +58579,15 @@ static int balance_nonroot(
** four bytes of the divider cell. So the pointer is safe to use
** later on.
**
- ** Unless SQLite is compiled in secure-delete mode. In this case,
+ ** But not if we are in secure-delete mode. In secure-delete mode,
** the dropCell() routine will overwrite the entire cell with zeroes.
** In this case, temporarily copy the cell into the aOvflSpace[]
** buffer. It will be copied out again as soon as the aSpace[] buffer
** is allocated. */
- if( pBt->secureDelete ){
- int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
+ if( pBt->btsFlags & BTS_SECURE_DELETE ){
+ int iOff;
+
+ iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
if( (iOff+szNew[i])>(int)pBt->usableSize ){
rc = SQLITE_CORRUPT_BKPT;
memset(apOld, 0, (i+1)*sizeof(MemPage*));
@@ -53506,7 +58626,7 @@ static int balance_nonroot(
/*
** Load pointers to all cells on sibling pages and the divider cells
** into the local apCell[] array. Make copies of the divider cells
- ** into space obtained from aSpace1[] and remove the the divider Cells
+ ** into space obtained from aSpace1[] and remove the divider cells
** from pParent.
**
** If the siblings are on leaf pages, then the child pointers of the
@@ -53520,7 +58640,7 @@ static int balance_nonroot(
** leafData: 1 if pPage holds key+data and pParent holds only keys.
*/
leafCorrection = apOld[0]->leaf*4;
- leafData = apOld[0]->hasData;
+ leafData = apOld[0]->intKeyLeaf;
for(i=0; i<nOld; i++){
int limit;
@@ -53635,7 +58755,9 @@ static int balance_nonroot(
d = r + 1 - leafData;
assert( d<nMaxCells );
assert( r<nMaxCells );
- while( szRight==0 || szRight+szCell[d]+2<=szLeft-(szCell[r]+2) ){
+ while( szRight==0
+ || (!bBulk && szRight+szCell[d]+2<=szLeft-(szCell[r]+2))
+ ){
szRight += szCell[d] + 2;
szLeft -= szCell[r] + 2;
cntNew[i-1]--;
@@ -53649,8 +58771,14 @@ static int balance_nonroot(
/* Either we found one or more cells (cntnew[0])>0) or pPage is
** a virtual root page. A virtual root page is when the real root
** page is page 1 and we are the only child of that page.
+ **
+ ** UPDATE: The assert() below is not necessarily true if the database
+ ** file is corrupt. The corruption will be detected and reported later
+ ** in this procedure so there is no need to act upon it now.
*/
+#if 0
assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) );
+#endif
TRACE(("BALANCE: old: %d %d %d ",
apOld[0]->pgno,
@@ -53676,7 +58804,7 @@ static int balance_nonroot(
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
- rc = allocateBtreePage(pBt, &pNew, &pgno, pgno, 0);
+ rc = allocateBtreePage(pBt, &pNew, &pgno, (bBulk ? 1 : pgno), 0);
if( rc ) goto balance_cleanup;
apNew[i] = pNew;
nNew++;
@@ -53702,7 +58830,7 @@ static int balance_nonroot(
}
/*
- ** Put the new pages in accending order. This helps to
+ ** Put the new pages in ascending order. This helps to
** keep entries in the disk file in order so that a scan
** of the table is a linear scan through the file. That
** in turn helps the operating system to deliver pages
@@ -53878,7 +59006,7 @@ static int balance_nonroot(
MemPage *pOld = apCopy[0];
int nOverflow = pOld->nOverflow;
int iNextOld = pOld->nCell + nOverflow;
- int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1);
+ int iOverflow = (nOverflow ? pOld->aiOvfl[0] : -1);
j = 0; /* Current 'old' sibling page */
k = 0; /* Current 'new' sibling page */
for(i=0; i<nCell; i++){
@@ -53887,18 +59015,20 @@ static int balance_nonroot(
/* Cell i is the cell immediately following the last cell on old
** sibling page j. If the siblings are not leaf pages of an
** intkey b-tree, then cell i was a divider cell. */
+ assert( j+1 < ArraySize(apCopy) );
+ assert( j+1 < nOld );
pOld = apCopy[++j];
iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;
if( pOld->nOverflow ){
nOverflow = pOld->nOverflow;
- iOverflow = i + !leafData + pOld->aOvfl[0].idx;
+ iOverflow = i + !leafData + pOld->aiOvfl[0];
}
isDivider = !leafData;
}
assert(nOverflow>0 || iOverflow<i );
- assert(nOverflow<2 || pOld->aOvfl[0].idx==pOld->aOvfl[1].idx-1);
- assert(nOverflow<3 || pOld->aOvfl[1].idx==pOld->aOvfl[2].idx-1);
+ assert(nOverflow<2 || pOld->aiOvfl[0]==pOld->aiOvfl[1]-1);
+ assert(nOverflow<3 || pOld->aiOvfl[1]==pOld->aiOvfl[2]-1);
if( i==iOverflow ){
isDivider = 1;
if( (--nOverflow)>0 ){
@@ -53965,6 +59095,9 @@ balance_cleanup:
return rc;
}
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
+#pragma optimize("", on)
+#endif
/*
@@ -54019,7 +59152,10 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno));
/* Copy the overflow cells from pRoot to pChild */
- memcpy(pChild->aOvfl, pRoot->aOvfl, pRoot->nOverflow*sizeof(pRoot->aOvfl[0]));
+ memcpy(pChild->aiOvfl, pRoot->aiOvfl,
+ pRoot->nOverflow*sizeof(pRoot->aiOvfl[0]));
+ memcpy(pChild->apOvfl, pRoot->apOvfl,
+ pRoot->nOverflow*sizeof(pRoot->apOvfl[0]));
pChild->nOverflow = pRoot->nOverflow;
/* Zero the contents of pRoot. Then install pChild as the right-child. */
@@ -54080,16 +59216,16 @@ static int balance(BtCursor *pCur){
rc = sqlite3PagerWrite(pParent->pDbPage);
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_QUICKBALANCE
- if( pPage->hasData
+ if( pPage->intKeyLeaf
&& pPage->nOverflow==1
- && pPage->aOvfl[0].idx==pPage->nCell
+ && pPage->aiOvfl[0]==pPage->nCell
&& pParent->pgno!=1
&& pParent->nCell==iIdx
){
/* Call balance_quick() to create a new sibling of pPage on which
** to store the overflow cell. balance_quick() inserts a new cell
** into pParent, which may cause pParent overflow. If this
- ** happens, the next interation of the do-loop will balance pParent
+ ** happens, the next iteration of the do-loop will balance pParent
** use either balance_nonroot() or balance_deeper(). Until this
** happens, the overflow cell is stored in the aBalanceQuickSpace[]
** buffer.
@@ -54122,7 +59258,7 @@ static int balance(BtCursor *pCur){
** pSpace buffer passed to the latter call to balance_nonroot().
*/
u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize);
- rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1);
+ rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1, pCur->hints);
if( pFree ){
/* If pFree is not NULL, it points to the pSpace buffer used
** by a previous call to balance_nonroot(). Its contents are
@@ -54166,7 +59302,7 @@ static int balance(BtCursor *pCur){
** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already
** been performed. seekResult is the search result returned (a negative
** number if pCur points at an entry that is smaller than (pKey, nKey), or
-** a positive value if pCur points at an etry that is larger than
+** a positive value if pCur points at an entry that is larger than
** (pKey, nKey)).
**
** If the seekResult parameter is non-zero, then the caller guarantees that
@@ -54199,7 +59335,9 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
}
assert( cursorHoldsMutex(pCur) );
- assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly );
+ assert( (pCur->curFlags & BTCF_WriteFlag)!=0
+ && pBt->inTransaction==TRANS_WRITE
+ && (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
/* Assert that the caller has been consistent. If this cursor was opened
@@ -54209,13 +59347,6 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** blob of associated data. */
assert( (pKey==0)==(pCur->pKeyInfo==0) );
- /* If this is an insert into a table b-tree, invalidate any incrblob
- ** cursors open on the row being replaced (assuming this is a replace
- ** operation - if it is not, the following is a no-op). */
- if( pCur->pKeyInfo==0 ){
- invalidateIncrblobCursors(p, nKey, 0);
- }
-
/* Save the positions of any other cursors open on this table.
**
** In some cases, the call to btreeMoveto() below is a no-op. For
@@ -54229,6 +59360,21 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
*/
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc;
+
+ if( pCur->pKeyInfo==0 ){
+ /* If this is an insert into a table b-tree, invalidate any incrblob
+ ** cursors open on the row being replaced */
+ invalidateIncrblobCursors(p, nKey, 0);
+
+ /* If the cursor is currently on the last row and we are appending a
+ ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto()
+ ** call */
+ if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0
+ && pCur->info.nKey==nKey-1 ){
+ loc = -1;
+ }
+ }
+
if( !loc ){
rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
if( rc ) return rc;
@@ -54243,9 +59389,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
pCur->pgnoRoot, nKey, nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
- allocateTempSpace(pBt);
newCell = pBt->pTmpSpace;
- if( newCell==0 ) return SQLITE_NOMEM;
+ assert( newCell!=0 );
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) );
@@ -54262,8 +59407,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( !pPage->leaf ){
memcpy(newCell, oldCell, 4);
}
- szOld = cellSizePtr(pPage, oldCell);
- rc = clearCell(pPage, oldCell);
+ rc = clearCell(pPage, oldCell, &szOld);
dropCell(pPage, idx, szOld, &rc);
if( rc ) goto end_insert;
}else if( loc<0 && pPage->nCell>0 ){
@@ -54275,9 +59419,9 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
- /* If no error has occured and pPage has an overflow cell, call balance()
+ /* If no error has occurred and pPage has an overflow cell, call balance()
** to redistribute the cells within the tree. Since balance() may move
- ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey
+ ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey
** variables.
**
** Previous versions of SQLite called moveToRoot() to move the cursor
@@ -54296,8 +59440,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** row without seeking the cursor. This can be a big performance boost.
*/
pCur->info.nSize = 0;
- pCur->validNKey = 0;
if( rc==SQLITE_OK && pPage->nOverflow ){
+ pCur->curFlags &= ~(BTCF_ValidNKey);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
@@ -54315,7 +59459,7 @@ end_insert:
/*
** Delete the entry that the cursor is pointing to. The cursor
-** is left pointing at a arbitrary location.
+** is left pointing at an arbitrary location.
*/
SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
Btree *p = pCur->pBtree;
@@ -54325,11 +59469,12 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pCell; /* Pointer to cell to delete */
int iCellIdx; /* Index of cell to delete */
int iCellDepth; /* Depth of node containing pCell */
+ u16 szCell; /* Size of the cell being deleted */
assert( cursorHoldsMutex(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
- assert( !pBt->readOnly );
- assert( pCur->wrFlag );
+ assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
+ assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
@@ -54339,12 +59484,6 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
return SQLITE_ERROR; /* Something has gone awry. */
}
- /* If this is a delete operation to remove a row from a table b-tree,
- ** invalidate any incrblob cursors open on the row being deleted. */
- if( pCur->pKeyInfo==0 ){
- invalidateIncrblobCursors(p, pCur->info.nKey, 0);
- }
-
iCellDepth = pCur->iPage;
iCellIdx = pCur->aiIdx[iCellDepth];
pPage = pCur->apPage[iCellDepth];
@@ -54358,7 +59497,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
** sub-tree headed by the child page of the cell being deleted. This makes
** balancing the tree following the delete operation easier. */
if( !pPage->leaf ){
- int notUsed;
+ int notUsed = 0;
rc = sqlite3BtreePrevious(pCur, &notUsed);
if( rc ) return rc;
}
@@ -54370,10 +59509,17 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
*/
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc;
+
+ /* If this is a delete operation to remove a row from a table b-tree,
+ ** invalidate any incrblob cursors open on the row being deleted. */
+ if( pCur->pKeyInfo==0 ){
+ invalidateIncrblobCursors(p, pCur->info.nKey, 0);
+ }
+
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
- rc = clearCell(pPage, pCell);
- dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc);
+ rc = clearCell(pPage, pCell, &szCell);
+ dropCell(pPage, iCellIdx, szCell, &rc);
if( rc ) return rc;
/* If the cell deleted was not located on a leaf page, then the cursor
@@ -54390,10 +59536,8 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
pCell = findCell(pLeaf, pLeaf->nCell-1);
nCell = cellSizePtr(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
-
- allocateTempSpace(pBt);
pTmp = pBt->pTmpSpace;
-
+ assert( pTmp!=0 );
rc = sqlite3PagerWrite(pLeaf->pDbPage);
insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
@@ -54449,7 +59593,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
assert( sqlite3BtreeHoldsMutex(p) );
assert( pBt->inTransaction==TRANS_WRITE );
- assert( !pBt->readOnly );
+ assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
#ifdef SQLITE_OMIT_AUTOVACUUM
rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
@@ -54488,7 +59632,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
** be moved to the allocated page (unless the allocated page happens
** to reside at pgnoRoot).
*/
- rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1);
+ rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -54503,7 +59647,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
u8 eType = 0;
Pgno iPtrPage = 0;
+ /* Save the positions of any open cursors. This is required in
+ ** case they are holding a reference to an xFetch reference
+ ** corresponding to page pgnoRoot. */
+ rc = saveAllCursors(pBt, 0, 0);
releasePage(pPageMove);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
/* Move the page currently at pgnoRoot to pgnoMove. */
rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0);
@@ -54597,25 +59748,28 @@ static int clearDatabasePage(
int rc;
unsigned char *pCell;
int i;
+ int hdr;
+ u16 szCell;
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, pgno, &pPage);
+ rc = getAndInitPage(pBt, pgno, &pPage, 0);
if( rc ) return rc;
+ hdr = pPage->hdrOffset;
for(i=0; i<pPage->nCell; i++){
pCell = findCell(pPage, i);
if( !pPage->leaf ){
rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}
- rc = clearCell(pPage, pCell);
+ rc = clearCell(pPage, pCell, &szCell);
if( rc ) goto cleardatabasepage_out;
}
if( !pPage->leaf ){
- rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange);
+ rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}else if( pnChange ){
assert( pPage->intKey );
@@ -54624,7 +59778,7 @@ static int clearDatabasePage(
if( freePageFlag ){
freePage(pPage, &rc);
}else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
- zeroPage(pPage, pPage->aData[0] | PTF_LEAF);
+ zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF);
}
cleardatabasepage_out:
@@ -54651,13 +59805,13 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
sqlite3BtreeEnter(p);
assert( p->inTrans==TRANS_WRITE );
- /* Invalidate all incrblob cursors open on table iTable (assuming iTable
- ** is the root of a table b-tree - if it is not, the following call is
- ** a no-op). */
- invalidateIncrblobCursors(p, 0, 1);
-
rc = saveAllCursors(pBt, (Pgno)iTable, 0);
+
if( SQLITE_OK==rc ){
+ /* Invalidate all incrblob cursors open on table iTable (assuming iTable
+ ** is the root of a table b-tree - if it is not, the following call is
+ ** a no-op). */
+ invalidateIncrblobCursors(p, 0, 1);
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
}
sqlite3BtreeLeave(p);
@@ -54665,6 +59819,15 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
}
/*
+** Delete all information from the single table that pCur is open on.
+**
+** This routine only work for pCur on an ephemeral table.
+*/
+SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor *pCur){
+ return sqlite3BtreeClearTable(pCur->pBtree, pCur->pgnoRoot, 0);
+}
+
+/*
** Erase all information in a table and add the root of the table to
** the freelist. Except, the root of the principle table (the one on
** page 1) is never added to the freelist.
@@ -54823,7 +59986,9 @@ SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
/* If auto-vacuum is disabled in this build and this is an auto-vacuum
** database, mark the database as read-only. */
#ifdef SQLITE_OMIT_AUTOVACUUM
- if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1;
+ if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){
+ pBt->btsFlags |= BTS_READ_ONLY;
+ }
#endif
sqlite3BtreeLeave(p);
@@ -54869,6 +60034,11 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
i64 nEntry = 0; /* Value to return in *pnEntry */
int rc; /* Return code */
+
+ if( pCur->pgnoRoot==0 ){
+ *pnEntry = 0;
+ return SQLITE_OK;
+ }
rc = moveToRoot(pCur);
/* Unless an error occurs, the following loop runs one iteration for each
@@ -54941,11 +60111,11 @@ SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){
*/
static void checkAppendMsg(
IntegrityCk *pCheck,
- char *zMsg1,
const char *zFormat,
...
){
va_list ap;
+ char zBuf[200];
if( !pCheck->mxErr ) return;
pCheck->mxErr--;
pCheck->nErr++;
@@ -54953,37 +60123,58 @@ static void checkAppendMsg(
if( pCheck->errMsg.nChar ){
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
}
- if( zMsg1 ){
- sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1);
+ if( pCheck->zPfx ){
+ sqlite3_snprintf(sizeof(zBuf), zBuf, pCheck->zPfx, pCheck->v1, pCheck->v2);
+ sqlite3StrAccumAppendAll(&pCheck->errMsg, zBuf);
}
sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
va_end(ap);
- if( pCheck->errMsg.mallocFailed ){
+ if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
pCheck->mallocFailed = 1;
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
+
+/*
+** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that
+** corresponds to page iPg is already set.
+*/
+static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
+ assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
+}
+
+/*
+** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
+*/
+static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
+ assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
+}
+
+
/*
** Add 1 to the reference count for page iPage. If this is the second
** reference to the page, add an error message to pCheck->zErrMsg.
-** Return 1 if there are 2 ore more references to the page and 0 if
+** Return 1 if there are 2 or more references to the page and 0 if
** if this is the first reference to the page.
**
** Also check that the page number is in bounds.
*/
-static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){
+static int checkRef(IntegrityCk *pCheck, Pgno iPage){
if( iPage==0 ) return 1;
if( iPage>pCheck->nPage ){
- checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);
+ checkAppendMsg(pCheck, "invalid page number %d", iPage);
return 1;
}
- if( pCheck->anRef[iPage]==1 ){
- checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
+ if( getPageReferenced(pCheck, iPage) ){
+ checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
return 1;
}
- return (pCheck->anRef[iPage]++)>1;
+ setPageReferenced(pCheck, iPage);
+ return 0;
}
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -54996,8 +60187,7 @@ static void checkPtrmap(
IntegrityCk *pCheck, /* Integrity check context */
Pgno iChild, /* Child page number */
u8 eType, /* Expected pointer map type */
- Pgno iParent, /* Expected pointer map parent page number */
- char *zContext /* Context description (used for error msg) */
+ Pgno iParent /* Expected pointer map parent page number */
){
int rc;
u8 ePtrmapType;
@@ -55006,12 +60196,12 @@ static void checkPtrmap(
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
- checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild);
+ checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
return;
}
if( ePtrmapType!=eType || iPtrmapParent!=iParent ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)",
iChild, eType, iParent, ePtrmapType, iPtrmapParent);
}
@@ -55026,8 +60216,7 @@ static void checkList(
IntegrityCk *pCheck, /* Integrity checking context */
int isFreeList, /* True for a freelist. False for overflow page list */
int iPage, /* Page number for first page in the list */
- int N, /* Expected number of pages in the list */
- char *zContext /* Context for error messages */
+ int N /* Expected number of pages in the list */
){
int i;
int expected = N;
@@ -55036,14 +60225,14 @@ static void checkList(
DbPage *pOvflPage;
unsigned char *pOvflData;
if( iPage<1 ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"%d of %d pages missing from overflow list starting at %d",
N+1, expected, iFirst);
break;
}
- if( checkRef(pCheck, iPage, zContext) ) break;
+ if( checkRef(pCheck, iPage) ) break;
if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){
- checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage);
+ checkAppendMsg(pCheck, "failed to get page %d", iPage);
break;
}
pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage);
@@ -55051,11 +60240,11 @@ static void checkList(
int n = get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
- checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
+ checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0);
}
#endif
if( n>(int)pCheck->pBt->usableSize/4-2 ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"freelist leaf count too big on page %d", iPage);
N--;
}else{
@@ -55063,10 +60252,10 @@ static void checkList(
Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
- checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext);
+ checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0);
}
#endif
- checkRef(pCheck, iFreePage, zContext);
+ checkRef(pCheck, iFreePage);
}
N -= n;
}
@@ -55079,7 +60268,7 @@ static void checkList(
*/
if( pCheck->pBt->autoVacuum && N>0 ){
i = get4byte(pOvflData);
- checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext);
+ checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage);
}
}
#endif
@@ -55111,7 +60300,6 @@ static void checkList(
static int checkTreePage(
IntegrityCk *pCheck, /* Context for the sanity check */
int iPage, /* Page number of the page to check */
- char *zParentContext, /* Parent context */
i64 *pnParentMinKey,
i64 *pnParentMaxKey
){
@@ -55122,23 +60310,26 @@ static int checkTreePage(
u8 *data;
BtShared *pBt;
int usableSize;
- char zContext[100];
char *hit = 0;
i64 nMinKey = 0;
i64 nMaxKey = 0;
-
- sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage);
+ const char *saved_zPfx = pCheck->zPfx;
+ int saved_v1 = pCheck->v1;
+ int saved_v2 = pCheck->v2;
/* Check that the page exists
*/
pBt = pCheck->pBt;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
- if( checkRef(pCheck, iPage, zParentContext) ) return 0;
+ if( checkRef(pCheck, iPage) ) return 0;
+ pCheck->zPfx = "Page %d: ";
+ pCheck->v1 = iPage;
if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
- return 0;
+ depth = -1;
+ goto end_of_check;
}
/* Clear MemPage.isInit to make sure the corruption detection code in
@@ -55146,10 +60337,11 @@ static int checkTreePage(
pPage->isInit = 0;
if( (rc = btreeInitPage(pPage))!=0 ){
assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"btreeInitPage() returns error code %d", rc);
releasePage(pPage);
- return 0;
+ depth = -1;
+ goto end_of_check;
}
/* Check out all the cells.
@@ -55162,23 +60354,23 @@ static int checkTreePage(
/* Check payload overflow pages
*/
- sqlite3_snprintf(sizeof(zContext), zContext,
- "On tree page %d cell %d: ", iPage, i);
+ pCheck->zPfx = "On tree page %d cell %d: ";
+ pCheck->v1 = iPage;
+ pCheck->v2 = i;
pCell = findCell(pPage,i);
btreeParseCellPtr(pPage, pCell, &info);
- sz = info.nData;
- if( !pPage->intKey ) sz += (int)info.nKey;
+ sz = info.nPayload;
/* For intKey pages, check that the keys are in order.
*/
- else if( i==0 ) nMinKey = nMaxKey = info.nKey;
- else{
- if( info.nKey <= nMaxKey ){
- checkAppendMsg(pCheck, zContext,
- "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
+ if( pPage->intKey ){
+ if( i==0 ){
+ nMinKey = nMaxKey = info.nKey;
+ }else if( info.nKey <= nMaxKey ){
+ checkAppendMsg(pCheck,
+ "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
}
nMaxKey = info.nKey;
}
- assert( sz==info.nPayload );
if( (sz>info.nLocal)
&& (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
){
@@ -55186,10 +60378,10 @@ static int checkTreePage(
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext);
+ checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
}
#endif
- checkList(pCheck, 0, pgnoOvfl, nPage, zContext);
+ checkList(pCheck, 0, pgnoOvfl, nPage);
}
/* Check sanity of left child page.
@@ -55198,12 +60390,12 @@ static int checkTreePage(
pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
+ checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
- d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey);
+ d2 = checkTreePage(pCheck, pgno, &nMinKey, i==0?NULL:&nMaxKey);
if( i>0 && d2!=depth ){
- checkAppendMsg(pCheck, zContext, "Child page depth differs");
+ checkAppendMsg(pCheck, "Child page depth differs");
}
depth = d2;
}
@@ -55211,37 +60403,39 @@ static int checkTreePage(
if( !pPage->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- sqlite3_snprintf(sizeof(zContext), zContext,
- "On page %d at right child: ", iPage);
+ pCheck->zPfx = "On page %d at right child: ";
+ pCheck->v1 = iPage;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
+ checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
- checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey);
+ checkTreePage(pCheck, pgno, NULL, !pPage->nCell?NULL:&nMaxKey);
}
/* For intKey leaf pages, check that the min/max keys are in order
** with any left/parent/right pages.
*/
+ pCheck->zPfx = "Page %d: ";
+ pCheck->v1 = iPage;
if( pPage->leaf && pPage->intKey ){
/* if we are a left child page */
if( pnParentMinKey ){
/* if we are the left most child page */
if( !pnParentMaxKey ){
if( nMaxKey > *pnParentMinKey ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"Rowid %lld out of order (max larger than parent min of %lld)",
nMaxKey, *pnParentMinKey);
}
}else{
if( nMinKey <= *pnParentMinKey ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"Rowid %lld out of order (min less than parent min of %lld)",
nMinKey, *pnParentMinKey);
}
if( nMaxKey > *pnParentMaxKey ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"Rowid %lld out of order (max larger than parent max of %lld)",
nMaxKey, *pnParentMaxKey);
}
@@ -55250,7 +60444,7 @@ static int checkTreePage(
/* else if we're a right child page */
} else if( pnParentMaxKey ){
if( nMinKey <= *pnParentMaxKey ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"Rowid %lld out of order (min less than parent max of %lld)",
nMinKey, *pnParentMaxKey);
}
@@ -55262,6 +60456,7 @@ static int checkTreePage(
data = pPage->aData;
hdr = pPage->hdrOffset;
hit = sqlite3PageMalloc( pBt->pageSize );
+ pCheck->zPfx = 0;
if( hit==0 ){
pCheck->mallocFailed = 1;
}else{
@@ -55279,7 +60474,8 @@ static int checkTreePage(
size = cellSizePtr(pPage, &data[pc]);
}
if( (int)(pc+size-1)>=usableSize ){
- checkAppendMsg(pCheck, 0,
+ pCheck->zPfx = 0;
+ checkAppendMsg(pCheck,
"Corruption detected in cell %d on page %d",i,iPage);
}else{
for(j=pc+size-1; j>=pc; j--) hit[j]++;
@@ -55301,19 +60497,24 @@ static int checkTreePage(
if( hit[i]==0 ){
cnt++;
}else if( hit[i]>1 ){
- checkAppendMsg(pCheck, 0,
+ checkAppendMsg(pCheck,
"Multiple uses for byte %d of page %d", i, iPage);
break;
}
}
if( cnt!=data[hdr+7] ){
- checkAppendMsg(pCheck, 0,
+ checkAppendMsg(pCheck,
"Fragmentation of %d bytes reported as %d on page %d",
cnt, data[hdr+7], iPage);
}
}
sqlite3PageFree(hit);
releasePage(pPage);
+
+end_of_check:
+ pCheck->zPfx = saved_zPfx;
+ pCheck->v1 = saved_v1;
+ pCheck->v2 = saved_v2;
return depth+1;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -55354,29 +60555,32 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
sCheck.mxErr = mxErr;
sCheck.nErr = 0;
sCheck.mallocFailed = 0;
+ sCheck.zPfx = 0;
+ sCheck.v1 = 0;
+ sCheck.v2 = 0;
*pnErr = 0;
if( sCheck.nPage==0 ){
sqlite3BtreeLeave(p);
return 0;
}
- sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
- if( !sCheck.anRef ){
+
+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
+ if( !sCheck.aPgRef ){
*pnErr = 1;
sqlite3BtreeLeave(p);
return 0;
}
- for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
i = PENDING_BYTE_PAGE(pBt);
- if( i<=sCheck.nPage ){
- sCheck.anRef[i] = 1;
- }
- sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
+ if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
+ sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
sCheck.errMsg.useMalloc = 2;
/* Check the integrity of the freelist
*/
+ sCheck.zPfx = "Main freelist: ";
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
- get4byte(&pBt->pPage1->aData[36]), "Main freelist: ");
+ get4byte(&pBt->pPage1->aData[36]));
+ sCheck.zPfx = 0;
/* Check all the tables.
*/
@@ -55384,30 +60588,32 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
- checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0);
+ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
}
#endif
- checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL);
+ sCheck.zPfx = "List of tree roots: ";
+ checkTreePage(&sCheck, aRoot[i], NULL, NULL);
+ sCheck.zPfx = 0;
}
/* Make sure every page in the file is referenced
*/
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
- if( sCheck.anRef[i]==0 ){
- checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
+ if( getPageReferenced(&sCheck, i)==0 ){
+ checkAppendMsg(&sCheck, "Page %d is never used", i);
}
#else
/* If the database supports auto-vacuum, make sure no tables contain
** references to pointer-map pages.
*/
- if( sCheck.anRef[i]==0 &&
+ if( getPageReferenced(&sCheck, i)==0 &&
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
- checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
+ checkAppendMsg(&sCheck, "Page %d is never used", i);
}
- if( sCheck.anRef[i]!=0 &&
+ if( getPageReferenced(&sCheck, i)!=0 &&
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
- checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
+ checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
}
#endif
}
@@ -55417,7 +60623,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
** of the integrity check.
*/
if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){
- checkAppendMsg(&sCheck, 0,
+ checkAppendMsg(&sCheck,
"Outstanding page count goes from %d to %d during this analysis",
nRef, sqlite3PagerRefcount(pBt->pPager)
);
@@ -55426,7 +60632,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
/* Clean up and report errors.
*/
sqlite3BtreeLeave(p);
- sqlite3_free(sCheck.anRef);
+ sqlite3_free(sCheck.aPgRef);
if( sCheck.mallocFailed ){
sqlite3StrAccumReset(&sCheck.errMsg);
*pnErr = sCheck.nErr+1;
@@ -55439,14 +60645,15 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/*
-** Return the full pathname of the underlying database file.
+** Return the full pathname of the underlying database file. Return
+** an empty string if the database is in-memory or a TEMP database.
**
** The pager filename is invariant as long as the pager is
** open so it is safe to access without the BtShared mutex.
*/
SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *p){
assert( p->pBt->pPager!=0 );
- return sqlite3PagerFilename(p->pBt->pPager);
+ return sqlite3PagerFilename(p->pBt->pPager, 1);
}
/*
@@ -55597,7 +60804,7 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
int rc;
assert( cursorHoldsMutex(pCsr) );
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
- assert( pCsr->isIncrblobHandle );
+ assert( pCsr->curFlags & BTCF_Incrblob );
rc = restoreCursorPosition(pCsr);
if( rc!=SQLITE_OK ){
@@ -55608,6 +60815,17 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
return SQLITE_ABORT;
}
+ /* Save the positions of all other cursors open on this table. This is
+ ** required in case any of them are holding references to an xFetch
+ ** version of the b-tree page modified by the accessPayload call below.
+ **
+ ** Note that pCsr must be open on a INTKEY table and saveCursorPosition()
+ ** and hence saveAllCursors() cannot fail on a BTREE_INTKEY table, hence
+ ** saveAllCursors can only return SQLITE_OK.
+ */
+ VVA_ONLY(rc =) saveAllCursors(pCsr->pBt, pCsr->pgnoRoot, pCsr);
+ assert( rc==SQLITE_OK );
+
/* Check some assumptions:
** (a) the cursor is open for writing,
** (b) there is a read/write transaction open,
@@ -55615,10 +60833,11 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
** (d) there are no conflicting read-locks, and
** (e) the cursor points at a valid row of an intKey table.
*/
- if( !pCsr->wrFlag ){
+ if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){
return SQLITE_READONLY;
}
- assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE );
+ assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0
+ && pCsr->pBt->inTransaction==TRANS_WRITE );
assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );
assert( pCsr->apPage[pCsr->iPage]->intKey );
@@ -55627,20 +60846,10 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
}
/*
-** Set a flag on this cursor to cache the locations of pages from the
-** overflow list for the current row. This is used by cursors opened
-** for incremental blob IO only.
-**
-** This function sets a flag only. The actual page location cache
-** (stored in BtCursor.aOverflow[]) is allocated and used by function
-** accessPayload() (the worker function for sqlite3BtreeData() and
-** sqlite3BtreePutData()).
+** Mark this cursor as an incremental blob cursor.
*/
-SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- invalidateOverflowCache(pCur);
- pCur->isIncrblobHandle = 1;
+SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
+ pCur->curFlags |= BTCF_Incrblob;
}
#endif
@@ -55653,13 +60862,13 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
BtShared *pBt = pBtree->pBt;
int rc; /* Return code */
- assert( pBtree->inTrans==TRANS_NONE );
assert( iVersion==1 || iVersion==2 );
/* If setting the version fields to 1, do not automatically open the
** WAL connection, even if the version fields are currently set to 2.
*/
- pBt->doNotUseWAL = (u8)(iVersion==1);
+ pBt->btsFlags &= ~BTS_NO_WAL;
+ if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;
rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
@@ -55676,10 +60885,26 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
}
}
- pBt->doNotUseWAL = 0;
+ pBt->btsFlags &= ~BTS_NO_WAL;
return rc;
}
+/*
+** set the mask of hint flags for cursor pCsr. Currently the only valid
+** values are 0 and BTREE_BULKLOAD.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
+ assert( mask==BTREE_BULKLOAD || mask==0 );
+ pCsr->hints = mask;
+}
+
+/*
+** Return true if the given Btree is read-only.
+*/
+SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){
+ return (p->pBt->btsFlags & BTS_READ_ONLY)!=0;
+}
+
/************** End of btree.c ***********************************************/
/************** Begin file backup.c ******************************************/
/*
@@ -55697,12 +60922,6 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
** API functions and the related features.
*/
-/* Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
/*
** Structure allocated for each backup operation.
*/
@@ -55775,15 +60994,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
int rc = 0;
pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
if( pParse==0 ){
- sqlite3Error(pErrorDb, SQLITE_NOMEM, "out of memory");
+ sqlite3ErrorWithMsg(pErrorDb, SQLITE_NOMEM, "out of memory");
rc = SQLITE_NOMEM;
}else{
pParse->db = pDb;
if( sqlite3OpenTempDatabase(pParse) ){
- sqlite3Error(pErrorDb, pParse->rc, "%s", pParse->zErrMsg);
+ sqlite3ErrorWithMsg(pErrorDb, pParse->rc, "%s", pParse->zErrMsg);
rc = SQLITE_ERROR;
}
sqlite3DbFree(pErrorDb, pParse->zErrMsg);
+ sqlite3ParserReset(pParse);
sqlite3StackFree(pErrorDb, pParse);
}
if( rc ){
@@ -55792,7 +61012,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
}
if( i<0 ){
- sqlite3Error(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb);
+ sqlite3ErrorWithMsg(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb);
return 0;
}
@@ -55837,7 +61057,7 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
sqlite3_mutex_enter(pDestDb->mutex);
if( pSrcDb==pDestDb ){
- sqlite3Error(
+ sqlite3ErrorWithMsg(
pDestDb, SQLITE_ERROR, "source and destination must be distinct"
);
p = 0;
@@ -55846,15 +61066,14 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
** sqlite3_backup_finish(). */
- p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup));
+ p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup));
if( !p ){
- sqlite3Error(pDestDb, SQLITE_NOMEM, 0);
+ sqlite3Error(pDestDb, SQLITE_NOMEM);
}
}
/* If the allocation succeeded, populate the new object. */
if( p ){
- memset(p, 0, sizeof(sqlite3_backup));
p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
p->pDestDb = pDestDb;
@@ -55895,20 +61114,28 @@ static int isFatalError(int rc){
** page iSrcPg from the source database. Copy this data into the
** destination database.
*/
-static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
+static int backupOnePage(
+ sqlite3_backup *p, /* Backup handle */
+ Pgno iSrcPg, /* Source database page to backup */
+ const u8 *zSrcData, /* Source database page data */
+ int bUpdate /* True for an update, false otherwise */
+){
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
#ifdef SQLITE_HAS_CODEC
- int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc);
+ /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
+ ** guaranteed that the shared-mutex is held by this thread, handle
+ ** p->pSrc may not actually be the owner. */
+ int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
#endif
-
int rc = SQLITE_OK;
i64 iOff;
+ assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
assert( p->bDestLocked );
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
@@ -55965,6 +61192,9 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
*/
memcpy(zOut, zIn, nCopy);
((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
+ if( iOff==0 && bUpdate==0 ){
+ sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
+ }
}
sqlite3PagerUnref(pDestPg);
}
@@ -56069,9 +61299,10 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
const Pgno iSrcPg = p->iNext; /* Source page number */
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
DbPage *pSrcPg; /* Source page object */
- rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ rc = sqlite3PagerAcquire(pSrcPager, iSrcPg, &pSrcPg,
+ PAGER_GET_READONLY);
if( rc==SQLITE_OK ){
- rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
+ rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
sqlite3PagerUnref(pSrcPg);
}
}
@@ -56092,102 +61323,129 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** the case where the source and destination databases have the
** same schema version.
*/
- if( rc==SQLITE_DONE
- && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK
- ){
- int nDestTruncate;
-
- if( p->pDestDb ){
- sqlite3ResetInternalSchema(p->pDestDb, -1);
+ if( rc==SQLITE_DONE ){
+ if( nSrcPage==0 ){
+ rc = sqlite3BtreeNewDb(p->pDest);
+ nSrcPage = 1;
}
-
- /* Set nDestTruncate to the final number of pages in the destination
- ** database. The complication here is that the destination page
- ** size may be different to the source page size.
- **
- ** If the source page size is smaller than the destination page size,
- ** round up. In this case the call to sqlite3OsTruncate() below will
- ** fix the size of the file. However it is important to call
- ** sqlite3PagerTruncateImage() here so that any pages in the
- ** destination file that lie beyond the nDestTruncate page mark are
- ** journalled by PagerCommitPhaseOne() before they are destroyed
- ** by the file truncation.
- */
- assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
- assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
- if( pgszSrc<pgszDest ){
- int ratio = pgszDest/pgszSrc;
- nDestTruncate = (nSrcPage+ratio-1)/ratio;
- if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
- nDestTruncate--;
+ if( rc==SQLITE_OK || rc==SQLITE_DONE ){
+ rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
+ }
+ if( rc==SQLITE_OK ){
+ if( p->pDestDb ){
+ sqlite3ResetAllSchemasOfConnection(p->pDestDb);
+ }
+ if( destMode==PAGER_JOURNALMODE_WAL ){
+ rc = sqlite3BtreeSetVersion(p->pDest, 2);
}
- }else{
- nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
}
- sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
-
- if( pgszSrc<pgszDest ){
- /* If the source page-size is smaller than the destination page-size,
- ** two extra things may need to happen:
- **
- ** * The destination may need to be truncated, and
+ if( rc==SQLITE_OK ){
+ int nDestTruncate;
+ /* Set nDestTruncate to the final number of pages in the destination
+ ** database. The complication here is that the destination page
+ ** size may be different to the source page size.
**
- ** * Data stored on the pages immediately following the
- ** pending-byte page in the source database may need to be
- ** copied into the destination database.
+ ** If the source page size is smaller than the destination page size,
+ ** round up. In this case the call to sqlite3OsTruncate() below will
+ ** fix the size of the file. However it is important to call
+ ** sqlite3PagerTruncateImage() here so that any pages in the
+ ** destination file that lie beyond the nDestTruncate page mark are
+ ** journalled by PagerCommitPhaseOne() before they are destroyed
+ ** by the file truncation.
*/
- const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
- sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
- i64 iOff;
- i64 iEnd;
-
- assert( pFile );
- assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
- nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
- && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
- ));
-
- /* This call ensures that all data required to recreate the original
- ** database has been stored in the journal for pDestPager and the
- ** journal synced to disk. So at this point we may safely modify
- ** the database file in any way, knowing that if a power failure
- ** occurs, the original database will be reconstructed from the
- ** journal file. */
- rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
-
- /* Write the extra pages and truncate the database file as required. */
- iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
- for(
- iOff=PENDING_BYTE+pgszSrc;
- rc==SQLITE_OK && iOff<iEnd;
- iOff+=pgszSrc
- ){
- PgHdr *pSrcPg = 0;
- const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
- rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
- if( rc==SQLITE_OK ){
- u8 *zData = sqlite3PagerGetData(pSrcPg);
- rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
+ assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
+ assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
+ if( pgszSrc<pgszDest ){
+ int ratio = pgszDest/pgszSrc;
+ nDestTruncate = (nSrcPage+ratio-1)/ratio;
+ if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
+ nDestTruncate--;
}
- sqlite3PagerUnref(pSrcPg);
- }
- if( rc==SQLITE_OK ){
- rc = backupTruncateFile(pFile, iSize);
+ }else{
+ nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
}
+ assert( nDestTruncate>0 );
- /* Sync the database file to disk. */
- if( rc==SQLITE_OK ){
- rc = sqlite3PagerSync(pDestPager);
+ if( pgszSrc<pgszDest ){
+ /* If the source page-size is smaller than the destination page-size,
+ ** two extra things may need to happen:
+ **
+ ** * The destination may need to be truncated, and
+ **
+ ** * Data stored on the pages immediately following the
+ ** pending-byte page in the source database may need to be
+ ** copied into the destination database.
+ */
+ const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
+ sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
+ Pgno iPg;
+ int nDstPage;
+ i64 iOff;
+ i64 iEnd;
+
+ assert( pFile );
+ assert( nDestTruncate==0
+ || (i64)nDestTruncate*(i64)pgszDest >= iSize || (
+ nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
+ && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
+ ));
+
+ /* This block ensures that all data required to recreate the original
+ ** database has been stored in the journal for pDestPager and the
+ ** journal synced to disk. So at this point we may safely modify
+ ** the database file in any way, knowing that if a power failure
+ ** occurs, the original database will be reconstructed from the
+ ** journal file. */
+ sqlite3PagerPagecount(pDestPager, &nDstPage);
+ for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
+ if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
+ DbPage *pPg;
+ rc = sqlite3PagerGet(pDestPager, iPg, &pPg);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerWrite(pPg);
+ sqlite3PagerUnref(pPg);
+ }
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
+ }
+
+ /* Write the extra pages and truncate the database file as required */
+ iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
+ for(
+ iOff=PENDING_BYTE+pgszSrc;
+ rc==SQLITE_OK && iOff<iEnd;
+ iOff+=pgszSrc
+ ){
+ PgHdr *pSrcPg = 0;
+ const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
+ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ if( rc==SQLITE_OK ){
+ u8 *zData = sqlite3PagerGetData(pSrcPg);
+ rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
+ }
+ sqlite3PagerUnref(pSrcPg);
+ }
+ if( rc==SQLITE_OK ){
+ rc = backupTruncateFile(pFile, iSize);
+ }
+
+ /* Sync the database file to disk. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerSync(pDestPager, 0);
+ }
+ }else{
+ sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
+ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
+ }
+
+ /* Finish committing the transaction to the destination database. */
+ if( SQLITE_OK==rc
+ && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
+ ){
+ rc = SQLITE_DONE;
}
- }else{
- rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
- }
-
- /* Finish committing the transaction to the destination database. */
- if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
- ){
- rc = SQLITE_DONE;
}
}
@@ -56221,14 +61479,14 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
- sqlite3_mutex *mutex; /* Mutex to protect source database */
+ sqlite3 *pSrcDb; /* Source database connection */
int rc; /* Value to return */
/* Enter the mutexes */
if( p==0 ) return SQLITE_OK;
- sqlite3_mutex_enter(p->pSrcDb->mutex);
+ pSrcDb = p->pSrcDb;
+ sqlite3_mutex_enter(pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
- mutex = p->pSrcDb->mutex;
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
@@ -56246,15 +61504,15 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
}
/* If a transaction is still open on the Btree, roll it back. */
- sqlite3BtreeRollback(p->pDest);
+ sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
/* Set the error code of the destination database handle. */
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
- sqlite3Error(p->pDestDb, rc, 0);
-
- /* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){
- sqlite3_mutex_leave(p->pDestDb->mutex);
+ sqlite3Error(p->pDestDb, rc);
+
+ /* Exit the mutexes and free the backup context structure. */
+ sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){
@@ -56263,7 +61521,7 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
** sqlite3_backup_finish(). */
sqlite3_free(p);
}
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(pSrcDb);
return rc;
}
@@ -56307,7 +61565,7 @@ SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, con
int rc;
assert( p->pDestDb );
sqlite3_mutex_enter(p->pDestDb->mutex);
- rc = backupOnePage(p, iPage, aData);
+ rc = backupOnePage(p, iPage, aData, 1);
sqlite3_mutex_leave(p->pDestDb->mutex);
assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
if( rc!=SQLITE_OK ){
@@ -56347,10 +61605,20 @@ SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *pBackup){
*/
SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
int rc;
+ sqlite3_file *pFd; /* File descriptor for database pTo */
sqlite3_backup b;
sqlite3BtreeEnter(pTo);
sqlite3BtreeEnter(pFrom);
+ assert( sqlite3BtreeIsInTrans(pTo) );
+ pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
+ if( pFd->pMethods ){
+ i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
+ rc = sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ if( rc ) goto copy_finished;
+ }
+
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
** to 0. This is used by the implementations of sqlite3_backup_step()
** and sqlite3_backup_finish() to detect that they are being called
@@ -56373,9 +61641,13 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
assert( b.rc!=SQLITE_OK );
rc = sqlite3_backup_finish(&b);
if( rc==SQLITE_OK ){
- pTo->pBt->pageSizeFixed = 0;
+ pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
+ }else{
+ sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
}
+ assert( sqlite3BtreeIsInTrans(pTo)==0 );
+copy_finished:
sqlite3BtreeLeave(pFrom);
sqlite3BtreeLeave(pTo);
return rc;
@@ -56402,11 +61674,52 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
** name sqlite_value
*/
+#ifdef SQLITE_DEBUG
/*
-** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*)
-** P if required.
+** Check invariants on a Mem object.
+**
+** This routine is intended for use inside of assert() statements, like
+** this: assert( sqlite3VdbeCheckMemInvariants(pMem) );
*/
-#define expandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
+SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
+ /* If MEM_Dyn is set then Mem.xDel!=0.
+ ** Mem.xDel is might not be initialized if MEM_Dyn is clear.
+ */
+ assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 );
+
+ /* MEM_Dyn may only be set if Mem.szMalloc==0. In this way we
+ ** ensure that if Mem.szMalloc>0 then it is safe to do
+ ** Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn.
+ ** That saves a few cycles in inner loops. */
+ assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 );
+
+ /* Cannot be both MEM_Int and MEM_Real at the same time */
+ assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) );
+
+ /* The szMalloc field holds the correct memory allocation size */
+ assert( p->szMalloc==0
+ || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) );
+
+ /* If p holds a string or blob, the Mem.z must point to exactly
+ ** one of the following:
+ **
+ ** (1) Memory in Mem.zMalloc and managed by the Mem object
+ ** (2) Memory to be freed using Mem.xDel
+ ** (3) An ephemeral string or blob
+ ** (4) A static string or blob
+ */
+ if( (p->flags & (MEM_Str|MEM_Blob)) && p->n>0 ){
+ assert(
+ ((p->szMalloc>0 && p->z==p->zMalloc)? 1 : 0) +
+ ((p->flags&MEM_Dyn)!=0 ? 1 : 0) +
+ ((p->flags&MEM_Ephem)!=0 ? 1 : 0) +
+ ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1
+ );
+ }
+ return 1;
+}
+#endif
+
/*
** If pMem is an object with a valid string representation, this routine
@@ -56422,7 +61735,9 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
** between formats.
*/
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
+#ifndef SQLITE_OMIT_UTF16
int rc;
+#endif
assert( (pMem->flags&MEM_RowSet)==0 );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
@@ -56447,59 +61762,84 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
/*
** Make sure pMem->z points to a writable allocation of at least
-** n bytes.
-**
-** If the memory cell currently contains string or blob data
-** and the third argument passed to this function is true, the
-** current content of the cell is preserved. Otherwise, it may
-** be discarded.
-**
-** This function sets the MEM_Dyn flag and clears any xDel callback.
-** It also clears MEM_Ephem and MEM_Static. If the preserve flag is
-** not set, Mem.n is zeroed.
-*/
-SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve){
- assert( 1 >=
- ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) +
- (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) +
- ((pMem->flags&MEM_Ephem) ? 1 : 0) +
- ((pMem->flags&MEM_Static) ? 1 : 0)
- );
+** min(n,32) bytes.
+**
+** If the bPreserve argument is true, then copy of the content of
+** pMem->z into the new allocation. pMem must be either a string or
+** blob if bPreserve is true. If bPreserve is false, any prior content
+** in pMem->z is discarded.
+*/
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
+ assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
- if( n<32 ) n = 32;
- if( sqlite3DbMallocSize(pMem->db, pMem->zMalloc)<n ){
- if( preserve && pMem->z==pMem->zMalloc ){
+ /* If the bPreserve flag is set to true, then the memory cell must already
+ ** contain a valid string or blob value. */
+ assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) );
+ testcase( bPreserve && pMem->z==0 );
+
+ assert( pMem->szMalloc==0
+ || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
+ if( pMem->szMalloc<n ){
+ if( n<32 ) n = 32;
+ if( bPreserve && pMem->szMalloc>0 && pMem->z==pMem->zMalloc ){
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
- preserve = 0;
+ bPreserve = 0;
}else{
- sqlite3DbFree(pMem->db, pMem->zMalloc);
+ if( pMem->szMalloc>0 ) sqlite3DbFree(pMem->db, pMem->zMalloc);
pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n);
}
+ if( pMem->zMalloc==0 ){
+ sqlite3VdbeMemSetNull(pMem);
+ pMem->z = 0;
+ pMem->szMalloc = 0;
+ return SQLITE_NOMEM;
+ }else{
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
+ }
}
- if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){
+ if( bPreserve && pMem->z && pMem->z!=pMem->zMalloc ){
memcpy(pMem->zMalloc, pMem->z, pMem->n);
}
- if( pMem->flags&MEM_Dyn && pMem->xDel ){
+ if( (pMem->flags&MEM_Dyn)!=0 ){
+ assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC );
pMem->xDel((void *)(pMem->z));
}
pMem->z = pMem->zMalloc;
- if( pMem->z==0 ){
- pMem->flags = MEM_Null;
- }else{
- pMem->flags &= ~(MEM_Ephem|MEM_Static);
+ pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static);
+ return SQLITE_OK;
+}
+
+/*
+** Change the pMem->zMalloc allocation to be at least szNew bytes.
+** If pMem->zMalloc already meets or exceeds the requested size, this
+** routine is a no-op.
+**
+** Any prior string or blob content in the pMem object may be discarded.
+** The pMem->xDel destructor is called, if it exists. Though MEM_Str
+** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, and MEM_Null
+** values are preserved.
+**
+** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM)
+** if unable to complete the resizing.
+*/
+SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
+ assert( szNew>0 );
+ assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 );
+ if( pMem->szMalloc<szNew ){
+ return sqlite3VdbeMemGrow(pMem, szNew, 0);
}
- pMem->xDel = 0;
- return (pMem->z ? SQLITE_OK : SQLITE_NOMEM);
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ pMem->z = pMem->zMalloc;
+ pMem->flags &= (MEM_Null|MEM_Int|MEM_Real);
+ return SQLITE_OK;
}
/*
-** Make the given Mem object MEM_Dyn. In other words, make it so
-** that any TEXT or BLOB content is stored in memory obtained from
-** malloc(). In this way, we know that the memory is safe to be
-** overwritten or altered.
+** Change pMem so that its MEM_Str or MEM_Blob value is stored in
+** MEM.zMalloc, where it can be safely written.
**
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
@@ -56507,9 +61847,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
int f;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( (pMem->flags&MEM_RowSet)==0 );
- expandBlob(pMem);
+ ExpandBlob(pMem);
f = pMem->flags;
- if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){
+ if( (f&(MEM_Str|MEM_Blob)) && (pMem->szMalloc==0 || pMem->z!=pMem->zMalloc) ){
if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
return SQLITE_NOMEM;
}
@@ -56553,15 +61893,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
}
#endif
-
/*
-** Make sure the given Mem is \u0000 terminated.
+** It is already known that pMem contains an unterminated string.
+** Add the zero terminator.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- if( (pMem->flags & MEM_Term)!=0 || (pMem->flags & MEM_Str)==0 ){
- return SQLITE_OK; /* Nothing to do */
- }
+static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
return SQLITE_NOMEM;
}
@@ -56572,20 +61908,34 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
}
/*
+** Make sure the given Mem is \u0000 terminated.
+*/
+SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+ testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) );
+ testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 );
+ if( (pMem->flags & (MEM_Term|MEM_Str))!=MEM_Str ){
+ return SQLITE_OK; /* Nothing to do */
+ }else{
+ return vdbeMemAddTerminator(pMem);
+ }
+}
+
+/*
** Add MEM_Str to the set of representations for the given Mem. Numbers
** are converted using sqlite3_snprintf(). Converting a BLOB to a string
** is a no-op.
**
-** Existing representations MEM_Int and MEM_Real are *not* invalidated.
+** Existing representations MEM_Int and MEM_Real are invalidated if
+** bForce is true but are retained if bForce is false.
**
** A MEM_Null value will never be passed to this function. This function is
** used for converting values to text for returning to the user (i.e. via
** sqlite3_value_text()), or for ensuring that values to be used as btree
** keys are strings. In the former case a NULL pointer is returned the
-** user and the later is an internal programming error.
+** user and the latter is an internal programming error.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){
- int rc = SQLITE_OK;
+SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
int fg = pMem->flags;
const int nByte = 32;
@@ -56597,11 +61947,11 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+ if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
return SQLITE_NOMEM;
}
- /* For a Real or Integer, use sqlite3_mprintf() to produce the UTF-8
+ /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
** string representation of the value. Then, if the required encoding
** is UTF-16le or UTF-16be do a translation.
**
@@ -56611,13 +61961,14 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){
sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
}else{
assert( fg & MEM_Real );
- sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->r);
+ sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
}
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
pMem->flags |= MEM_Str|MEM_Term;
+ if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
sqlite3VdbeChangeEncoding(pMem, enc);
- return rc;
+ return SQLITE_OK;
}
/*
@@ -56632,74 +61983,96 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
int rc = SQLITE_OK;
if( ALWAYS(pFunc && pFunc->xFinalize) ){
sqlite3_context ctx;
+ Mem t;
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
memset(&ctx, 0, sizeof(ctx));
- ctx.s.flags = MEM_Null;
- ctx.s.db = pMem->db;
+ memset(&t, 0, sizeof(t));
+ t.flags = MEM_Null;
+ t.db = pMem->db;
+ ctx.pOut = &t;
ctx.pMem = pMem;
ctx.pFunc = pFunc;
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
- assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel );
- sqlite3DbFree(pMem->db, pMem->zMalloc);
- memcpy(pMem, &ctx.s, sizeof(ctx.s));
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ if( pMem->szMalloc>0 ) sqlite3DbFree(pMem->db, pMem->zMalloc);
+ memcpy(pMem, &t, sizeof(t));
rc = ctx.isError;
}
return rc;
}
/*
-** If the memory cell contains a string value that must be freed by
-** invoking an external callback, free it now. Calling this function
-** does not free any Mem.zMalloc buffer.
+** If the memory cell contains a value that must be freed by
+** invoking the external callback in Mem.xDel, then this routine
+** will free that value. It also sets Mem.flags to MEM_Null.
+**
+** This is a helper routine for sqlite3VdbeMemSetNull() and
+** for sqlite3VdbeMemRelease(). Use those other routines as the
+** entry point for releasing Mem resources.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){
+static SQLITE_NOINLINE void vdbeMemClearExternAndSetNull(Mem *p){
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
- testcase( p->flags & MEM_Agg );
- testcase( p->flags & MEM_Dyn );
- testcase( p->flags & MEM_RowSet );
- testcase( p->flags & MEM_Frame );
- if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){
- if( p->flags&MEM_Agg ){
- sqlite3VdbeMemFinalize(p, p->u.pDef);
- assert( (p->flags & MEM_Agg)==0 );
- sqlite3VdbeMemRelease(p);
- }else if( p->flags&MEM_Dyn && p->xDel ){
- assert( (p->flags&MEM_RowSet)==0 );
- p->xDel((void *)p->z);
- p->xDel = 0;
- }else if( p->flags&MEM_RowSet ){
- sqlite3RowSetClear(p->u.pRowSet);
- }else if( p->flags&MEM_Frame ){
- sqlite3VdbeMemSetNull(p);
- }
+ assert( VdbeMemDynamic(p) );
+ if( p->flags&MEM_Agg ){
+ sqlite3VdbeMemFinalize(p, p->u.pDef);
+ assert( (p->flags & MEM_Agg)==0 );
+ testcase( p->flags & MEM_Dyn );
+ }
+ if( p->flags&MEM_Dyn ){
+ assert( (p->flags&MEM_RowSet)==0 );
+ assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
+ p->xDel((void *)p->z);
+ }else if( p->flags&MEM_RowSet ){
+ sqlite3RowSetClear(p->u.pRowSet);
+ }else if( p->flags&MEM_Frame ){
+ VdbeFrame *pFrame = p->u.pFrame;
+ pFrame->pParent = pFrame->v->pDelFrame;
+ pFrame->v->pDelFrame = pFrame;
}
+ p->flags = MEM_Null;
}
/*
-** Release any memory held by the Mem. This may leave the Mem in an
-** inconsistent state, for example with (Mem.z==0) and
-** (Mem.type==SQLITE_TEXT).
+** Release memory held by the Mem p, both external memory cleared
+** by p->xDel and memory in p->zMalloc.
+**
+** This is a helper routine invoked by sqlite3VdbeMemRelease() in
+** the unusual case where there really is memory in p that needs
+** to be freed.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){
- sqlite3VdbeMemReleaseExternal(p);
- sqlite3DbFree(p->db, p->zMalloc);
+static SQLITE_NOINLINE void vdbeMemClear(Mem *p){
+ if( VdbeMemDynamic(p) ){
+ vdbeMemClearExternAndSetNull(p);
+ }
+ if( p->szMalloc ){
+ sqlite3DbFree(p->db, p->zMalloc);
+ p->szMalloc = 0;
+ }
p->z = 0;
- p->zMalloc = 0;
- p->xDel = 0;
}
/*
-** Convert a 64-bit IEEE double into a 64-bit signed integer.
-** If the double is too large, return 0x8000000000000000.
+** Release any memory resources held by the Mem. Both the memory that is
+** free by Mem.xDel and the Mem.zMalloc allocation are freed.
+**
+** Use this routine prior to clean up prior to abandoning a Mem, or to
+** reset a Mem back to its minimum memory utilization.
**
-** Most systems appear to do this simply by assigning
-** variables and without the extra range tests. But
-** there are reports that windows throws an expection
-** if the floating point value is out of range. (See ticket #2880.)
-** Because we do not completely understand the problem, we will
-** take the conservative approach and always do range tests
-** before attempting the conversion.
+** Use sqlite3VdbeMemSetNull() to release just the Mem.xDel space
+** prior to inserting new content into the Mem.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){
+ assert( sqlite3VdbeCheckMemInvariants(p) );
+ if( VdbeMemDynamic(p) || p->szMalloc ){
+ vdbeMemClear(p);
+ }
+}
+
+/*
+** Convert a 64-bit IEEE double into a 64-bit signed integer.
+** If the double is out of range of a 64-bit signed integer then
+** return the closest available 64-bit signed integer.
*/
static i64 doubleToInt64(double r){
#ifdef SQLITE_OMIT_FLOATING_POINT
@@ -56716,14 +62089,10 @@ static i64 doubleToInt64(double r){
static const i64 maxInt = LARGEST_INT64;
static const i64 minInt = SMALLEST_INT64;
- if( r<(double)minInt ){
- return minInt;
- }else if( r>(double)maxInt ){
- /* minInt is correct here - not maxInt. It turns out that assigning
- ** a very large positive number to an integer results in a very large
- ** negative integer. This makes no sense, but it is what x86 hardware
- ** does so for compatibility we will do the same in software. */
+ if( r<=(double)minInt ){
return minInt;
+ }else if( r>=(double)maxInt ){
+ return maxInt;
}else{
return (i64)r;
}
@@ -56736,7 +62105,7 @@ static i64 doubleToInt64(double r){
** If pMem is an integer, then the value is exact. If pMem is
** a floating-point then the value returned is the integer part.
** If pMem is a string or blob, then we make an attempt to convert
-** it into a integer and return that. If pMem represents an
+** it into an integer and return that. If pMem represents an
** an SQL-NULL value, return 0.
**
** If pMem represents a string value, its encoding might be changed.
@@ -56749,11 +62118,10 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){
if( flags & MEM_Int ){
return pMem->u.i;
}else if( flags & MEM_Real ){
- return doubleToInt64(pMem->r);
+ return doubleToInt64(pMem->u.r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value = 0;
assert( pMem->z || pMem->n==0 );
- testcase( pMem->z==0 );
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}else{
@@ -56771,7 +62139,7 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
if( pMem->flags & MEM_Real ){
- return pMem->r;
+ return pMem->u.r;
}else if( pMem->flags & MEM_Int ){
return (double)pMem->u.i;
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
@@ -56790,12 +62158,13 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
** MEM_Int if we can.
*/
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
+ i64 ix;
assert( pMem->flags & MEM_Real );
assert( (pMem->flags & MEM_RowSet)==0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- pMem->u.i = doubleToInt64(pMem->r);
+ ix = doubleToInt64(pMem->u.r);
/* Only mark the value as an integer if
**
@@ -56805,13 +62174,11 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
**
** The second and third terms in the following conditional enforces
** the second condition under the assumption that addition overflow causes
- ** values to wrap around. On x86 hardware, the third term is always
- ** true and could be omitted. But we leave it in because other
- ** architectures might behave differently.
+ ** values to wrap around.
*/
- if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64
- && ALWAYS(pMem->u.i<LARGEST_INT64) ){
- pMem->flags |= MEM_Int;
+ if( pMem->u.r==ix && ix>SMALLEST_INT64 && ix<LARGEST_INT64 ){
+ pMem->u.i = ix;
+ MemSetTypeFlag(pMem, MEM_Int);
}
}
@@ -56836,7 +62203,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- pMem->r = sqlite3VdbeRealValue(pMem);
+ pMem->u.r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
return SQLITE_OK;
}
@@ -56856,7 +62223,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
MemSetTypeFlag(pMem, MEM_Int);
}else{
- pMem->r = sqlite3VdbeRealValue(pMem);
+ pMem->u.r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
sqlite3VdbeIntegerAffinity(pMem);
}
@@ -56867,19 +62234,83 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
}
/*
+** Cast the datatype of the value in pMem according to the affinity
+** "aff". Casting is different from applying affinity in that a cast
+** is forced. In other words, the value is converted into the desired
+** affinity even if that results in loss of data. This routine is
+** used (for example) to implement the SQL "cast()" operator.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
+ if( pMem->flags & MEM_Null ) return;
+ switch( aff ){
+ case SQLITE_AFF_NONE: { /* Really a cast to BLOB */
+ if( (pMem->flags & MEM_Blob)==0 ){
+ sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
+ assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
+ MemSetTypeFlag(pMem, MEM_Blob);
+ }else{
+ pMem->flags &= ~(MEM_TypeMask&~MEM_Blob);
+ }
+ break;
+ }
+ case SQLITE_AFF_NUMERIC: {
+ sqlite3VdbeMemNumerify(pMem);
+ break;
+ }
+ case SQLITE_AFF_INTEGER: {
+ sqlite3VdbeMemIntegerify(pMem);
+ break;
+ }
+ case SQLITE_AFF_REAL: {
+ sqlite3VdbeMemRealify(pMem);
+ break;
+ }
+ default: {
+ assert( aff==SQLITE_AFF_TEXT );
+ assert( MEM_Str==(MEM_Blob>>3) );
+ pMem->flags |= (pMem->flags&MEM_Blob)>>3;
+ sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
+ assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
+ pMem->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero);
+ break;
+ }
+ }
+}
+
+/*
+** Initialize bulk memory to be a consistent Mem object.
+**
+** The minimum amount of initialization feasible is performed.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem *pMem, sqlite3 *db, u16 flags){
+ assert( (flags & ~MEM_TypeMask)==0 );
+ pMem->flags = flags;
+ pMem->db = db;
+ pMem->szMalloc = 0;
+}
+
+
+/*
** Delete any previous value and set the value stored in *pMem to NULL.
+**
+** This routine calls the Mem.xDel destructor to dispose of values that
+** require the destructor. But it preserves the Mem.zMalloc memory allocation.
+** To free all resources, use sqlite3VdbeMemRelease(), which both calls this
+** routine to invoke the destructor and deallocates Mem.zMalloc.
+**
+** Use this routine to reset the Mem prior to insert a new value.
+**
+** Use sqlite3VdbeMemRelease() to complete erase the Mem prior to abandoning it.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){
- if( pMem->flags & MEM_Frame ){
- VdbeFrame *pFrame = pMem->u.pFrame;
- pFrame->pParent = pFrame->v->pDelFrame;
- pFrame->v->pDelFrame = pFrame;
- }
- if( pMem->flags & MEM_RowSet ){
- sqlite3RowSetClear(pMem->u.pRowSet);
+ if( VdbeMemDynamic(pMem) ){
+ vdbeMemClearExternAndSetNull(pMem);
+ }else{
+ pMem->flags = MEM_Null;
}
- MemSetTypeFlag(pMem, MEM_Null);
- pMem->type = SQLITE_NULL;
+}
+SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){
+ sqlite3VdbeMemSetNull((Mem*)p);
}
/*
@@ -56889,19 +62320,22 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
sqlite3VdbeMemRelease(pMem);
pMem->flags = MEM_Blob|MEM_Zero;
- pMem->type = SQLITE_BLOB;
pMem->n = 0;
if( n<0 ) n = 0;
pMem->u.nZero = n;
pMem->enc = SQLITE_UTF8;
+ pMem->z = 0;
+}
-#ifdef SQLITE_OMIT_INCRBLOB
- sqlite3VdbeMemGrow(pMem, n, 0);
- if( pMem->z ){
- pMem->n = n;
- memset(pMem->z, 0, n);
- }
-#endif
+/*
+** The pMem is known to contain content that needs to be destroyed prior
+** to a value change. So invoke the destructor, then set the value to
+** a 64-bit integer.
+*/
+static SQLITE_NOINLINE void vdbeReleaseAndSetInt64(Mem *pMem, i64 val){
+ sqlite3VdbeMemSetNull(pMem);
+ pMem->u.i = val;
+ pMem->flags = MEM_Int;
}
/*
@@ -56909,10 +62343,12 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
** manifest type INTEGER.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
- sqlite3VdbeMemRelease(pMem);
- pMem->u.i = val;
- pMem->flags = MEM_Int;
- pMem->type = SQLITE_INTEGER;
+ if( VdbeMemDynamic(pMem) ){
+ vdbeReleaseAndSetInt64(pMem, val);
+ }else{
+ pMem->u.i = val;
+ pMem->flags = MEM_Int;
+ }
}
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -56921,13 +62357,10 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
** manifest type REAL.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
- if( sqlite3IsNaN(val) ){
- sqlite3VdbeMemSetNull(pMem);
- }else{
- sqlite3VdbeMemRelease(pMem);
- pMem->r = val;
+ sqlite3VdbeMemSetNull(pMem);
+ if( !sqlite3IsNaN(val) ){
+ pMem->u.r = val;
pMem->flags = MEM_Real;
- pMem->type = SQLITE_FLOAT;
}
}
#endif
@@ -56944,10 +62377,11 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem *pMem){
pMem->zMalloc = sqlite3DbMallocRaw(db, 64);
if( db->mallocFailed ){
pMem->flags = MEM_Null;
+ pMem->szMalloc = 0;
}else{
assert( pMem->zMalloc );
- pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc,
- sqlite3DbMallocSize(db, pMem->zMalloc));
+ pMem->szMalloc = sqlite3DbMallocSize(db, pMem->zMalloc);
+ pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, pMem->szMalloc);
assert( pMem->u.pRowSet!=0 );
pMem->flags = MEM_RowSet;
}
@@ -56971,19 +62405,19 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){
#ifdef SQLITE_DEBUG
/*
-** This routine prepares a memory cell for modication by breaking
+** This routine prepares a memory cell for modification by breaking
** its link to a shallow copy and by marking any current shallow
** copies of this cell as invalid.
**
** This is used for testing and debugging only - to make sure shallow
** copies are not misused.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
+SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
int i;
Mem *pX;
for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
- pX->flags |= MEM_Invalid;
+ pX->flags |= MEM_Undefined;
pX->pScopyFrom = 0;
}
}
@@ -56994,7 +62428,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
/*
** Size of struct Mem not including the Mem.zMalloc member.
*/
-#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc))
+#define MEMCELLSIZE offsetof(Mem,zMalloc)
/*
** Make an shallow copy of pFrom into pTo. Prior contents of
@@ -57004,9 +62438,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
*/
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
assert( (pFrom->flags & MEM_RowSet)==0 );
- sqlite3VdbeMemReleaseExternal(pTo);
+ assert( pTo->db==pFrom->db );
+ if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
- pTo->xDel = 0;
if( (pFrom->flags&MEM_Static)==0 ){
pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem);
assert( srcType==MEM_Ephem || srcType==MEM_Static );
@@ -57021,11 +62455,11 @@ SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int sr
SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;
+ assert( pTo->db==pFrom->db );
assert( (pFrom->flags & MEM_RowSet)==0 );
- sqlite3VdbeMemReleaseExternal(pTo);
+ if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
-
if( pTo->flags&(MEM_Str|MEM_Blob) ){
if( 0==(pFrom->flags&MEM_Static) ){
pTo->flags |= MEM_Ephem;
@@ -57050,8 +62484,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
sqlite3VdbeMemRelease(pTo);
memcpy(pTo, pFrom, sizeof(Mem));
pFrom->flags = MEM_Null;
- pFrom->xDel = 0;
- pFrom->zMalloc = 0;
+ pFrom->szMalloc = 0;
}
/*
@@ -57098,7 +62531,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
if( nByte<0 ){
assert( enc!=0 );
if( enc==SQLITE_UTF8 ){
- for(nByte=0; nByte<=iLimit && z[nByte]; nByte++){}
+ nByte = sqlite3Strlen30(z);
+ if( nByte>iLimit ) nByte = iLimit+1;
}else{
for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
}
@@ -57117,14 +62551,17 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
if( nByte>iLimit ){
return SQLITE_TOOBIG;
}
- if( sqlite3VdbeMemGrow(pMem, nAlloc, 0) ){
+ testcase( nAlloc==0 );
+ testcase( nAlloc==31 );
+ testcase( nAlloc==32 );
+ if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){
return SQLITE_NOMEM;
}
memcpy(pMem->z, z, nAlloc);
}else if( xDel==SQLITE_DYNAMIC ){
sqlite3VdbeMemRelease(pMem);
pMem->zMalloc = pMem->z = (char *)z;
- pMem->xDel = 0;
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
}else{
sqlite3VdbeMemRelease(pMem);
pMem->z = (char *)z;
@@ -57135,7 +62572,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
pMem->n = nByte;
pMem->flags = flags;
pMem->enc = (enc==0 ? SQLITE_UTF8 : enc);
- pMem->type = (enc==0 ? SQLITE_BLOB : SQLITE_TEXT);
#ifndef SQLITE_OMIT_UTF16
if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
@@ -57151,148 +62587,34 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
}
/*
-** Compare the values contained by the two memory cells, returning
-** negative, zero or positive if pMem1 is less than, equal to, or greater
-** than pMem2. Sorting order is NULL's first, followed by numbers (integers
-** and reals) sorted numerically, followed by text ordered by the collating
-** sequence pColl and finally blob's ordered by memcmp().
-**
-** Two NULL values are considered equal by this function.
-*/
-SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
- int rc;
- int f1, f2;
- int combined_flags;
-
- f1 = pMem1->flags;
- f2 = pMem2->flags;
- combined_flags = f1|f2;
- assert( (combined_flags & MEM_RowSet)==0 );
-
- /* If one value is NULL, it is less than the other. If both values
- ** are NULL, return 0.
- */
- if( combined_flags&MEM_Null ){
- return (f2&MEM_Null) - (f1&MEM_Null);
- }
-
- /* If one value is a number and the other is not, the number is less.
- ** If both are numbers, compare as reals if one is a real, or as integers
- ** if both values are integers.
- */
- if( combined_flags&(MEM_Int|MEM_Real) ){
- if( !(f1&(MEM_Int|MEM_Real)) ){
- return 1;
- }
- if( !(f2&(MEM_Int|MEM_Real)) ){
- return -1;
- }
- if( (f1 & f2 & MEM_Int)==0 ){
- double r1, r2;
- if( (f1&MEM_Real)==0 ){
- r1 = (double)pMem1->u.i;
- }else{
- r1 = pMem1->r;
- }
- if( (f2&MEM_Real)==0 ){
- r2 = (double)pMem2->u.i;
- }else{
- r2 = pMem2->r;
- }
- if( r1<r2 ) return -1;
- if( r1>r2 ) return 1;
- return 0;
- }else{
- assert( f1&MEM_Int );
- assert( f2&MEM_Int );
- if( pMem1->u.i < pMem2->u.i ) return -1;
- if( pMem1->u.i > pMem2->u.i ) return 1;
- return 0;
- }
- }
-
- /* If one value is a string and the other is a blob, the string is less.
- ** If both are strings, compare using the collating functions.
- */
- if( combined_flags&MEM_Str ){
- if( (f1 & MEM_Str)==0 ){
- return 1;
- }
- if( (f2 & MEM_Str)==0 ){
- return -1;
- }
-
- assert( pMem1->enc==pMem2->enc );
- assert( pMem1->enc==SQLITE_UTF8 ||
- pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
-
- /* The collation sequence must be defined at this point, even if
- ** the user deletes the collation sequence after the vdbe program is
- ** compiled (this was not always the case).
- */
- assert( !pColl || pColl->xCmp );
-
- if( pColl ){
- if( pMem1->enc==pColl->enc ){
- /* The strings are already in the correct encoding. Call the
- ** comparison function directly */
- return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
- }else{
- const void *v1, *v2;
- int n1, n2;
- Mem c1;
- Mem c2;
- memset(&c1, 0, sizeof(c1));
- memset(&c2, 0, sizeof(c2));
- sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
- sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
- v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
- n1 = v1==0 ? 0 : c1.n;
- v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
- n2 = v2==0 ? 0 : c2.n;
- rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
- sqlite3VdbeMemRelease(&c1);
- sqlite3VdbeMemRelease(&c2);
- return rc;
- }
- }
- /* If a NULL pointer was passed as the collate function, fall through
- ** to the blob case and use memcmp(). */
- }
-
- /* Both values must be blobs. Compare using memcmp(). */
- rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
- if( rc==0 ){
- rc = pMem1->n - pMem2->n;
- }
- return rc;
-}
-
-/*
** Move data out of a btree key or data field and into a Mem structure.
** The data or key is taken from the entry that pCur is currently pointing
** to. offset and amt determine what portion of the data or key to retrieve.
** key is true to get the key or false to get data. The result is written
** into the pMem element.
**
-** The pMem structure is assumed to be uninitialized. Any prior content
-** is overwritten without being freed.
+** The pMem object must have been initialized. This routine will use
+** pMem->zMalloc to hold the content from the btree, if possible. New
+** pMem->zMalloc space will be allocated if necessary. The calling routine
+** is responsible for making sure that the pMem object is eventually
+** destroyed.
**
** If this routine fails for any reason (malloc returns NULL or unable
** to read from the disk) then the pMem is left in an inconsistent state.
*/
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
BtCursor *pCur, /* Cursor pointing at record to retrieve. */
- int offset, /* Offset from the start of data to return bytes from. */
- int amt, /* Number of bytes to return. */
+ u32 offset, /* Offset from the start of data to return bytes from. */
+ u32 amt, /* Number of bytes to return. */
int key, /* If true, retrieve from the btree key, not data. */
Mem *pMem /* OUT: Return data in this Mem structure. */
){
char *zData; /* Data from the btree layer */
- int available = 0; /* Number of bytes available on the local btree page */
+ u32 available = 0; /* Number of bytes available on the local btree page */
int rc = SQLITE_OK; /* Return code */
assert( sqlite3BtreeCursorIsValid(pCur) );
+ assert( !VdbeMemDynamic(pMem) );
/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
** that both the BtShared and database handle mutexes are held. */
@@ -57304,65 +62626,60 @@ SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
}
assert( zData!=0 );
- if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){
- sqlite3VdbeMemRelease(pMem);
+ if( offset+amt<=available ){
pMem->z = &zData[offset];
pMem->flags = MEM_Blob|MEM_Ephem;
- }else if( SQLITE_OK==(rc = sqlite3VdbeMemGrow(pMem, amt+2, 0)) ){
- pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term;
- pMem->enc = 0;
- pMem->type = SQLITE_BLOB;
- if( key ){
- rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
- }else{
- rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
- }
- pMem->z[amt] = 0;
- pMem->z[amt+1] = 0;
- if( rc!=SQLITE_OK ){
- sqlite3VdbeMemRelease(pMem);
+ pMem->n = (int)amt;
+ }else{
+ pMem->flags = MEM_Null;
+ if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+2)) ){
+ if( key ){
+ rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
+ }else{
+ rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
+ }
+ if( rc==SQLITE_OK ){
+ pMem->z[amt] = 0;
+ pMem->z[amt+1] = 0;
+ pMem->flags = MEM_Blob|MEM_Term;
+ pMem->n = (int)amt;
+ }else{
+ sqlite3VdbeMemRelease(pMem);
+ }
}
}
- pMem->n = amt;
return rc;
}
-/* This function is only available internally, it is not part of the
-** external API. It works in a similar way to sqlite3_value_text(),
-** except the data returned is in the encoding specified by the second
-** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or
-** SQLITE_UTF8.
-**
-** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED.
-** If that is the case, then the result must be aligned on an even byte
-** boundary.
+/*
+** The pVal argument is known to be a value other than NULL.
+** Convert it into a string with encoding enc and return a pointer
+** to a zero-terminated version of that string.
*/
-SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
- if( !pVal ) return 0;
-
+static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
+ assert( pVal!=0 );
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
assert( (pVal->flags & MEM_RowSet)==0 );
-
- if( pVal->flags&MEM_Null ){
- return 0;
- }
- assert( (MEM_Blob>>3) == MEM_Str );
- pVal->flags |= (pVal->flags & MEM_Blob)>>3;
- expandBlob(pVal);
- if( pVal->flags&MEM_Str ){
- sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
+ assert( (pVal->flags & (MEM_Null))==0 );
+ if( pVal->flags & (MEM_Blob|MEM_Str) ){
+ pVal->flags |= MEM_Str;
+ if( pVal->flags & MEM_Zero ){
+ sqlite3VdbeMemExpandBlob(pVal);
+ }
+ if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){
+ sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
+ }
if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){
assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 );
if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){
return 0;
}
}
- sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */
+ sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */
}else{
- assert( (pVal->flags&MEM_Blob)==0 );
- sqlite3VdbeMemStringify(pVal, enc);
+ sqlite3VdbeMemStringify(pVal, enc, 0);
assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) );
}
assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0
@@ -57374,6 +62691,30 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
}
}
+/* This function is only available internally, it is not part of the
+** external API. It works in a similar way to sqlite3_value_text(),
+** except the data returned is in the encoding specified by the second
+** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or
+** SQLITE_UTF8.
+**
+** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED.
+** If that is the case, then the result must be aligned on an even byte
+** boundary.
+*/
+SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
+ if( !pVal ) return 0;
+ assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
+ assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
+ assert( (pVal->flags & MEM_RowSet)==0 );
+ if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
+ return pVal->z;
+ }
+ if( pVal->flags&MEM_Null ){
+ return 0;
+ }
+ return valueToText(pVal, enc);
+}
+
/*
** Create a new sqlite3_value object.
*/
@@ -57381,50 +62722,116 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *db){
Mem *p = sqlite3DbMallocZero(db, sizeof(*p));
if( p ){
p->flags = MEM_Null;
- p->type = SQLITE_NULL;
p->db = db;
}
return p;
}
/*
-** Create a new sqlite3_value object, containing the value of pExpr.
+** Context object passed by sqlite3Stat4ProbeSetValue() through to
+** valueNew(). See comments above valueNew() for details.
+*/
+struct ValueNewStat4Ctx {
+ Parse *pParse;
+ Index *pIdx;
+ UnpackedRecord **ppRec;
+ int iVal;
+};
+
+/*
+** Allocate and return a pointer to a new sqlite3_value object. If
+** the second argument to this function is NULL, the object is allocated
+** by calling sqlite3ValueNew().
**
-** This only works for very simple expressions that consist of one constant
-** token (i.e. "5", "5.1", "'a string'"). If the expression can
-** be converted directly into a value, then the value is allocated and
-** a pointer written to *ppVal. The caller is responsible for deallocating
-** the value by passing it to sqlite3ValueFree() later on. If the expression
-** cannot be converted to a value, then *ppVal is set to NULL.
+** Otherwise, if the second argument is non-zero, then this function is
+** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
+** already been allocated, allocate the UnpackedRecord structure that
+** that function will return to its caller here. Then return a pointer
+** an sqlite3_value within the UnpackedRecord.a[] array.
*/
-SQLITE_PRIVATE int sqlite3ValueFromExpr(
- sqlite3 *db, /* The database connection */
- Expr *pExpr, /* The expression to evaluate */
- u8 enc, /* Encoding to use */
- u8 affinity, /* Affinity to use */
- sqlite3_value **ppVal /* Write the new value here */
+static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( p ){
+ UnpackedRecord *pRec = p->ppRec[0];
+
+ if( pRec==0 ){
+ Index *pIdx = p->pIdx; /* Index being probed */
+ int nByte; /* Bytes of space to allocate */
+ int i; /* Counter variable */
+ int nCol = pIdx->nColumn; /* Number of index columns including rowid */
+
+ nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord));
+ pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
+ if( pRec ){
+ pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
+ if( pRec->pKeyInfo ){
+ assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
+ assert( pRec->pKeyInfo->enc==ENC(db) );
+ pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
+ for(i=0; i<nCol; i++){
+ pRec->aMem[i].flags = MEM_Null;
+ pRec->aMem[i].db = db;
+ }
+ }else{
+ sqlite3DbFree(db, pRec);
+ pRec = 0;
+ }
+ }
+ if( pRec==0 ) return 0;
+ p->ppRec[0] = pRec;
+ }
+
+ pRec->nField = p->iVal+1;
+ return &pRec->aMem[p->iVal];
+ }
+#else
+ UNUSED_PARAMETER(p);
+#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+ return sqlite3ValueNew(db);
+}
+
+/*
+** Extract a value from the supplied expression in the manner described
+** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
+** using valueNew().
+**
+** If pCtx is NULL and an error occurs after the sqlite3_value object
+** has been allocated, it is freed before returning. Or, if pCtx is not
+** NULL, it is assumed that the caller will free any allocated object
+** in all cases.
+*/
+static int valueFromExpr(
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal, /* Write the new value here */
+ struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
){
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
int negInt = 1;
const char *zNeg = "";
+ int rc = SQLITE_OK;
if( !pExpr ){
*ppVal = 0;
return SQLITE_OK;
}
- op = pExpr->op;
-
- /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
- ** The ifdef here is to enable us to achieve 100% branch test coverage even
- ** when SQLITE_ENABLE_STAT2 is omitted.
- */
-#ifdef SQLITE_ENABLE_STAT2
- if( op==TK_REGISTER ) op = pExpr->op2;
-#else
+ while( (op = pExpr->op)==TK_UPLUS ) pExpr = pExpr->pLeft;
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
-#endif
+
+ if( op==TK_CAST ){
+ u8 aff = sqlite3AffinityType(pExpr->u.zToken,0);
+ rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
+ testcase( rc!=SQLITE_OK );
+ if( *ppVal ){
+ sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8);
+ sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8);
+ }
+ return rc;
+ }
/* Handle negative integers in a single step. This is needed in the
** case when the value is -9223372036854775808.
@@ -57438,7 +62845,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
@@ -57446,7 +62853,6 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
if( zVal==0 ) goto no_mem;
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
- if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT;
}
if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
@@ -57455,24 +62861,26 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
if( enc!=SQLITE_UTF8 ){
- sqlite3VdbeChangeEncoding(pVal, enc);
+ rc = sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
- if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
+ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal)
+ && pVal!=0
+ ){
sqlite3VdbeMemNumerify(pVal);
- if( pVal->u.i==SMALLEST_INT64 ){
- pVal->flags &= MEM_Int;
- pVal->flags |= MEM_Real;
- pVal->r = (double)LARGEST_INT64;
+ if( pVal->flags & MEM_Real ){
+ pVal->u.r = -pVal->u.r;
+ }else if( pVal->u.i==SMALLEST_INT64 ){
+ pVal->u.r = -(double)SMALLEST_INT64;
+ MemSetTypeFlag(pVal, MEM_Real);
}else{
pVal->u.i = -pVal->u.i;
}
- pVal->r = -pVal->r;
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}else if( op==TK_NULL ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
@@ -57480,7 +62888,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
int nVal;
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
assert( pExpr->u.zToken[1]=='\'' );
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( !pVal ) goto no_mem;
zVal = &pExpr->u.zToken[2];
nVal = sqlite3Strlen30(zVal)-1;
@@ -57490,21 +62898,301 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
#endif
- if( pVal ){
- sqlite3VdbeMemStoreType(pVal);
- }
*ppVal = pVal;
- return SQLITE_OK;
+ return rc;
no_mem:
db->mallocFailed = 1;
sqlite3DbFree(db, zVal);
- sqlite3ValueFree(pVal);
- *ppVal = 0;
+ assert( *ppVal==0 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( pCtx==0 ) sqlite3ValueFree(pVal);
+#else
+ assert( pCtx==0 ); sqlite3ValueFree(pVal);
+#endif
return SQLITE_NOMEM;
}
/*
+** Create a new sqlite3_value object, containing the value of pExpr.
+**
+** This only works for very simple expressions that consist of one constant
+** token (i.e. "5", "5.1", "'a string'"). If the expression can
+** be converted directly into a value, then the value is allocated and
+** a pointer written to *ppVal. The caller is responsible for deallocating
+** the value by passing it to sqlite3ValueFree() later on. If the expression
+** cannot be converted to a value, then *ppVal is set to NULL.
+*/
+SQLITE_PRIVATE int sqlite3ValueFromExpr(
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* Write the new value here */
+){
+ return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0);
+}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** The implementation of the sqlite_record() function. This function accepts
+** a single argument of any type. The return value is a formatted database
+** record (a blob) containing the argument value.
+**
+** This is used to convert the value stored in the 'sample' column of the
+** sqlite_stat3 table to the record format SQLite uses internally.
+*/
+static void recordFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const int file_format = 1;
+ int iSerial; /* Serial type */
+ int nSerial; /* Bytes of space for iSerial as varint */
+ int nVal; /* Bytes of space required for argv[0] */
+ int nRet;
+ sqlite3 *db;
+ u8 *aRet;
+
+ UNUSED_PARAMETER( argc );
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format);
+ nSerial = sqlite3VarintLen(iSerial);
+ nVal = sqlite3VdbeSerialTypeLen(iSerial);
+ db = sqlite3_context_db_handle(context);
+
+ nRet = 1 + nSerial + nVal;
+ aRet = sqlite3DbMallocRaw(db, nRet);
+ if( aRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ aRet[0] = nSerial+1;
+ putVarint32(&aRet[1], iSerial);
+ sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial);
+ sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, aRet);
+ }
+}
+
+/*
+** Register built-in functions used to help read ANALYZE data.
+*/
+SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
+ static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = {
+ FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
+ };
+ int i;
+ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
+ FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs);
+ for(i=0; i<ArraySize(aAnalyzeTableFuncs); i++){
+ sqlite3FuncDefInsert(pHash, &aFunc[i]);
+ }
+}
+
+/*
+** Attempt to extract a value from pExpr and use it to construct *ppVal.
+**
+** If pAlloc is not NULL, then an UnpackedRecord object is created for
+** pAlloc if one does not exist and the new value is added to the
+** UnpackedRecord object.
+**
+** A value is extracted in the following cases:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The expression is a literal value.
+**
+** On success, *ppVal is made to point to the extracted value. The caller
+** is responsible for ensuring that the value is eventually freed.
+*/
+static int stat4ValueFromExpr(
+ Parse *pParse, /* Parse context */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */
+ sqlite3_value **ppVal /* OUT: New value object (or NULL) */
+){
+ int rc = SQLITE_OK;
+ sqlite3_value *pVal = 0;
+ sqlite3 *db = pParse->db;
+
+ /* Skip over any TK_COLLATE nodes */
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+
+ if( !pExpr ){
+ pVal = valueNew(db, pAlloc);
+ if( pVal ){
+ sqlite3VdbeMemSetNull((Mem*)pVal);
+ }
+ }else if( pExpr->op==TK_VARIABLE
+ || NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
+ ){
+ Vdbe *v;
+ int iBindVar = pExpr->iColumn;
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
+ if( (v = pParse->pReprepare)!=0 ){
+ pVal = valueNew(db, pAlloc);
+ if( pVal ){
+ rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
+ if( rc==SQLITE_OK ){
+ sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
+ }
+ pVal->db = pParse->db;
+ }
+ }
+ }else{
+ rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc);
+ }
+
+ assert( pVal==0 || pVal->db==db );
+ *ppVal = pVal;
+ return rc;
+}
+
+/*
+** This function is used to allocate and populate UnpackedRecord
+** structures intended to be compared against sample index keys stored
+** in the sqlite_stat4 table.
+**
+** A single call to this function attempts to populates field iVal (leftmost
+** is 0 etc.) of the unpacked record with a value extracted from expression
+** pExpr. Extraction of values is possible if:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The sqlite3ValueFromExpr() function is able to extract a value
+** from the expression (i.e. the expression is a literal value).
+**
+** If a value can be extracted, the affinity passed as the 5th argument
+** is applied to it before it is copied into the UnpackedRecord. Output
+** parameter *pbOk is set to true if a value is extracted, or false
+** otherwise.
+**
+** When this function is called, *ppRec must either point to an object
+** allocated by an earlier call to this function, or must be NULL. If it
+** is NULL and a value can be successfully extracted, a new UnpackedRecord
+** is allocated (and *ppRec set to point to it) before returning.
+**
+** Unless an error is encountered, SQLITE_OK is returned. It is not an
+** error if a value cannot be extracted from pExpr. If an error does
+** occur, an SQLite error code is returned.
+*/
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Index being probed */
+ UnpackedRecord **ppRec, /* IN/OUT: Probe record */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ int iVal, /* Array element to populate */
+ int *pbOk /* OUT: True if value was extracted */
+){
+ int rc;
+ sqlite3_value *pVal = 0;
+ struct ValueNewStat4Ctx alloc;
+
+ alloc.pParse = pParse;
+ alloc.pIdx = pIdx;
+ alloc.ppRec = ppRec;
+ alloc.iVal = iVal;
+
+ rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
+ assert( pVal==0 || pVal->db==pParse->db );
+ *pbOk = (pVal!=0);
+ return rc;
+}
+
+/*
+** Attempt to extract a value from expression pExpr using the methods
+** as described for sqlite3Stat4ProbeSetValue() above.
+**
+** If successful, set *ppVal to point to a new value object and return
+** SQLITE_OK. If no value can be extracted, but no other error occurs
+** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error
+** does occur, return an SQLite error code. The final value of *ppVal
+** is undefined in this case.
+*/
+SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(
+ Parse *pParse, /* Parse context */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* OUT: New value object (or NULL) */
+){
+ return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal);
+}
+
+/*
+** Extract the iCol-th column from the nRec-byte record in pRec. Write
+** the column value into *ppVal. If *ppVal is initially NULL then a new
+** sqlite3_value object is allocated.
+**
+** If *ppVal is initially NULL then the caller is responsible for
+** ensuring that the value written into *ppVal is eventually freed.
+*/
+SQLITE_PRIVATE int sqlite3Stat4Column(
+ sqlite3 *db, /* Database handle */
+ const void *pRec, /* Pointer to buffer containing record */
+ int nRec, /* Size of buffer pRec in bytes */
+ int iCol, /* Column to extract */
+ sqlite3_value **ppVal /* OUT: Extracted value */
+){
+ u32 t; /* a column type code */
+ int nHdr; /* Size of the header in the record */
+ int iHdr; /* Next unread header byte */
+ int iField; /* Next unread data byte */
+ int szField; /* Size of the current data field */
+ int i; /* Column index */
+ u8 *a = (u8*)pRec; /* Typecast byte array */
+ Mem *pMem = *ppVal; /* Write result into this Mem object */
+
+ assert( iCol>0 );
+ iHdr = getVarint32(a, nHdr);
+ if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
+ iField = nHdr;
+ for(i=0; i<=iCol; i++){
+ iHdr += getVarint32(&a[iHdr], t);
+ testcase( iHdr==nHdr );
+ testcase( iHdr==nHdr+1 );
+ if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT;
+ szField = sqlite3VdbeSerialTypeLen(t);
+ iField += szField;
+ }
+ testcase( iField==nRec );
+ testcase( iField==nRec+1 );
+ if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
+ if( pMem==0 ){
+ pMem = *ppVal = sqlite3ValueNew(db);
+ if( pMem==0 ) return SQLITE_NOMEM;
+ }
+ sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
+ pMem->enc = ENC(db);
+ return SQLITE_OK;
+}
+
+/*
+** Unless it is NULL, the argument must be an UnpackedRecord object returned
+** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes
+** the object.
+*/
+SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
+ if( pRec ){
+ int i;
+ int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
+ Mem *aMem = pRec->aMem;
+ sqlite3 *db = aMem[0].db;
+ for(i=0; i<nCol; i++){
+ if( aMem[i].szMalloc ) sqlite3DbFree(db, aMem[i].zMalloc);
+ }
+ sqlite3KeyInfoUnref(pRec->pKeyInfo);
+ sqlite3DbFree(db, pRec);
+ }
+}
+#endif /* ifdef SQLITE_ENABLE_STAT4 */
+
+/*
** Change the string value of an sqlite3_value object
*/
SQLITE_PRIVATE void sqlite3ValueSetStr(
@@ -57556,27 +63244,14 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
**
*************************************************************************
** This file contains code used for creating, destroying, and populating
-** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior
-** to version 2.8.7, all this code was combined into the vdbe.c source file.
-** But that file was getting too big so this subroutines were split out.
+** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)
*/
-
-
-/*
-** When debugging the code generator in a symbolic debugger, one can
-** set the sqlite3VdbeAddopTrace to 1 and all opcodes will be printed
-** as they are added to the instruction stream.
-*/
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3VdbeAddopTrace = 0;
-#endif
-
-
/*
** Create a new virtual database engine.
*/
-SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
+SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){
+ sqlite3 *db = pParse->db;
Vdbe *p;
p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
if( p==0 ) return 0;
@@ -57588,6 +63263,10 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
p->pPrev = 0;
db->pVdbe = p;
p->magic = VDBE_MAGIC_INIT;
+ p->pParse = pParse;
+ assert( pParse->aLabel==0 );
+ assert( pParse->nLabel==0 );
+ assert( pParse->nOpAlloc==0 );
return p;
}
@@ -57597,7 +63276,7 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
assert( isPrepareV2==1 || isPrepareV2==0 );
if( p==0 ) return;
-#ifdef SQLITE_OMIT_TRACE
+#if defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_ENABLE_SQLLOG)
if( !isPrepareV2 ) return;
#endif
assert( p->zSql==0 );
@@ -57634,35 +63313,55 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
pB->isPrepareV2 = pA->isPrepareV2;
}
-#ifdef SQLITE_DEBUG
/*
-** Turn tracing on or off
-*/
-SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe *p, FILE *trace){
- p->trace = trace;
-}
-#endif
-
-/*
-** Resize the Vdbe.aOp array so that it is at least one op larger than
-** it was.
+** Resize the Vdbe.aOp array so that it is at least nOp elements larger
+** than its current size. nOp is guaranteed to be less than or equal
+** to 1024/sizeof(Op).
**
** If an out-of-memory error occurs while resizing the array, return
-** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
+** SQLITE_NOMEM. In this case Vdbe.aOp and Parse.nOpAlloc remain
** unchanged (this is so that any opcodes already allocated can be
** correctly deallocated along with the rest of the Vdbe).
*/
-static int growOpArray(Vdbe *p){
+static int growOpArray(Vdbe *v, int nOp){
VdbeOp *pNew;
+ Parse *p = v->pParse;
+
+ /* The SQLITE_TEST_REALLOC_STRESS compile-time option is designed to force
+ ** more frequent reallocs and hence provide more opportunities for
+ ** simulated OOM faults. SQLITE_TEST_REALLOC_STRESS is generally used
+ ** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array
+ ** by the minimum* amount required until the size reaches 512. Normal
+ ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current
+ ** size of the op array or add 1KB of space, whichever is smaller. */
+#ifdef SQLITE_TEST_REALLOC_STRESS
+ int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp);
+#else
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
- pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
+ UNUSED_PARAMETER(nOp);
+#endif
+
+ assert( nOp<=(1024/sizeof(Op)) );
+ assert( nNew>=(p->nOpAlloc+nOp) );
+ pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
if( pNew ){
p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
- p->aOp = pNew;
+ v->aOp = pNew;
}
return (pNew ? SQLITE_OK : SQLITE_NOMEM);
}
+#ifdef SQLITE_DEBUG
+/* This routine is just a convenient place to set a breakpoint that will
+** fire after each opcode is inserted and displayed using
+** "PRAGMA vdbe_addoptrace=on".
+*/
+static void test_addop_breakpoint(void){
+ static int n = 0;
+ n++;
+}
+#endif
+
/*
** Add a new instruction to the list of instructions current in the
** VDBE. Return the address of the new instruction.
@@ -57686,8 +63385,8 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
i = p->nOp;
assert( p->magic==VDBE_MAGIC_INIT );
assert( op>0 && op<0xff );
- if( p->nOpAlloc<=i ){
- if( growOpArray(p) ){
+ if( p->pParse->nOpAlloc<=i ){
+ if( growOpArray(p, 1) ){
return 1;
}
}
@@ -57700,14 +63399,31 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOp->zComment = 0;
- if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+#endif
+#ifdef SQLITE_DEBUG
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ int jj, kk;
+ Parse *pParse = p->pParse;
+ for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){
+ struct yColCache *x = pParse->aColCache + jj;
+ if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue;
+ printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn);
+ kk++;
+ }
+ if( kk ) printf("\n");
+ sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+ test_addop_breakpoint();
+ }
#endif
#ifdef VDBE_PROFILE
pOp->cycles = 0;
pOp->cnt = 0;
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOp->iSrcLine = 0;
+#endif
return i;
}
SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){
@@ -57740,7 +63456,8 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4(
/*
** Add an OP_ParseSchema opcode. This routine is broken out from
-** sqlite3VdbeAddOp4() since it needs to also local all btrees.
+** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
+** as having been used.
**
** The zWhere string must have been obtained from sqlite3_malloc().
** This routine will take ownership of the allocated memory.
@@ -57782,15 +63499,13 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
**
** Zero is returned if a malloc() fails.
*/
-SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
- int i;
- i = p->nLabel++;
- assert( p->magic==VDBE_MAGIC_INIT );
- if( i>=p->nLabelAlloc ){
- int n = p->nLabelAlloc*2 + 5;
- p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
- n*sizeof(p->aLabel[0]));
- p->nLabelAlloc = sqlite3DbMallocSize(p->db, p->aLabel)/sizeof(p->aLabel[0]);
+SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
+ Parse *p = v->pParse;
+ int i = p->nLabel++;
+ assert( v->magic==VDBE_MAGIC_INIT );
+ if( (i & (i-1))==0 ){
+ p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
+ (i*2+1)*sizeof(p->aLabel[0]));
}
if( p->aLabel ){
p->aLabel[i] = -1;
@@ -57803,13 +63518,15 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
** be inserted. The parameter "x" must have been obtained from
** a prior call to sqlite3VdbeMakeLabel().
*/
-SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){
+SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
+ Parse *p = v->pParse;
int j = -1-x;
- assert( p->magic==VDBE_MAGIC_INIT );
- assert( j>=0 && j<p->nLabel );
- if( p->aLabel ){
- p->aLabel[j] = p->nOp;
+ assert( v->magic==VDBE_MAGIC_INIT );
+ assert( j<p->nLabel );
+ if( ALWAYS(j>=0) && p->aLabel ){
+ p->aLabel[j] = v->nOp;
}
+ p->iFixedOp = v->nOp - 1;
}
/*
@@ -57923,7 +63640,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
|| (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1)
#endif
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
- && (pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
+ && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
){
hasAbort = 1;
break;
@@ -57931,7 +63648,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
}
sqlite3DbFree(v->db, sIter.apSub);
- /* Return true if hasAbort==mayAbort. Or if a malloc failure occured.
+ /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred.
** If malloc failed, then the while() loop above may not have iterated
** through all opcodes and hasAbort may be set incorrectly. Return
** true for this case to prevent the assert() in the callers frame
@@ -57957,37 +63674,79 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i;
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
- int *aLabel = p->aLabel;
+ Parse *pParse = p->pParse;
+ int *aLabel = pParse->aLabel;
p->readOnly = 1;
+ p->bIsReader = 0;
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
- pOp->opflags = sqlite3OpcodeProperty[opcode];
- if( opcode==OP_Function || opcode==OP_AggStep ){
- if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
- }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
- p->readOnly = 0;
+ /* NOTE: Be sure to update mkopcodeh.awk when adding or removing
+ ** cases from this switch! */
+ switch( opcode ){
+ case OP_Function:
+ case OP_AggStep: {
+ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
+ break;
+ }
+ case OP_Transaction: {
+ if( pOp->p2!=0 ) p->readOnly = 0;
+ /* fall thru */
+ }
+ case OP_AutoCommit:
+ case OP_Savepoint: {
+ p->bIsReader = 1;
+ break;
+ }
+#ifndef SQLITE_OMIT_WAL
+ case OP_Checkpoint:
+#endif
+ case OP_Vacuum:
+ case OP_JournalMode: {
+ p->readOnly = 0;
+ p->bIsReader = 1;
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate ){
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
- }else if( opcode==OP_VFilter ){
- int n;
- assert( p->nOp - i >= 3 );
- assert( pOp[-1].opcode==OP_Integer );
- n = pOp[-1].p1;
- if( n>nMaxArgs ) nMaxArgs = n;
+ case OP_VUpdate: {
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ break;
+ }
+ case OP_VFilter: {
+ int n;
+ assert( p->nOp - i >= 3 );
+ assert( pOp[-1].opcode==OP_Integer );
+ n = pOp[-1].p1;
+ if( n>nMaxArgs ) nMaxArgs = n;
+ break;
+ }
#endif
+ case OP_Next:
+ case OP_NextIfOpen:
+ case OP_SorterNext: {
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
+ case OP_Prev:
+ case OP_PrevIfOpen: {
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
}
+ pOp->opflags = sqlite3OpcodeProperty[opcode];
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
- assert( -1-pOp->p2<p->nLabel );
+ assert( -1-pOp->p2<pParse->nLabel );
pOp->p2 = aLabel[-1-pOp->p2];
}
}
- sqlite3DbFree(p->db, p->aLabel);
- p->aLabel = 0;
-
+ sqlite3DbFree(p->db, pParse->aLabel);
+ pParse->aLabel = 0;
+ pParse->nLabel = 0;
*pMaxFuncArgs = nMaxArgs;
+ assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) );
}
/*
@@ -58014,7 +63773,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg)
assert( aOp && !p->db->mallocFailed );
/* Check that sqlite3VdbeUsesBtree() was not called on this VM */
- assert( p->btreeMask==0 );
+ assert( DbMaskAllZero(p->btreeMask) );
resolveP2Values(p, pnMaxArg);
*pnOp = p->nOp;
@@ -58026,10 +63785,10 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg)
** Add a whole list of operations to the operation stack. Return the
** address of the first operation added.
*/
-SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
+SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
int addr;
assert( p->magic==VDBE_MAGIC_INIT );
- if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){
+ if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
addr = p->nOp;
@@ -58041,7 +63800,8 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp)
VdbeOp *pOut = &p->aOp[i+addr];
pOut->opcode = pIn->opcode;
pOut->p1 = pIn->p1;
- if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){
+ if( p2<0 ){
+ assert( sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP );
pOut->p2 = addr + ADDR(p2);
}else{
pOut->p2 = p2;
@@ -58050,9 +63810,16 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp)
pOut->p4type = P4_NOTUSED;
pOut->p4.p = 0;
pOut->p5 = 0;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOut->zComment = 0;
- if( sqlite3VdbeAddopTrace ){
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOut->iSrcLine = iLineno+i;
+#else
+ (void)iLineno;
+#endif
+#ifdef SQLITE_DEBUG
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
}
#endif
@@ -58068,10 +63835,9 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp)
** static array using sqlite3VdbeAddOpList but we want to make a
** few minor changes to the program.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
assert( p!=0 );
- assert( addr>=0 );
- if( p->nOp>addr ){
+ if( ((u32)p->nOp)>addr ){
p->aOp[addr].p1 = val;
}
}
@@ -58080,10 +63846,9 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
** Change the value of the P2 operand for a specific instruction.
** This routine is useful for setting a jump destination.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
assert( p!=0 );
- assert( addr>=0 );
- if( p->nOp>addr ){
+ if( ((u32)p->nOp)>addr ){
p->aOp[addr].p2 = val;
}
}
@@ -58091,10 +63856,9 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
/*
** Change the value of the P3 operand for a specific instruction.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
assert( p!=0 );
- assert( addr>=0 );
- if( p->nOp>addr ){
+ if( ((u32)p->nOp)>addr ){
p->aOp[addr].p3 = val;
}
}
@@ -58116,8 +63880,8 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- assert( addr>=0 );
sqlite3VdbeChangeP2(p, addr, p->nOp);
+ p->pParse->iFixedOp = p->nOp - 1;
}
@@ -58126,7 +63890,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
** the FuncDef is not ephermal, then do nothing.
*/
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
- if( ALWAYS(pDef) && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){
+ if( ALWAYS(pDef) && (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
sqlite3DbFree(db, pDef);
}
}
@@ -58143,21 +63907,16 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
case P4_REAL:
case P4_INT64:
case P4_DYNAMIC:
- case P4_KEYINFO:
- case P4_INTARRAY:
- case P4_KEYINFO_HANDOFF: {
+ case P4_INTARRAY: {
sqlite3DbFree(db, p4);
break;
}
- case P4_MPRINTF: {
- if( db->pnBytesFreed==0 ) sqlite3_free(p4);
+ case P4_KEYINFO: {
+ if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
break;
}
- case P4_VDBEFUNC: {
- VdbeFunc *pVdbeFunc = (VdbeFunc *)p4;
- freeEphemeralFunction(db, pVdbeFunc->pFunc);
- if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
- sqlite3DbFree(db, pVdbeFunc);
+ case P4_MPRINTF: {
+ if( db->pnBytesFreed==0 ) sqlite3_free(p4);
break;
}
case P4_FUNCDEF: {
@@ -58169,7 +63928,7 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
sqlite3ValueFree((sqlite3_value*)p4);
}else{
Mem *p = (Mem*)p4;
- sqlite3DbFree(db, p->zMalloc);
+ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
sqlite3DbFree(db, p);
}
break;
@@ -58192,7 +63951,7 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
Op *pOp;
for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
freeP4(db, pOp->p4type, pOp->p4.p);
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
sqlite3DbFree(db, pOp->zComment);
#endif
}
@@ -58211,18 +63970,29 @@ SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
}
/*
-** Change N opcodes starting at addr to No-ops.
+** Change the opcode at addr into OP_Noop
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
- if( p->aOp ){
+SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
+ if( addr<p->nOp ){
VdbeOp *pOp = &p->aOp[addr];
sqlite3 *db = p->db;
- while( N-- ){
- freeP4(db, pOp->p4type, pOp->p4.p);
- memset(pOp, 0, sizeof(pOp[0]));
- pOp->opcode = OP_Noop;
- pOp++;
- }
+ freeP4(db, pOp->p4type, pOp->p4.p);
+ memset(pOp, 0, sizeof(pOp[0]));
+ pOp->opcode = OP_Noop;
+ if( addr==p->nOp-1 ) p->nOp--;
+ }
+}
+
+/*
+** If the last opcode is "op" and it is not a jump destination,
+** then remove it. Return true if and only if an opcode was removed.
+*/
+SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
+ if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){
+ sqlite3VdbeChangeToNoop(p, p->nOp-1);
+ return 1;
+ }else{
+ return 0;
}
}
@@ -58236,14 +64006,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr, int N){
** the string is made into memory obtained from sqlite3_malloc().
** A value of n==0 means copy bytes of zP4 up to and including the
** first null byte. If n>0 then copy n+1 bytes of zP4.
-**
-** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure.
-** A copy is made of the KeyInfo structure into memory obtained from
-** sqlite3_malloc, to be freed when the Vdbe is finalized.
-** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure
-** stored in memory that the caller has obtained from sqlite3_malloc. The
-** caller should not free the allocation, it will be freed when the Vdbe is
-** finalized.
**
** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points
** to a string or structure that is guaranteed to exist for the lifetime of
@@ -58258,7 +64020,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
db = p->db;
assert( p->magic==VDBE_MAGIC_INIT );
if( p->aOp==0 || db->mallocFailed ){
- if ( n!=P4_KEYINFO && n!=P4_VTAB ) {
+ if( n!=P4_VTAB ){
freeP4(db, n, (void*)*(char**)&zP4);
}
return;
@@ -58269,6 +64031,9 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
addr = p->nOp - 1;
}
pOp = &p->aOp[addr];
+ assert( pOp->p4type==P4_NOTUSED
+ || pOp->p4type==P4_INT32
+ || pOp->p4type==P4_KEYINFO );
freeP4(db, pOp->p4type, pOp->p4.p);
pOp->p4.p = 0;
if( n==P4_INT32 ){
@@ -58280,27 +64045,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
}else if( n==P4_KEYINFO ){
- KeyInfo *pKeyInfo;
- int nField, nByte;
-
- nField = ((KeyInfo*)zP4)->nField;
- nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField;
- pKeyInfo = sqlite3DbMallocRaw(0, nByte);
- pOp->p4.pKeyInfo = pKeyInfo;
- if( pKeyInfo ){
- u8 *aSortOrder;
- memcpy((char*)pKeyInfo, zP4, nByte - nField);
- aSortOrder = pKeyInfo->aSortOrder;
- if( aSortOrder ){
- pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField];
- memcpy(pKeyInfo->aSortOrder, aSortOrder, nField);
- }
- pOp->p4type = P4_KEYINFO;
- }else{
- p->db->mallocFailed = 1;
- pOp->p4type = P4_NOTUSED;
- }
- }else if( n==P4_KEYINFO_HANDOFF ){
pOp->p4.p = (void*)zP4;
pOp->p4type = P4_KEYINFO;
}else if( n==P4_VTAB ){
@@ -58318,42 +64062,62 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
}
}
-#ifndef NDEBUG
/*
-** Change the comment on the the most recently coded instruction. Or
+** Set the P4 on the most recently added opcode to the KeyInfo for the
+** index given.
+*/
+SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ assert( pIdx!=0 );
+ sqlite3VdbeChangeP4(v, -1, (char*)sqlite3KeyInfoOfIndex(pParse, pIdx),
+ P4_KEYINFO);
+}
+
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+/*
+** Change the comment on the most recently coded instruction. Or
** insert a No-op and add the comment to that new instruction. This
** makes the code easier to read during debugging. None of this happens
** in a production build.
*/
-SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
- va_list ap;
- if( !p ) return;
+static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){
assert( p->nOp>0 || p->aOp==0 );
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
if( p->nOp ){
- char **pz = &p->aOp[p->nOp-1].zComment;
+ assert( p->aOp );
+ sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment);
+ p->aOp[p->nOp-1].zComment = sqlite3VMPrintf(p->db, zFormat, ap);
+ }
+}
+SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
+ va_list ap;
+ if( p ){
va_start(ap, zFormat);
- sqlite3DbFree(p->db, *pz);
- *pz = sqlite3VMPrintf(p->db, zFormat, ap);
+ vdbeVComment(p, zFormat, ap);
va_end(ap);
}
}
SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
va_list ap;
- if( !p ) return;
- sqlite3VdbeAddOp0(p, OP_Noop);
- assert( p->nOp>0 || p->aOp==0 );
- assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
- if( p->nOp ){
- char **pz = &p->aOp[p->nOp-1].zComment;
+ if( p ){
+ sqlite3VdbeAddOp0(p, OP_Noop);
va_start(ap, zFormat);
- sqlite3DbFree(p->db, *pz);
- *pz = sqlite3VMPrintf(p->db, zFormat, ap);
+ vdbeVComment(p, zFormat, ap);
va_end(ap);
}
}
#endif /* NDEBUG */
+#ifdef SQLITE_VDBE_COVERAGE
+/*
+** Set the value if the iSrcLine field for the previously coded instruction.
+*/
+SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){
+ sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine;
+}
+#endif /* SQLITE_VDBE_COVERAGE */
+
/*
** Return the opcode for a given address. If the address is -1, then
** return the most recently inserted opcode.
@@ -58362,28 +64126,17 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
** routine, then a pointer to a dummy VdbeOp will be returned. That opcode
** is readable but not writable, though it is cast to a writable value.
** The return of a dummy opcode allows the call to continue functioning
-** after a OOM fault without having to check to see if the return from
+** after an OOM fault without having to check to see if the return from
** this routine is a valid pointer. But because the dummy.opcode is 0,
** dummy will never be written to. This is verified by code inspection and
** by running with Valgrind.
-**
-** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called
-** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE,
-** an OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as
-** a new VDBE is created. So we are free to set addr to p->nOp-1 without
-** having to double-check to make sure that the result is non-negative. But
-** if SQLITE_OMIT_TRACE is defined, the OP_Trace is omitted and we do need to
-** check the value of p->nOp-1 before continuing.
*/
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
** zeros, which is correct. MSVC generates a warning, nevertheless. */
- static const VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
+ static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->magic==VDBE_MAGIC_INIT );
if( addr<0 ){
-#ifdef SQLITE_OMIT_TRACE
- if( p->nOp==0 ) return (VdbeOp*)&dummy;
-#endif
addr = p->nOp - 1;
}
assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
@@ -58394,6 +64147,97 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
}
}
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
+/*
+** Return an integer value for one of the parameters to the opcode pOp
+** determined by character c.
+*/
+static int translateP(char c, const Op *pOp){
+ if( c=='1' ) return pOp->p1;
+ if( c=='2' ) return pOp->p2;
+ if( c=='3' ) return pOp->p3;
+ if( c=='4' ) return pOp->p4.i;
+ return pOp->p5;
+}
+
+/*
+** Compute a string for the "comment" field of a VDBE opcode listing.
+**
+** The Synopsis: field in comments in the vdbe.c source file gets converted
+** to an extra string that is appended to the sqlite3OpcodeName(). In the
+** absence of other comments, this synopsis becomes the comment on the opcode.
+** Some translation occurs:
+**
+** "PX" -> "r[X]"
+** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1
+** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
+** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
+*/
+static int displayComment(
+ const Op *pOp, /* The opcode to be commented */
+ const char *zP4, /* Previously obtained value for P4 */
+ char *zTemp, /* Write result here */
+ int nTemp /* Space available in zTemp[] */
+){
+ const char *zOpName;
+ const char *zSynopsis;
+ int nOpName;
+ int ii, jj;
+ zOpName = sqlite3OpcodeName(pOp->opcode);
+ nOpName = sqlite3Strlen30(zOpName);
+ if( zOpName[nOpName+1] ){
+ int seenCom = 0;
+ char c;
+ zSynopsis = zOpName += nOpName + 1;
+ for(ii=jj=0; jj<nTemp-1 && (c = zSynopsis[ii])!=0; ii++){
+ if( c=='P' ){
+ c = zSynopsis[++ii];
+ if( c=='4' ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", zP4);
+ }else if( c=='X' ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", pOp->zComment);
+ seenCom = 1;
+ }else{
+ int v1 = translateP(c, pOp);
+ int v2;
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1);
+ if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){
+ ii += 3;
+ jj += sqlite3Strlen30(zTemp+jj);
+ v2 = translateP(zSynopsis[ii], pOp);
+ if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){
+ ii += 2;
+ v2++;
+ }
+ if( v2>1 ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1);
+ }
+ }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){
+ ii += 4;
+ }
+ }
+ jj += sqlite3Strlen30(zTemp+jj);
+ }else{
+ zTemp[jj++] = c;
+ }
+ }
+ if( !seenCom && jj<nTemp-5 && pOp->zComment ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "; %s", pOp->zComment);
+ jj += sqlite3Strlen30(zTemp+jj);
+ }
+ if( jj<nTemp ) zTemp[jj] = 0;
+ }else if( pOp->zComment ){
+ sqlite3_snprintf(nTemp, zTemp, "%s", pOp->zComment);
+ jj = sqlite3Strlen30(zTemp);
+ }else{
+ zTemp[0] = 0;
+ jj = 0;
+ }
+ return jj;
+}
+#endif /* SQLITE_DEBUG */
+
+
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
|| defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
/*
@@ -58404,30 +64248,30 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
char *zP4 = zTemp;
assert( nTemp>=20 );
switch( pOp->p4type ){
- case P4_KEYINFO_STATIC:
case P4_KEYINFO: {
int i, j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
- sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField);
+ assert( pKeyInfo->aSortOrder!=0 );
+ sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField);
i = sqlite3Strlen30(zTemp);
for(j=0; j<pKeyInfo->nField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
- if( pColl ){
- int n = sqlite3Strlen30(pColl->zName);
- if( i+n>nTemp-6 ){
- memcpy(&zTemp[i],",...",4);
- break;
- }
- zTemp[i++] = ',';
- if( pKeyInfo->aSortOrder && pKeyInfo->aSortOrder[j] ){
- zTemp[i++] = '-';
- }
- memcpy(&zTemp[i], pColl->zName,n+1);
- i += n;
- }else if( i+4<nTemp-6 ){
- memcpy(&zTemp[i],",nil",4);
- i += 4;
+ const char *zColl = pColl ? pColl->zName : "nil";
+ int n = sqlite3Strlen30(zColl);
+ if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
+ zColl = "B";
+ n = 1;
+ }
+ if( i+n>nTemp-6 ){
+ memcpy(&zTemp[i],",...",4);
+ break;
}
+ zTemp[i++] = ',';
+ if( pKeyInfo->aSortOrder[j] ){
+ zTemp[i++] = '-';
+ }
+ memcpy(&zTemp[i], zColl, n+1);
+ i += n;
}
zTemp[i++] = ')';
zTemp[i] = 0;
@@ -58436,7 +64280,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
- sqlite3_snprintf(nTemp, zTemp, "collseq(%.20s)", pColl->zName);
+ sqlite3_snprintf(nTemp, zTemp, "(%.20s)", pColl->zName);
break;
}
case P4_FUNCDEF: {
@@ -58458,13 +64302,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
case P4_MEM: {
Mem *pMem = pOp->p4.pMem;
- assert( (pMem->flags & MEM_Null)==0 );
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
- sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
+ sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->u.r);
+ }else if( pMem->flags & MEM_Null ){
+ sqlite3_snprintf(nTemp, zTemp, "NULL");
}else{
assert( pMem->flags & MEM_Blob );
zP4 = "(blob)";
@@ -58486,6 +64331,10 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
sqlite3_snprintf(nTemp, zTemp, "program");
break;
}
+ case P4_ADVANCE: {
+ zTemp[0] = 0;
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
@@ -58503,15 +64352,16 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**
** The prepared statements need to know in advance the complete set of
-** attached databases that they will be using. A mask of these databases
-** is maintained in p->btreeMask and is used for locking and other purposes.
+** attached databases that will be use. A mask of these databases
+** is maintained in p->btreeMask. The p->lockMask value is the subset of
+** p->btreeMask of databases that will require a lock.
*/
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){
assert( i>=0 && i<p->db->nDb && i<(int)sizeof(yDbMask)*8 );
assert( i<(int)sizeof(p->btreeMask)*8 );
- p->btreeMask |= ((yDbMask)1)<<i;
+ DbMaskSet(p->btreeMask, i);
if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){
- p->lockMask |= ((yDbMask)1)<<i;
+ DbMaskSet(p->lockMask, i);
}
}
@@ -58539,16 +64389,15 @@ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){
*/
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){
int i;
- yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
- if( p->lockMask==0 ) return; /* The common case */
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
- for(i=0, mask=1; i<nDb; i++, mask += mask){
- if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
+ for(i=0; i<nDb; i++){
+ if( i!=1 && DbMaskTest(p->lockMask,i) && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeEnter(aDb[i].pBt);
}
}
@@ -58561,16 +64410,15 @@ SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){
*/
SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
int i;
- yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
- if( p->lockMask==0 ) return; /* The common case */
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
- for(i=0, mask=1; i<nDb; i++, mask += mask){
- if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
+ for(i=0; i<nDb; i++){
+ if( i!=1 && DbMaskTest(p->lockMask,i) && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeLeave(aDb[i].pBt);
}
}
@@ -58584,16 +64432,21 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
char *zP4;
char zPtr[50];
- static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n";
+ char zCom[100];
+ static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
if( pOut==0 ) pOut = stdout;
zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
- fprintf(pOut, zFormat1, pc,
- sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
-#ifdef SQLITE_DEBUG
- pOp->zComment ? pOp->zComment : ""
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ displayComment(pOp, zP4, zCom, sizeof(zCom));
#else
- ""
+ zCom[0] = 0;
#endif
+ /* NB: The sqlite3OpcodeName() function is implemented by code created
+ ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
+ ** information from the vdbe.c source text */
+ fprintf(pOut, zFormat1, pc,
+ sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
+ zCom
);
fflush(pOut);
}
@@ -58604,17 +64457,18 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
*/
static void releaseMemArray(Mem *p, int N){
if( p && N ){
- Mem *pEnd;
+ Mem *pEnd = &p[N];
sqlite3 *db = p->db;
u8 malloc_failed = db->mallocFailed;
if( db->pnBytesFreed ){
- for(pEnd=&p[N]; p<pEnd; p++){
- sqlite3DbFree(db, p->zMalloc);
- }
+ do{
+ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
+ }while( (++p)<pEnd );
return;
}
- for(pEnd=&p[N]; p<pEnd; p++){
+ do{
assert( (&p[1])==pEnd || p[0].db==p[1].db );
+ assert( sqlite3VdbeCheckMemInvariants(p) );
/* This block is really an inlined version of sqlite3VdbeMemRelease()
** that takes advantage of the fact that the memory cell value is
@@ -58628,15 +64482,19 @@ static void releaseMemArray(Mem *p, int N){
** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction.
*/
+ testcase( p->flags & MEM_Agg );
+ testcase( p->flags & MEM_Dyn );
+ testcase( p->flags & MEM_Frame );
+ testcase( p->flags & MEM_RowSet );
if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
sqlite3VdbeMemRelease(p);
- }else if( p->zMalloc ){
+ }else if( p->szMalloc ){
sqlite3DbFree(db, p->zMalloc);
- p->zMalloc = 0;
+ p->szMalloc = 0;
}
- p->flags = MEM_Null;
- }
+ p->flags = MEM_Undefined;
+ }while( (++p)<pEnd );
db->mallocFailed = malloc_failed;
}
}
@@ -58682,7 +64540,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
sqlite3 *db = p->db; /* The database connection */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
- Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */
+ Mem *pMem = &p->aMem[1]; /* First Mem of result set */
assert( p->explain );
assert( p->magic==VDBE_MAGIC_RUN );
@@ -58693,6 +64551,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
releaseMemArray(pMem, 8);
+ p->pResultSet = 0;
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -58738,7 +64597,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
}else{
- char *z;
+ char *zP4;
Op *pOp;
if( i<p->nOp ){
/* The output line number is small enough that we are still in the
@@ -58756,15 +64615,13 @@ SQLITE_PRIVATE int sqlite3VdbeList(
}
if( p->explain==1 ){
pMem->flags = MEM_Int;
- pMem->type = SQLITE_INTEGER;
pMem->u.i = i; /* Program counter */
pMem++;
pMem->flags = MEM_Static|MEM_Str|MEM_Term;
- pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
+ pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
- pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8;
pMem++;
@@ -58779,7 +64636,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
for(j=0; j<nSub; j++){
if( apSub[j]==pOp->p4.pProgram ) break;
}
- if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){
+ if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, nSub!=0) ){
apSub = (SubProgram **)pSub->z;
apSub[nSub++] = pOp->p4.pProgram;
pSub->flags |= MEM_Blob;
@@ -58790,63 +64647,57 @@ SQLITE_PRIVATE int sqlite3VdbeList(
pMem->flags = MEM_Int;
pMem->u.i = pOp->p1; /* P1 */
- pMem->type = SQLITE_INTEGER;
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p2; /* P2 */
- pMem->type = SQLITE_INTEGER;
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
- pMem->type = SQLITE_INTEGER;
pMem++;
- if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
+ if( sqlite3VdbeMemClearAndResize(pMem, 32) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
- pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
- z = displayP4(pOp, pMem->z, 32);
- if( z!=pMem->z ){
- sqlite3VdbeMemSetStr(pMem, z, -1, SQLITE_UTF8, 0);
+ pMem->flags = MEM_Str|MEM_Term;
+ zP4 = displayP4(pOp, pMem->z, 32);
+ if( zP4!=pMem->z ){
+ sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
}
- pMem->type = SQLITE_TEXT;
pMem++;
if( p->explain==1 ){
- if( sqlite3VdbeMemGrow(pMem, 4, 0) ){
+ if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
- pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
+ pMem->flags = MEM_Str|MEM_Term;
pMem->n = 2;
sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
- pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8;
pMem++;
-#ifdef SQLITE_DEBUG
- if( pOp->zComment ){
- pMem->flags = MEM_Str|MEM_Term;
- pMem->z = pOp->zComment;
- pMem->n = sqlite3Strlen30(pMem->z);
- pMem->enc = SQLITE_UTF8;
- pMem->type = SQLITE_TEXT;
- }else
-#endif
- {
- pMem->flags = MEM_Null; /* Comment */
- pMem->type = SQLITE_NULL;
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
+ assert( p->db->mallocFailed );
+ return SQLITE_ERROR;
}
+ pMem->flags = MEM_Str|MEM_Term;
+ pMem->n = displayComment(pOp, zP4, pMem->z, 500);
+ pMem->enc = SQLITE_UTF8;
+#else
+ pMem->flags = MEM_Null; /* Comment */
+#endif
}
p->nResColumn = 8 - 4*(p->explain-1);
+ p->pResultSet = &p->aMem[1];
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
}
@@ -58859,15 +64710,17 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** Print the SQL that was used to generate a VDBE program.
*/
SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe *p){
- int nOp = p->nOp;
- VdbeOp *pOp;
- if( nOp<1 ) return;
- pOp = &p->aOp[0];
- if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
- const char *z = pOp->p4.z;
- while( sqlite3Isspace(*z) ) z++;
- printf("SQL: [%s]\n", z);
+ const char *z = 0;
+ if( p->zSql ){
+ z = p->zSql;
+ }else if( p->nOp>=1 ){
+ const VdbeOp *pOp = &p->aOp[0];
+ if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){
+ z = pOp->p4.z;
+ while( sqlite3Isspace(*z) ) z++;
+ }
}
+ if( z ) printf("SQL: [%s]\n", z);
}
#endif
@@ -58881,7 +64734,7 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){
if( sqlite3IoTrace==0 ) return;
if( nOp<1 ) return;
pOp = &p->aOp[0];
- if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
+ if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){
int i, j;
char z[1000];
sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z);
@@ -58984,13 +64837,13 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
/*
** Prepare a virtual machine for execution for the first time after
** creating the virtual machine. This involves things such
-** as allocating stack space and initializing the program counter.
+** as allocating registers and initializing the program counter.
** After the VDBE has be prepped, it can be executed by one or more
** calls to sqlite3VdbeExec().
**
-** This function may be called exact once on a each virtual machine.
+** This function may be called exactly once on each virtual machine.
** After this routine is called the VM has been "packaged" and is ready
-** to run. After this routine is called, futher calls to
+** to run. After this routine is called, further calls to
** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects
** the Vdbe from the Parse object that helped generate it so that the
** the Vdbe becomes an independent entity and the Parse object can be
@@ -59008,6 +64861,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
int nMem; /* Number of VM memory registers */
int nCursor; /* Number of cursors required */
int nArg; /* Number of arguments in subprograms */
+ int nOnce; /* Number of OP_Once instructions */
int n; /* Loop counter */
u8 *zCsr; /* Memory available for allocation */
u8 *zEnd; /* First byte past allocated memory */
@@ -59017,12 +64871,15 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
assert( p->nOp>0 );
assert( pParse!=0 );
assert( p->magic==VDBE_MAGIC_INIT );
+ assert( pParse==p->pParse );
db = p->db;
assert( db->mallocFailed==0 );
nVar = pParse->nVar;
nMem = pParse->nMem;
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
+ nOnce = pParse->nOnce;
+ if( nOnce==0 ) nOnce = 1; /* Ensure at least one byte in p->aOnceFlag[] */
/* For each cursor required, also allocate a memory cell. Memory
** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
@@ -59038,8 +64895,8 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
/* Allocate space for memory registers, SQL variables, VDBE cursors and
** an array to marshal SQL function arguments in.
*/
- zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
- zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
+ zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
+ zEnd = (u8*)&p->aOp[pParse->nOpAlloc]; /* First byte past end of zCsr[] */
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
@@ -59069,6 +64926,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
&zCsr, zEnd, &nByte);
+ p->aOnceFlag = allocSpace(p->aOnceFlag, nOnce, &zCsr, zEnd, &nByte);
if( nByte ){
p->pFree = sqlite3DbMallocZero(db, nByte);
}
@@ -59076,7 +64934,8 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
zEnd = &zCsr[nByte];
}while( nByte && !db->mallocFailed );
- p->nCursor = (u16)nCursor;
+ p->nCursor = nCursor;
+ p->nOnceFlag = nOnce;
if( p->aVar ){
p->nVar = (ynVar)nVar;
for(n=0; n<nVar; n++){
@@ -59093,7 +64952,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->aMem--; /* aMem[] goes from 1..nMem */
p->nMem = nMem; /* not from 0..nMem-1 */
for(n=1; n<=nMem; n++){
- p->aMem[n].flags = MEM_Null;
+ p->aMem[n].flags = MEM_Undefined;
p->aMem[n].db = db;
}
}
@@ -59109,6 +64968,7 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
if( pCx==0 ){
return;
}
+ sqlite3VdbeSorterClose(p->db, pCx);
if( pCx->pBt ){
sqlite3BtreeClose(pCx->pBt);
/* The pCx->pCursor will be close automatically, if it exists, by
@@ -59117,9 +64977,9 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
sqlite3BtreeCloseCursor(pCx->pCursor);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( pCx->pVtabCursor ){
+ else if( pCx->pVtabCursor ){
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
- const sqlite3_module *pModule = pCx->pModule;
+ const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
p->inVtabMethod = 1;
pModule->xClose(pVtabCursor);
p->inVtabMethod = 0;
@@ -59134,6 +64994,8 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
*/
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
+ v->aOnceFlag = pFrame->aOnceFlag;
+ v->nOnceFlag = pFrame->nOnceFlag;
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -59158,9 +65020,10 @@ static void closeAllCursors(Vdbe *p){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
+ p->pFrame = 0;
+ p->nFrame = 0;
}
- p->pFrame = 0;
- p->nFrame = 0;
+ assert( p->nFrame==0 );
if( p->apCsr ){
int i;
@@ -59180,14 +65043,14 @@ static void closeAllCursors(Vdbe *p){
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}
+
+ /* Delete any auxdata allocations made by the VM */
+ if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0);
+ assert( p->pAuxData==0 );
}
/*
-** Clean up the VM after execution.
-**
-** This routine will automatically close any cursors, lists, and/or
-** sorters that were left open. It also deletes the values of
-** variables in the aVar[] array.
+** Clean up the VM after a single run.
*/
static void Cleanup(Vdbe *p){
sqlite3 *db = p->db;
@@ -59196,8 +65059,10 @@ static void Cleanup(Vdbe *p){
/* Execute assert() statements to ensure that the Vdbe.apCsr[] and
** Vdbe.aMem[] arrays have already been cleaned up. */
int i;
- for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
- for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
+ if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
+ if( p->aMem ){
+ for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
+ }
#endif
sqlite3DbFree(db, p->zErrMsg);
@@ -59286,7 +65151,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
- rc = sqlite3VtabSync(db, &p->zErrMsg);
+ rc = sqlite3VtabSync(db, p);
/* This loop determines (a) if the commit hook should be invoked and
** (b) how many database files have open write transactions, not
@@ -59299,7 +65164,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( sqlite3BtreeIsInTrans(pBt) ){
needXcommit = 1;
if( i!=1 ) nTrans++;
+ sqlite3BtreeEnter(pBt);
rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
+ sqlite3BtreeLeave(pBt);
}
}
if( rc!=SQLITE_OK ){
@@ -59310,7 +65177,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( needXcommit && db->xCommitCallback ){
rc = db->xCommitCallback(db->pCommitArg);
if( rc ){
- return SQLITE_CONSTRAINT;
+ return SQLITE_CONSTRAINT_COMMITHOOK;
}
}
@@ -59351,7 +65218,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* The complex case - There is a multi-file write-transaction active.
** This requires a master journal file to ensure the transaction is
- ** committed atomicly.
+ ** committed atomically.
*/
#ifndef SQLITE_OMIT_DISKIO
else{
@@ -59362,16 +65229,31 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
sqlite3_file *pMaster = 0;
i64 offset = 0;
int res;
+ int retryCount = 0;
+ int nMainFile;
/* Select a master journal file name */
+ nMainFile = sqlite3Strlen30(zMainFile);
+ zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile);
+ if( zMaster==0 ) return SQLITE_NOMEM;
do {
u32 iRandom;
- sqlite3DbFree(db, zMaster);
- sqlite3_randomness(sizeof(iRandom), &iRandom);
- zMaster = sqlite3MPrintf(db, "%s-mj%08X", zMainFile, iRandom&0x7fffffff);
- if( !zMaster ){
- return SQLITE_NOMEM;
+ if( retryCount ){
+ if( retryCount>100 ){
+ sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
+ sqlite3OsDelete(pVfs, zMaster, 0);
+ break;
+ }else if( retryCount==1 ){
+ sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster);
+ }
}
+ retryCount++;
+ sqlite3_randomness(sizeof(iRandom), &iRandom);
+ sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X",
+ (iRandom>>8)&0xffffff, iRandom&0xff);
+ /* The antipenultimate character of the master journal name must
+ ** be "9" to avoid name collisions when using 8+3 filenames. */
+ assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' );
sqlite3FileSuffix3(zMainFile, zMaster);
rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
}while( rc==SQLITE_OK && res );
@@ -59488,7 +65370,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
/*
-** This routine checks that the sqlite3.activeVdbeCnt count variable
+** This routine checks that the sqlite3.nVdbeActive count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
@@ -59501,53 +65383,30 @@ static void checkActiveVdbeCnt(sqlite3 *db){
Vdbe *p;
int cnt = 0;
int nWrite = 0;
+ int nRead = 0;
p = db->pVdbe;
while( p ){
- if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){
+ if( sqlite3_stmt_busy((sqlite3_stmt*)p) ){
cnt++;
if( p->readOnly==0 ) nWrite++;
+ if( p->bIsReader ) nRead++;
}
p = p->pNext;
}
- assert( cnt==db->activeVdbeCnt );
- assert( nWrite==db->writeVdbeCnt );
+ assert( cnt==db->nVdbeActive );
+ assert( nWrite==db->nVdbeWrite );
+ assert( nRead==db->nVdbeRead );
}
#else
#define checkActiveVdbeCnt(x)
#endif
/*
-** For every Btree that in database connection db which
-** has been modified, "trip" or invalidate each cursor in
-** that Btree might have been modified so that the cursor
-** can never be used again. This happens when a rollback
-*** occurs. We have to trip all the other cursors, even
-** cursor from other VMs in different database connections,
-** so that none of them try to use the data at which they
-** were pointing and which now may have been changed due
-** to the rollback.
-**
-** Remember that a rollback can delete tables complete and
-** reorder rootpages. So it is not sufficient just to save
-** the state of the cursor. We have to invalidate the cursor
-** so that it is never used again.
-*/
-static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
- int i;
- for(i=0; i<db->nDb; i++){
- Btree *p = db->aDb[i].pBt;
- if( p && sqlite3BtreeIsInTrans(p) ){
- sqlite3BtreeTripAllCursors(p, SQLITE_ABORT);
- }
- }
-}
-
-/*
** If the Vdbe passed as the first argument opened a statement-transaction,
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
-** statement transaction is commtted.
+** statement transaction is committed.
**
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
** Otherwise SQLITE_OK.
@@ -59558,7 +65417,7 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
/* If p->iStatement is greater than zero, then this Vdbe opened a
** statement transaction that should be closed here. The only exception
- ** is that an IO error may have occured, causing an emergency rollback.
+ ** is that an IO error may have occurred, causing an emergency rollback.
** In this case (db->nStatement==0), and there is nothing to do.
*/
if( db->nStatement && p->iStatement ){
@@ -59601,6 +65460,7 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
** the statement transaction was opened. */
if( eOp==SAVEPOINT_ROLLBACK ){
db->nDeferredCons = p->nStmtDefCons;
+ db->nDeferredImmCons = p->nStmtDefImmCons;
}
}
return rc;
@@ -59613,16 +65473,18 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
**
** If there are outstanding FK violations and this function returns
-** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT and write
-** an error message to it. Then return SQLITE_ERROR.
+** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
+** and write an error message to it. Then return SQLITE_ERROR.
*/
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
sqlite3 *db = p->db;
- if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){
- p->rc = SQLITE_CONSTRAINT;
+ if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
+ || (!deferred && p->nFkConstraint>0)
+ ){
+ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
- sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
+ sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed");
return SQLITE_ERROR;
}
return SQLITE_OK;
@@ -59665,14 +65527,16 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
if( p->db->mallocFailed ){
p->rc = SQLITE_NOMEM;
}
+ if( p->aOnceFlag ) memset(p->aOnceFlag, 0, p->nOnceFlag);
closeAllCursors(p);
if( p->magic!=VDBE_MAGIC_RUN ){
return SQLITE_OK;
}
checkActiveVdbeCnt(db);
- /* No commit or rollback needed if the program never started */
- if( p->pc>=0 ){
+ /* No commit or rollback needed if the program never started or if the
+ ** SQL statement does not read or write a database file. */
+ if( p->pc>=0 && p->bIsReader ){
int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
@@ -59682,7 +65546,6 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* Check for one of the special errors */
mrc = p->rc & 0xff;
- assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
@@ -59693,7 +65556,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
**
** Even if the statement is read-only, it is important to perform
** a statement or transaction rollback operation. If the error
- ** occured while writing to the journal, sub-journal or database
+ ** occurred while writing to the journal, sub-journal or database
** file as part of an effort to free up cache space (see function
** pagerStress() in pager.c), the rollback is required to restore
** the pager to a consistent state.
@@ -59705,8 +65568,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* We are forced to roll back the active transaction. Before doing
** so, abort any other statements this handle currently has active.
*/
- invalidateCursorsOnModifiedBtrees(db);
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
}
@@ -59726,7 +65588,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
*/
if( !sqlite3VtabInSync(db)
&& db->autoCommit
- && db->writeVdbeCnt==(p->readOnly==0)
+ && db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFk(p, 1);
@@ -59735,7 +65597,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3VdbeLeave(p);
return SQLITE_ERROR;
}
- rc = SQLITE_CONSTRAINT;
+ rc = SQLITE_CONSTRAINT_FOREIGNKEY;
}else{
/* The auto-commit flag is true, the vdbe program was successful
** or hit an 'OR FAIL' constraint and there are no deferred foreign
@@ -59748,13 +65610,15 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
p->rc = rc;
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_OK);
}else{
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
}else{
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_OK);
}
db->nStatement = 0;
}else if( eStatementOp==0 ){
@@ -59763,8 +65627,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}else if( p->errorAction==OE_Abort ){
eStatementOp = SAVEPOINT_ROLLBACK;
}else{
- invalidateCursorsOnModifiedBtrees(db);
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
}
@@ -59779,13 +65642,12 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
if( eStatementOp ){
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
if( rc ){
- if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){
+ if( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ){
p->rc = rc;
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
- invalidateCursorsOnModifiedBtrees(db);
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
}
@@ -59802,12 +65664,6 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
p->nChange = 0;
}
-
- /* Rollback or commit any schema changes that occurred. */
- if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
- sqlite3ResetInternalSchema(db, -1);
- db->flags = (db->flags | SQLITE_InternChanges);
- }
/* Release the locks */
sqlite3VdbeLeave(p);
@@ -59815,11 +65671,12 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* We have successfully halted and closed the VM. Record this fact. */
if( p->pc>=0 ){
- db->activeVdbeCnt--;
- if( !p->readOnly ){
- db->writeVdbeCnt--;
- }
- assert( db->activeVdbeCnt>=db->writeVdbeCnt );
+ db->nVdbeActive--;
+ if( !p->readOnly ) db->nVdbeWrite--;
+ if( p->bIsReader ) db->nVdbeRead--;
+ assert( db->nVdbeActive>=db->nVdbeRead );
+ assert( db->nVdbeRead>=db->nVdbeWrite );
+ assert( db->nVdbeWrite>=0 );
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
@@ -59835,7 +65692,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3ConnectionUnlocked(db);
}
- assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
+ assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 );
return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}
@@ -59849,6 +65706,52 @@ SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){
}
/*
+** Copy the error code and error message belonging to the VDBE passed
+** as the first argument to its database handle (so that they will be
+** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
+**
+** This function does not clear the VDBE error code or message, just
+** copies them to the database handle.
+*/
+SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){
+ sqlite3 *db = p->db;
+ int rc = p->rc;
+ if( p->zErrMsg ){
+ u8 mallocFailed = db->mallocFailed;
+ sqlite3BeginBenignMalloc();
+ if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db);
+ sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
+ sqlite3EndBenignMalloc();
+ db->mallocFailed = mallocFailed;
+ db->errCode = rc;
+ }else{
+ sqlite3Error(db, rc);
+ }
+ return rc;
+}
+
+#ifdef SQLITE_ENABLE_SQLLOG
+/*
+** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run,
+** invoke it.
+*/
+static void vdbeInvokeSqllog(Vdbe *v){
+ if( sqlite3GlobalConfig.xSqllog && v->rc==SQLITE_OK && v->zSql && v->pc>=0 ){
+ char *zExpanded = sqlite3VdbeExpandSql(v, v->zSql);
+ assert( v->db->init.busy==0 );
+ if( zExpanded ){
+ sqlite3GlobalConfig.xSqllog(
+ sqlite3GlobalConfig.pSqllogArg, v->db, zExpanded, 1
+ );
+ sqlite3DbFree(v->db, zExpanded);
+ }
+ }
+}
+#else
+# define vdbeInvokeSqllog(x)
+#endif
+
+/*
** Clean up a VDBE after execution but do not delete the VDBE just yet.
** Write any error messages into *pzErrMsg. Return the result code.
**
@@ -59875,26 +65778,17 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
** instructions yet, leave the main database error information unchanged.
*/
if( p->pc>=0 ){
- if( p->zErrMsg ){
- sqlite3BeginBenignMalloc();
- sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
- sqlite3EndBenignMalloc();
- db->errCode = p->rc;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = 0;
- }else if( p->rc ){
- sqlite3Error(db, p->rc, 0);
- }else{
- sqlite3Error(db, SQLITE_OK, 0);
- }
+ vdbeInvokeSqllog(p);
+ sqlite3VdbeTransferError(p);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = 0;
if( p->runOnlyOnce ) p->expired = 1;
}else if( p->rc && p->expired ){
/* The expired flag was set on the VDBE before the first call
** to sqlite3_step(). For consistency (since sqlite3_step() was
** called), set the database error in this case as well.
*/
- sqlite3Error(db, p->rc, 0);
- sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
+ sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
@@ -59915,18 +65809,31 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
fprintf(out, "%02x", p->aOp[i].opcode);
}
fprintf(out, "\n");
+ if( p->zSql ){
+ char c, pc = 0;
+ fprintf(out, "-- ");
+ for(i=0; (c = p->zSql[i])!=0; i++){
+ if( pc=='\n' ) fprintf(out, "-- ");
+ putc(c, out);
+ pc = c;
+ }
+ if( pc!='\n' ) fprintf(out, "\n");
+ }
for(i=0; i<p->nOp; i++){
- fprintf(out, "%6d %10lld %8lld ",
+ char zHdr[100];
+ sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
p->aOp[i].cnt,
p->aOp[i].cycles,
p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0
);
+ fprintf(out, "%s", zHdr);
sqlite3VdbePrintOp(out, i, &p->aOp[i]);
}
fclose(out);
}
}
#endif
+ p->iCurrentTime = 0;
p->magic = VDBE_MAGIC_INIT;
return p->rc & db->errMask;
}
@@ -59946,31 +65853,49 @@ SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){
}
/*
-** Call the destructor for each auxdata entry in pVdbeFunc for which
-** the corresponding bit in mask is clear. Auxdata entries beyond 31
-** are always destroyed. To destroy all auxdata entries, call this
-** routine with mask==0.
+** If parameter iOp is less than zero, then invoke the destructor for
+** all auxiliary data pointers currently cached by the VM passed as
+** the first argument.
+**
+** Or, if iOp is greater than or equal to zero, then the destructor is
+** only invoked for those auxiliary data pointers created by the user
+** function invoked by the OP_Function opcode at instruction iOp of
+** VM pVdbe, and only then if:
+**
+** * the associated function parameter is the 32nd or later (counting
+** from left to right), or
+**
+** * the corresponding bit in argument mask is clear (where the first
+** function parameter corresponds to bit 0 etc.).
*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
- int i;
- for(i=0; i<pVdbeFunc->nAux; i++){
- struct AuxData *pAux = &pVdbeFunc->apAux[i];
- if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
+ AuxData **pp = &pVdbe->pAuxData;
+ while( *pp ){
+ AuxData *pAux = *pp;
+ if( (iOp<0)
+ || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg))))
+ ){
+ testcase( pAux->iArg==31 );
if( pAux->xDelete ){
pAux->xDelete(pAux->pAux);
}
- pAux->pAux = 0;
+ *pp = pAux->pNext;
+ sqlite3DbFree(pVdbe->db, pAux);
+ }else{
+ pp= &pAux->pNext;
}
}
}
/*
-** Free all memory associated with the Vdbe passed as the second argument.
+** Free all memory associated with the Vdbe passed as the second argument,
+** except for object itself, which is preserved.
+**
** The difference between this function and sqlite3VdbeDelete() is that
** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with
-** the database connection.
+** the database connection and frees the object itself.
*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
+SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
int i;
assert( p->db==0 || p->db==db );
@@ -59983,11 +65908,9 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
}
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
vdbeFreeOpArray(db, p->aOp, p->nOp);
- sqlite3DbFree(db, p->aLabel);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
- sqlite3DbFree(db, p);
}
/*
@@ -59998,6 +65921,8 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
if( NEVER(p==0) ) return;
db = p->db;
+ assert( sqlite3_mutex_held(db->mutex) );
+ sqlite3VdbeClearObject(db, p);
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{
@@ -60009,7 +65934,58 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
}
p->magic = VDBE_MAGIC_DEAD;
p->db = 0;
- sqlite3VdbeDeleteObject(db, p);
+ sqlite3DbFree(db, p);
+}
+
+/*
+** The cursor "p" has a pending seek operation that has not yet been
+** carried out. Seek the cursor now. If an error occurs, return
+** the appropriate error code.
+*/
+static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
+ int res, rc;
+#ifdef SQLITE_TEST
+ extern int sqlite3_search_count;
+#endif
+ assert( p->deferredMoveto );
+ assert( p->isTable );
+ rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
+ if( rc ) return rc;
+ if( res!=0 ) return SQLITE_CORRUPT_BKPT;
+#ifdef SQLITE_TEST
+ sqlite3_search_count++;
+#endif
+ p->deferredMoveto = 0;
+ p->cacheStatus = CACHE_STALE;
+ return SQLITE_OK;
+}
+
+/*
+** Something has moved cursor "p" out of place. Maybe the row it was
+** pointed to was deleted out from under it. Or maybe the btree was
+** rebalanced. Whatever the cause, try to restore "p" to the place it
+** is supposed to be pointing. If the row was deleted out from under the
+** cursor, set the cursor to point to a NULL row.
+*/
+static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
+ int isDifferentRow, rc;
+ assert( p->pCursor!=0 );
+ assert( sqlite3BtreeCursorHasMoved(p->pCursor) );
+ rc = sqlite3BtreeCursorRestore(p->pCursor, &isDifferentRow);
+ p->cacheStatus = CACHE_STALE;
+ if( isDifferentRow ) p->nullRow = 1;
+ return rc;
+}
+
+/*
+** Check to ensure that the cursor is valid. Restore the cursor
+** if need be. Return any I/O error from the restore operation.
+*/
+SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){
+ if( sqlite3BtreeCursorHasMoved(p->pCursor) ){
+ return handleMovedCursor(p);
+ }
+ return SQLITE_OK;
}
/*
@@ -60027,29 +66003,10 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
*/
SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
if( p->deferredMoveto ){
- int res, rc;
-#ifdef SQLITE_TEST
- extern int sqlite3_search_count;
-#endif
- assert( p->isTable );
- rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
- if( rc ) return rc;
- p->lastRowid = p->movetoTarget;
- if( res!=0 ) return SQLITE_CORRUPT_BKPT;
- p->rowidIsValid = 1;
-#ifdef SQLITE_TEST
- sqlite3_search_count++;
-#endif
- p->deferredMoveto = 0;
- p->cacheStatus = CACHE_STALE;
- }else if( ALWAYS(p->pCursor) ){
- int hasMoved;
- int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved);
- if( rc ) return rc;
- if( hasMoved ){
- p->cacheStatus = CACHE_STALE;
- p->nullRow = 1;
- }
+ return handleDeferredMoveto(p);
+ }
+ if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){
+ return handleMovedCursor(p);
}
return SQLITE_OK;
}
@@ -60072,7 +66029,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
** the blob of data that it corresponds to. In a table record, all serial
** types are stored at the start of the record, and the blobs of data at
** the end. Hence these functions allow the caller to handle the
-** serial-type and data blob seperately.
+** serial-type and data blob separately.
**
** The following table describes the various storage classes for data:
**
@@ -60101,7 +66058,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
*/
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
int flags = pMem->flags;
- int n;
+ u32 n;
if( flags&MEM_Null ){
return 0;
@@ -60111,9 +66068,6 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
# define MAX_6BYTE ((((i64)0x00008000)<<32)-1)
i64 i = pMem->u.i;
u64 u;
- if( file_format>=4 && (i&1)==i ){
- return 8+(u32)i;
- }
if( i<0 ){
if( i<(-MAX_6BYTE) ) return 6;
/* Previous test prevents: u = -(-9223372036854775808) */
@@ -60121,7 +66075,9 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
}else{
u = i;
}
- if( u<=127 ) return 1;
+ if( u<=127 ){
+ return ((i&1)==i && file_format>=4) ? 8+(u32)u : 1;
+ }
if( u<=32767 ) return 2;
if( u<=8388607 ) return 3;
if( u<=2147483647 ) return 4;
@@ -60132,11 +66088,11 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
return 7;
}
assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) );
- n = pMem->n;
+ assert( pMem->n>=0 );
+ n = (u32)pMem->n;
if( flags & MEM_Zero ){
n += pMem->u.nZero;
}
- assert( n>=0 );
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
@@ -60210,21 +66166,15 @@ static u64 floatSwap(u64 in){
** buf. It is assumed that the caller has allocated sufficient space.
** Return the number of bytes written.
**
-** nBuf is the amount of space left in buf[]. nBuf must always be
-** large enough to hold the entire field. Except, if the field is
-** a blob with a zero-filled tail, then buf[] might be just the right
-** size to hold everything except for the zero-filled tail. If buf[]
-** is only big enough to hold the non-zero prefix, then only write that
-** prefix into buf[]. But if buf[] is large enough to hold both the
-** prefix and the tail then write the prefix and set the tail to all
-** zeros.
+** nBuf is the amount of space left in buf[]. The caller is responsible
+** for allocating enough space to buf[] to hold the entire field, exclusive
+** of the pMem->u.nZero bytes for a MEM_Zero value.
**
** Return the number of bytes actually written into buf[]. The number
** of bytes in the zero-filled tail is included in the return value only
** if those bytes were zeroed in buf[].
*/
-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
- u32 serial_type = sqlite3VdbeSerialType(pMem, file_format);
+SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
u32 len;
/* Integer and Real */
@@ -60232,18 +66182,18 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f
u64 v;
u32 i;
if( serial_type==7 ){
- assert( sizeof(v)==sizeof(pMem->r) );
- memcpy(&v, &pMem->r, sizeof(v));
+ assert( sizeof(v)==sizeof(pMem->u.r) );
+ memcpy(&v, &pMem->u.r, sizeof(v));
swapMixedEndianFloat(v);
}else{
v = pMem->u.i;
}
len = i = sqlite3VdbeSerialTypeLen(serial_type);
- assert( len<=(u32)nBuf );
- while( i-- ){
- buf[i] = (u8)(v&0xFF);
+ assert( i>0 );
+ do{
+ buf[--i] = (u8)(v&0xFF);
v >>= 8;
- }
+ }while( i );
return len;
}
@@ -60251,17 +66201,8 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f
if( serial_type>=12 ){
assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0)
== (int)sqlite3VdbeSerialTypeLen(serial_type) );
- assert( pMem->n<=nBuf );
len = pMem->n;
memcpy(buf, pMem->z, len);
- if( pMem->flags & MEM_Zero ){
- len += pMem->u.nZero;
- assert( nBuf>=0 );
- if( len > (u32)nBuf ){
- len = (u32)nBuf;
- }
- memset(&buf[pMem->n], 0, len-pMem->n);
- }
return len;
}
@@ -60269,10 +66210,56 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f
return 0;
}
+/* Input "x" is a sequence of unsigned characters that represent a
+** big-endian integer. Return the equivalent native integer
+*/
+#define ONE_BYTE_INT(x) ((i8)(x)[0])
+#define TWO_BYTE_INT(x) (256*(i8)((x)[0])|(x)[1])
+#define THREE_BYTE_INT(x) (65536*(i8)((x)[0])|((x)[1]<<8)|(x)[2])
+#define FOUR_BYTE_UINT(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+#define FOUR_BYTE_INT(x) (16777216*(i8)((x)[0])|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+
/*
** Deserialize the data blob pointed to by buf as serial type serial_type
** and store the result in pMem. Return the number of bytes read.
+**
+** This function is implemented as two separate routines for performance.
+** The few cases that require local variables are broken out into a separate
+** routine so that in most cases the overhead of moving the stack pointer
+** is avoided.
*/
+static u32 SQLITE_NOINLINE serialGet(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ u32 serial_type, /* Serial type to deserialize */
+ Mem *pMem /* Memory cell to write value into */
+){
+ u64 x = FOUR_BYTE_UINT(buf);
+ u32 y = FOUR_BYTE_UINT(buf+4);
+ x = (x<<32) + y;
+ if( serial_type==6 ){
+ pMem->u.i = *(i64*)&x;
+ pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
+ }else{
+#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
+ /* Verify that integers and floating point values use the same
+ ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
+ ** defined that 64-bit floating point values really are mixed
+ ** endian.
+ */
+ static const u64 t1 = ((u64)0x3ff00000)<<32;
+ static const double r1 = 1.0;
+ u64 t2 = t1;
+ swapMixedEndianFloat(t2);
+ assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 );
+#endif
+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+ swapMixedEndianFloat(x);
+ memcpy(&pMem->u.r, &x, sizeof(x));
+ pMem->flags = sqlite3IsNaN(pMem->u.r) ? MEM_Null : MEM_Real;
+ }
+ return 8;
+}
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
@@ -60286,63 +66273,40 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
break;
}
case 1: { /* 1-byte signed integer */
- pMem->u.i = (signed char)buf[0];
+ pMem->u.i = ONE_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 1;
}
case 2: { /* 2-byte signed integer */
- pMem->u.i = (((signed char)buf[0])<<8) | buf[1];
+ pMem->u.i = TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 2;
}
case 3: { /* 3-byte signed integer */
- pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2];
+ pMem->u.i = THREE_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 3;
}
case 4: { /* 4-byte signed integer */
- pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
+ pMem->u.i = FOUR_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 4;
}
case 5: { /* 6-byte signed integer */
- u64 x = (((signed char)buf[0])<<8) | buf[1];
- u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
- x = (x<<32) | y;
- pMem->u.i = *(i64*)&x;
+ pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 6;
}
case 6: /* 8-byte signed integer */
case 7: { /* IEEE floating point */
- u64 x;
- u32 y;
-#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
- /* Verify that integers and floating point values use the same
- ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
- ** defined that 64-bit floating point values really are mixed
- ** endian.
- */
- static const u64 t1 = ((u64)0x3ff00000)<<32;
- static const double r1 = 1.0;
- u64 t2 = t1;
- swapMixedEndianFloat(t2);
- assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 );
-#endif
-
- x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
- y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7];
- x = (x<<32) | y;
- if( serial_type==6 ){
- pMem->u.i = *(i64*)&x;
- pMem->flags = MEM_Int;
- }else{
- assert( sizeof(x)==8 && sizeof(pMem->r)==8 );
- swapMixedEndianFloat(x);
- memcpy(&pMem->r, &x, sizeof(x));
- pMem->flags = sqlite3IsNaN(pMem->r) ? MEM_Null : MEM_Real;
- }
- return 8;
+ /* These use local variables, so do them in a separate routine
+ ** to avoid having to move the frame pointer in the common case */
+ return serialGet(buf,serial_type,pMem);
}
case 8: /* Integer 0 */
case 9: { /* Integer 1 */
@@ -60351,162 +66315,132 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
return 0;
}
default: {
- u32 len = (serial_type-12)/2;
+ static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem };
pMem->z = (char *)buf;
- pMem->n = len;
- pMem->xDel = 0;
- if( serial_type&0x01 ){
- pMem->flags = MEM_Str | MEM_Ephem;
- }else{
- pMem->flags = MEM_Blob | MEM_Ephem;
- }
- return len;
+ pMem->n = (serial_type-12)/2;
+ pMem->flags = aFlag[serial_type&1];
+ return pMem->n;
}
}
return 0;
}
-
-
/*
-** Given the nKey-byte encoding of a record in pKey[], parse the
-** record into a UnpackedRecord structure. Return a pointer to
-** that structure.
+** This routine is used to allocate sufficient space for an UnpackedRecord
+** structure large enough to be used with sqlite3VdbeRecordUnpack() if
+** the first argument is a pointer to KeyInfo structure pKeyInfo.
**
-** The calling function might provide szSpace bytes of memory
-** space at pSpace. This space can be used to hold the returned
-** VDbeParsedRecord structure if it is large enough. If it is
-** not big enough, space is obtained from sqlite3_malloc().
+** The space is either allocated using sqlite3DbMallocRaw() or from within
+** the unaligned buffer passed via the second and third arguments (presumably
+** stack space). If the former, then *ppFree is set to a pointer that should
+** be eventually freed by the caller using sqlite3DbFree(). Or, if the
+** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
+** before returning.
**
-** The returned structure should be closed by a call to
-** sqlite3VdbeDeleteUnpackedRecord().
-*/
-SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(
- KeyInfo *pKeyInfo, /* Information about the record format */
- int nKey, /* Size of the binary record */
- const void *pKey, /* The binary record */
- char *pSpace, /* Unaligned space available to hold the object */
- int szSpace /* Size of pSpace[] in bytes */
+** If an OOM error occurs, NULL is returned.
+*/
+SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
+ KeyInfo *pKeyInfo, /* Description of the record */
+ char *pSpace, /* Unaligned space available */
+ int szSpace, /* Size of pSpace[] in bytes */
+ char **ppFree /* OUT: Caller should free this pointer */
){
- const unsigned char *aKey = (const unsigned char *)pKey;
- UnpackedRecord *p; /* The unpacked record that we will return */
- int nByte; /* Memory space needed to hold p, in bytes */
- int d;
- u32 idx;
- u16 u; /* Unsigned loop counter */
- u32 szHdr;
- Mem *pMem;
- int nOff; /* Increase pSpace by this much to 8-byte align it */
-
- /*
- ** We want to shift the pointer pSpace up such that it is 8-byte aligned.
+ UnpackedRecord *p; /* Unpacked record to return */
+ int nOff; /* Increment pSpace by nOff to align it */
+ int nByte; /* Number of bytes required for *p */
+
+ /* We want to shift the pointer pSpace up such that it is 8-byte aligned.
** Thus, we need to calculate a value, nOff, between 0 and 7, to shift
** it by. If pSpace is already 8-byte aligned, nOff should be zero.
*/
nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7;
- pSpace += nOff;
- szSpace -= nOff;
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
- if( nByte>szSpace ){
- p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
- if( p==0 ) return 0;
- p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY;
+ if( nByte>szSpace+nOff ){
+ p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
+ *ppFree = (char *)p;
+ if( !p ) return 0;
}else{
- p = (UnpackedRecord*)pSpace;
- p->flags = UNPACKED_NEED_DESTROY;
+ p = (UnpackedRecord*)&pSpace[nOff];
+ *ppFree = 0;
}
+
+ p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
+ assert( pKeyInfo->aSortOrder!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nField + 1;
- p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
+ return p;
+}
+
+/*
+** Given the nKey-byte encoding of a record in pKey[], populate the
+** UnpackedRecord structure indicated by the fourth argument with the
+** contents of the decoded record.
+*/
+SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
+ KeyInfo *pKeyInfo, /* Information about the record format */
+ int nKey, /* Size of the binary record */
+ const void *pKey, /* The binary record */
+ UnpackedRecord *p /* Populate this structure before returning. */
+){
+ const unsigned char *aKey = (const unsigned char *)pKey;
+ int d;
+ u32 idx; /* Offset in aKey[] to read from */
+ u16 u; /* Unsigned loop counter */
+ u32 szHdr;
+ Mem *pMem = p->aMem;
+
+ p->default_rc = 0;
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
idx = getVarint32(aKey, szHdr);
d = szHdr;
u = 0;
- while( idx<szHdr && u<p->nField && d<=nKey ){
+ while( idx<szHdr && d<=nKey ){
u32 serial_type;
idx += getVarint32(&aKey[idx], serial_type);
pMem->enc = pKeyInfo->enc;
pMem->db = pKeyInfo->db;
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
- pMem->zMalloc = 0;
+ pMem->szMalloc = 0;
d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
pMem++;
- u++;
+ if( (++u)>=p->nField ) break;
}
assert( u<=pKeyInfo->nField + 1 );
p->nField = u;
- return (void*)p;
-}
-
-/*
-** This routine destroys a UnpackedRecord object.
-*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
-#ifdef SQLITE_DEBUG
- int i;
- Mem *pMem;
-
- assert( p!=0 );
- assert( p->flags & UNPACKED_NEED_DESTROY );
- for(i=0, pMem=p->aMem; i<p->nField; i++, pMem++){
- /* The unpacked record is always constructed by the
- ** sqlite3VdbeUnpackRecord() function above, which makes all
- ** strings and blobs static. And none of the elements are
- ** ever transformed, so there is never anything to delete.
- */
- if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem);
- }
-#endif
- if( p->flags & UNPACKED_NEED_FREE ){
- sqlite3DbFree(p->pKeyInfo->db, p);
- }
}
+#if SQLITE_DEBUG
/*
-** This function compares the two table rows or index records
-** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
-** or positive integer if key1 is less than, equal to or
-** greater than key2. The {nKey1, pKey1} key must be a blob
-** created by th OP_MakeRecord opcode of the VDBE. The pPKey2
-** key must be a parsed key such as obtained from
-** sqlite3VdbeParseRecord.
+** This function compares two index or table record keys in the same way
+** as the sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(),
+** this function deserializes and compares values using the
+** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
+** in assert() statements to ensure that the optimized code in
+** sqlite3VdbeRecordCompare() returns results with these two primitives.
**
-** Key1 and Key2 do not have to contain the same number of fields.
-** The key with fewer fields is usually compares less than the
-** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set
-** and the common prefixes are equal, then key1 is less than key2.
-** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are
-** equal, then the keys are considered to be equal and
-** the parts beyond the common prefix are ignored.
-**
-** If the UNPACKED_IGNORE_ROWID flag is set, then the last byte of
-** the header of pKey1 is ignored. It is assumed that pKey1 is
-** an index key, and thus ends with a rowid value. The last byte
-** of the header will therefore be the serial type of the rowid:
-** one of 1, 2, 3, 4, 5, 6, 8, or 9 - the integer serial types.
-** The serial type of the final rowid will always be a single byte.
-** By ignoring this last byte of the header, we force the comparison
-** to ignore the rowid at the end of key1.
+** Return true if the result of comparison is equivalent to desiredResult.
+** Return false if there is a disagreement.
*/
-SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
+static int vdbeRecordCompareDebug(
int nKey1, const void *pKey1, /* Left key */
- UnpackedRecord *pPKey2 /* Right key */
+ const UnpackedRecord *pPKey2, /* Right key */
+ int desiredResult /* Correct answer */
){
- int d1; /* Offset into aKey[] of next data element */
+ u32 d1; /* Offset into aKey[] of next data element */
u32 idx1; /* Offset into aKey[] of next header element */
u32 szHdr1; /* Number of bytes in header */
int i = 0;
- int nField;
int rc = 0;
const unsigned char *aKey1 = (const unsigned char *)pKey1;
KeyInfo *pKeyInfo;
Mem mem1;
pKeyInfo = pPKey2->pKeyInfo;
+ if( pKeyInfo->db==0 ) return 1;
mem1.enc = pKeyInfo->enc;
mem1.db = pKeyInfo->db;
/* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */
- VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */
+ VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
/* Compilers may complain that mem1.u.i is potentially uninitialized.
** We could initialize it, as shown here, to silence those complaints.
@@ -60519,16 +66453,27 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
idx1 = getVarint32(aKey1, szHdr1);
d1 = szHdr1;
- if( pPKey2->flags & UNPACKED_IGNORE_ROWID ){
- szHdr1--;
- }
- nField = pKeyInfo->nField;
- while( idx1<szHdr1 && i<pPKey2->nField ){
+ assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
+ assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->nField>0 );
+ assert( idx1<=szHdr1 || CORRUPT_DB );
+ do{
u32 serial_type1;
/* Read the serial types for the next element in each key. */
idx1 += getVarint32( aKey1+idx1, serial_type1 );
- if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
+
+ /* Verify that there is enough key space remaining to avoid
+ ** a buffer overread. The "d1+serial_type1+2" subexpression will
+ ** always be greater than or equal to the amount of required key space.
+ ** Use that approximation to avoid the more expensive call to
+ ** sqlite3VdbeSerialTypeLen() in the common case.
+ */
+ if( d1+serial_type1+2>(u32)nKey1
+ && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1
+ ){
+ break;
+ }
/* Extract the values to be compared.
*/
@@ -60536,58 +66481,623 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
/* Do the comparison
*/
- rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
- i<nField ? pKeyInfo->aColl[i] : 0);
+ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]);
if( rc!=0 ){
- assert( mem1.zMalloc==0 ); /* See comment below */
-
- /* Invert the result if we are using DESC sort order. */
- if( pKeyInfo->aSortOrder && i<nField && pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ assert( mem1.szMalloc==0 ); /* See comment below */
+ if( pKeyInfo->aSortOrder[i] ){
+ rc = -rc; /* Invert the result for DESC sort order. */
}
-
- /* If the PREFIX_SEARCH flag is set and all fields except the final
- ** rowid field were equal, then clear the PREFIX_SEARCH flag and set
- ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
- ** This is used by the OP_IsUnique opcode.
- */
- if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
- assert( idx1==szHdr1 && rc );
- assert( mem1.flags & MEM_Int );
- pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
- pPKey2->rowid = mem1.u.i;
- }
-
- return rc;
+ goto debugCompareEnd;
}
i++;
- }
+ }while( idx1<szHdr1 && i<pPKey2->nField );
/* No memory allocation is ever used on mem1. Prove this using
** the following assert(). If the assert() fails, it indicates a
** memory leak and a need to call sqlite3VdbeMemRelease(&mem1).
*/
- assert( mem1.zMalloc==0 );
+ assert( mem1.szMalloc==0 );
/* rc==0 here means that one of the keys ran out of fields and
- ** all the fields up to that point were equal. If the UNPACKED_INCRKEY
- ** flag is set, then break the tie by treating key2 as larger.
- ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
- ** are considered to be equal. Otherwise, the longer key is the
- ** larger. As it happens, the pPKey2 will always be the longer
- ** if there is a difference.
- */
- assert( rc==0 );
- if( pPKey2->flags & UNPACKED_INCRKEY ){
- rc = -1;
- }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){
- /* Leave rc==0 */
- }else if( idx1<szHdr1 ){
- rc = 1;
+ ** all the fields up to that point were equal. Return the default_rc
+ ** value. */
+ rc = pPKey2->default_rc;
+
+debugCompareEnd:
+ if( desiredResult==0 && rc==0 ) return 1;
+ if( desiredResult<0 && rc<0 ) return 1;
+ if( desiredResult>0 && rc>0 ) return 1;
+ if( CORRUPT_DB ) return 1;
+ if( pKeyInfo->db->mallocFailed ) return 1;
+ return 0;
+}
+#endif
+
+/*
+** Both *pMem1 and *pMem2 contain string values. Compare the two values
+** using the collation sequence pColl. As usual, return a negative , zero
+** or positive value if *pMem1 is less than, equal to or greater than
+** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
+*/
+static int vdbeCompareMemString(
+ const Mem *pMem1,
+ const Mem *pMem2,
+ const CollSeq *pColl,
+ u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
+){
+ if( pMem1->enc==pColl->enc ){
+ /* The strings are already in the correct encoding. Call the
+ ** comparison function directly */
+ return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
+ }else{
+ int rc;
+ const void *v1, *v2;
+ int n1, n2;
+ Mem c1;
+ Mem c2;
+ sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
+ sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
+ sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
+ sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
+ v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
+ n1 = v1==0 ? 0 : c1.n;
+ v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
+ n2 = v2==0 ? 0 : c2.n;
+ rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
+ sqlite3VdbeMemRelease(&c1);
+ sqlite3VdbeMemRelease(&c2);
+ if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM;
+ return rc;
}
- return rc;
}
+
+/*
+** Compare two blobs. Return negative, zero, or positive if the first
+** is less than, equal to, or greater than the second, respectively.
+** If one blob is a prefix of the other, then the shorter is the lessor.
+*/
+static SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
+ int c = memcmp(pB1->z, pB2->z, pB1->n>pB2->n ? pB2->n : pB1->n);
+ if( c ) return c;
+ return pB1->n - pB2->n;
+}
+
+
+/*
+** Compare the values contained by the two memory cells, returning
+** negative, zero or positive if pMem1 is less than, equal to, or greater
+** than pMem2. Sorting order is NULL's first, followed by numbers (integers
+** and reals) sorted numerically, followed by text ordered by the collating
+** sequence pColl and finally blob's ordered by memcmp().
+**
+** Two NULL values are considered equal by this function.
+*/
+SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
+ int f1, f2;
+ int combined_flags;
+
+ f1 = pMem1->flags;
+ f2 = pMem2->flags;
+ combined_flags = f1|f2;
+ assert( (combined_flags & MEM_RowSet)==0 );
+
+ /* If one value is NULL, it is less than the other. If both values
+ ** are NULL, return 0.
+ */
+ if( combined_flags&MEM_Null ){
+ return (f2&MEM_Null) - (f1&MEM_Null);
+ }
+
+ /* If one value is a number and the other is not, the number is less.
+ ** If both are numbers, compare as reals if one is a real, or as integers
+ ** if both values are integers.
+ */
+ if( combined_flags&(MEM_Int|MEM_Real) ){
+ double r1, r2;
+ if( (f1 & f2 & MEM_Int)!=0 ){
+ if( pMem1->u.i < pMem2->u.i ) return -1;
+ if( pMem1->u.i > pMem2->u.i ) return 1;
+ return 0;
+ }
+ if( (f1&MEM_Real)!=0 ){
+ r1 = pMem1->u.r;
+ }else if( (f1&MEM_Int)!=0 ){
+ r1 = (double)pMem1->u.i;
+ }else{
+ return 1;
+ }
+ if( (f2&MEM_Real)!=0 ){
+ r2 = pMem2->u.r;
+ }else if( (f2&MEM_Int)!=0 ){
+ r2 = (double)pMem2->u.i;
+ }else{
+ return -1;
+ }
+ if( r1<r2 ) return -1;
+ if( r1>r2 ) return 1;
+ return 0;
+ }
+
+ /* If one value is a string and the other is a blob, the string is less.
+ ** If both are strings, compare using the collating functions.
+ */
+ if( combined_flags&MEM_Str ){
+ if( (f1 & MEM_Str)==0 ){
+ return 1;
+ }
+ if( (f2 & MEM_Str)==0 ){
+ return -1;
+ }
+
+ assert( pMem1->enc==pMem2->enc );
+ assert( pMem1->enc==SQLITE_UTF8 ||
+ pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
+
+ /* The collation sequence must be defined at this point, even if
+ ** the user deletes the collation sequence after the vdbe program is
+ ** compiled (this was not always the case).
+ */
+ assert( !pColl || pColl->xCmp );
+
+ if( pColl ){
+ return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
+ }
+ /* If a NULL pointer was passed as the collate function, fall through
+ ** to the blob case and use memcmp(). */
+ }
+ /* Both values must be blobs. Compare using memcmp(). */
+ return sqlite3BlobCompare(pMem1, pMem2);
+}
+
+
+/*
+** The first argument passed to this function is a serial-type that
+** corresponds to an integer - all values between 1 and 9 inclusive
+** except 7. The second points to a buffer containing an integer value
+** serialized according to serial_type. This function deserializes
+** and returns the value.
+*/
+static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
+ u32 y;
+ assert( CORRUPT_DB || (serial_type>=1 && serial_type<=9 && serial_type!=7) );
+ switch( serial_type ){
+ case 0:
+ case 1:
+ testcase( aKey[0]&0x80 );
+ return ONE_BYTE_INT(aKey);
+ case 2:
+ testcase( aKey[0]&0x80 );
+ return TWO_BYTE_INT(aKey);
+ case 3:
+ testcase( aKey[0]&0x80 );
+ return THREE_BYTE_INT(aKey);
+ case 4: {
+ testcase( aKey[0]&0x80 );
+ y = FOUR_BYTE_UINT(aKey);
+ return (i64)*(int*)&y;
+ }
+ case 5: {
+ testcase( aKey[0]&0x80 );
+ return FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey);
+ }
+ case 6: {
+ u64 x = FOUR_BYTE_UINT(aKey);
+ testcase( aKey[0]&0x80 );
+ x = (x<<32) | FOUR_BYTE_UINT(aKey+4);
+ return (i64)*(i64*)&x;
+ }
+ }
+
+ return (serial_type - 8);
+}
+
+/*
+** This function compares the two table rows or index records
+** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
+** or positive integer if key1 is less than, equal to or
+** greater than key2. The {nKey1, pKey1} key must be a blob
+** created by the OP_MakeRecord opcode of the VDBE. The pPKey2
+** key must be a parsed key such as obtained from
+** sqlite3VdbeParseRecord.
+**
+** If argument bSkip is non-zero, it is assumed that the caller has already
+** determined that the first fields of the keys are equal.
+**
+** Key1 and Key2 do not have to contain the same number of fields. If all
+** fields that appear in both keys are equal, then pPKey2->default_rc is
+** returned.
+**
+** If database corruption is discovered, set pPKey2->errCode to
+** SQLITE_CORRUPT and return 0. If an OOM error is encountered,
+** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
+** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
+*/
+static int vdbeRecordCompareWithSkip(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2, /* Right key */
+ int bSkip /* If true, skip the first field */
+){
+ u32 d1; /* Offset into aKey[] of next data element */
+ int i; /* Index of next field to compare */
+ u32 szHdr1; /* Size of record header in bytes */
+ u32 idx1; /* Offset of first type in header */
+ int rc = 0; /* Return value */
+ Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */
+ KeyInfo *pKeyInfo = pPKey2->pKeyInfo;
+ const unsigned char *aKey1 = (const unsigned char *)pKey1;
+ Mem mem1;
+
+ /* If bSkip is true, then the caller has already determined that the first
+ ** two elements in the keys are equal. Fix the various stack variables so
+ ** that this routine begins comparing at the second field. */
+ if( bSkip ){
+ u32 s1;
+ idx1 = 1 + getVarint32(&aKey1[1], s1);
+ szHdr1 = aKey1[0];
+ d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1);
+ i = 1;
+ pRhs++;
+ }else{
+ idx1 = getVarint32(aKey1, szHdr1);
+ d1 = szHdr1;
+ if( d1>(unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
+ i = 0;
+ }
+
+ VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
+ assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField
+ || CORRUPT_DB );
+ assert( pPKey2->pKeyInfo->aSortOrder!=0 );
+ assert( pPKey2->pKeyInfo->nField>0 );
+ assert( idx1<=szHdr1 || CORRUPT_DB );
+ do{
+ u32 serial_type;
+
+ /* RHS is an integer */
+ if( pRhs->flags & MEM_Int ){
+ serial_type = aKey1[idx1];
+ testcase( serial_type==12 );
+ if( serial_type>=12 ){
+ rc = +1;
+ }else if( serial_type==0 ){
+ rc = -1;
+ }else if( serial_type==7 ){
+ double rhs = (double)pRhs->u.i;
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ if( mem1.u.r<rhs ){
+ rc = -1;
+ }else if( mem1.u.r>rhs ){
+ rc = +1;
+ }
+ }else{
+ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
+ i64 rhs = pRhs->u.i;
+ if( lhs<rhs ){
+ rc = -1;
+ }else if( lhs>rhs ){
+ rc = +1;
+ }
+ }
+ }
+
+ /* RHS is real */
+ else if( pRhs->flags & MEM_Real ){
+ serial_type = aKey1[idx1];
+ if( serial_type>=12 ){
+ rc = +1;
+ }else if( serial_type==0 ){
+ rc = -1;
+ }else{
+ double rhs = pRhs->u.r;
+ double lhs;
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ if( serial_type==7 ){
+ lhs = mem1.u.r;
+ }else{
+ lhs = (double)mem1.u.i;
+ }
+ if( lhs<rhs ){
+ rc = -1;
+ }else if( lhs>rhs ){
+ rc = +1;
+ }
+ }
+ }
+
+ /* RHS is a string */
+ else if( pRhs->flags & MEM_Str ){
+ getVarint32(&aKey1[idx1], serial_type);
+ testcase( serial_type==12 );
+ if( serial_type<12 ){
+ rc = -1;
+ }else if( !(serial_type & 0x01) ){
+ rc = +1;
+ }else{
+ mem1.n = (serial_type - 12) / 2;
+ testcase( (d1+mem1.n)==(unsigned)nKey1 );
+ testcase( (d1+mem1.n+1)==(unsigned)nKey1 );
+ if( (d1+mem1.n) > (unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }else if( pKeyInfo->aColl[i] ){
+ mem1.enc = pKeyInfo->enc;
+ mem1.db = pKeyInfo->db;
+ mem1.flags = MEM_Str;
+ mem1.z = (char*)&aKey1[d1];
+ rc = vdbeCompareMemString(
+ &mem1, pRhs, pKeyInfo->aColl[i], &pPKey2->errCode
+ );
+ }else{
+ int nCmp = MIN(mem1.n, pRhs->n);
+ rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
+ if( rc==0 ) rc = mem1.n - pRhs->n;
+ }
+ }
+ }
+
+ /* RHS is a blob */
+ else if( pRhs->flags & MEM_Blob ){
+ getVarint32(&aKey1[idx1], serial_type);
+ testcase( serial_type==12 );
+ if( serial_type<12 || (serial_type & 0x01) ){
+ rc = -1;
+ }else{
+ int nStr = (serial_type - 12) / 2;
+ testcase( (d1+nStr)==(unsigned)nKey1 );
+ testcase( (d1+nStr+1)==(unsigned)nKey1 );
+ if( (d1+nStr) > (unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }else{
+ int nCmp = MIN(nStr, pRhs->n);
+ rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
+ if( rc==0 ) rc = nStr - pRhs->n;
+ }
+ }
+ }
+
+ /* RHS is null */
+ else{
+ serial_type = aKey1[idx1];
+ rc = (serial_type!=0);
+ }
+
+ if( rc!=0 ){
+ if( pKeyInfo->aSortOrder[i] ){
+ rc = -rc;
+ }
+ assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
+ assert( mem1.szMalloc==0 ); /* See comment below */
+ return rc;
+ }
+
+ i++;
+ pRhs++;
+ d1 += sqlite3VdbeSerialTypeLen(serial_type);
+ idx1 += sqlite3VarintLen(serial_type);
+ }while( idx1<(unsigned)szHdr1 && i<pPKey2->nField && d1<=(unsigned)nKey1 );
+
+ /* No memory allocation is ever used on mem1. Prove this using
+ ** the following assert(). If the assert() fails, it indicates a
+ ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). */
+ assert( mem1.szMalloc==0 );
+
+ /* rc==0 here means that one or both of the keys ran out of fields and
+ ** all the fields up to that point were equal. Return the default_rc
+ ** value. */
+ assert( CORRUPT_DB
+ || vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
+ || pKeyInfo->db->mallocFailed
+ );
+ return pPKey2->default_rc;
+}
+SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
+){
+ return vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
+}
+
+
+/*
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is an integer, and (b) the
+** size-of-header varint at the start of (pKey1/nKey1) fits in a single
+** byte (i.e. is less than 128).
+**
+** To avoid concerns about buffer overreads, this routine is only used
+** on schemas where the maximum valid header size is 63 bytes or less.
+*/
+static int vdbeRecordCompareInt(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
+){
+ const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F];
+ int serial_type = ((const u8*)pKey1)[1];
+ int res;
+ u32 y;
+ u64 x;
+ i64 v = pPKey2->aMem[0].u.i;
+ i64 lhs;
+
+ assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB );
+ switch( serial_type ){
+ case 1: { /* 1-byte signed integer */
+ lhs = ONE_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 2: { /* 2-byte signed integer */
+ lhs = TWO_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 3: { /* 3-byte signed integer */
+ lhs = THREE_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 4: { /* 4-byte signed integer */
+ y = FOUR_BYTE_UINT(aKey);
+ lhs = (i64)*(int*)&y;
+ testcase( lhs<0 );
+ break;
+ }
+ case 5: { /* 6-byte signed integer */
+ lhs = FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 6: { /* 8-byte signed integer */
+ x = FOUR_BYTE_UINT(aKey);
+ x = (x<<32) | FOUR_BYTE_UINT(aKey+4);
+ lhs = *(i64*)&x;
+ testcase( lhs<0 );
+ break;
+ }
+ case 8:
+ lhs = 0;
+ break;
+ case 9:
+ lhs = 1;
+ break;
+
+ /* This case could be removed without changing the results of running
+ ** this code. Including it causes gcc to generate a faster switch
+ ** statement (since the range of switch targets now starts at zero and
+ ** is contiguous) but does not cause any duplicate code to be generated
+ ** (as gcc is clever enough to combine the two like cases). Other
+ ** compilers might be similar. */
+ case 0: case 7:
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
+
+ default:
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
+ }
+
+ if( v>lhs ){
+ res = pPKey2->r1;
+ }else if( v<lhs ){
+ res = pPKey2->r2;
+ }else if( pPKey2->nField>1 ){
+ /* The first fields of the two keys are equal. Compare the trailing
+ ** fields. */
+ res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
+ }else{
+ /* The first fields of the two keys are equal and there are no trailing
+ ** fields. Return pPKey2->default_rc in this case. */
+ res = pPKey2->default_rc;
+ }
+
+ assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
+ return res;
+}
+
+/*
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is a string, that (b) the first field
+** uses the collation sequence BINARY and (c) that the size-of-header varint
+** at the start of (pKey1/nKey1) fits in a single byte.
+*/
+static int vdbeRecordCompareString(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
+){
+ const u8 *aKey1 = (const u8*)pKey1;
+ int serial_type;
+ int res;
+
+ getVarint32(&aKey1[1], serial_type);
+ if( serial_type<12 ){
+ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */
+ }else if( !(serial_type & 0x01) ){
+ res = pPKey2->r2; /* (pKey1/nKey1) is a blob */
+ }else{
+ int nCmp;
+ int nStr;
+ int szHdr = aKey1[0];
+
+ nStr = (serial_type-12) / 2;
+ if( (szHdr + nStr) > nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
+ nCmp = MIN( pPKey2->aMem[0].n, nStr );
+ res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
+
+ if( res==0 ){
+ res = nStr - pPKey2->aMem[0].n;
+ if( res==0 ){
+ if( pPKey2->nField>1 ){
+ res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
+ }else{
+ res = pPKey2->default_rc;
+ }
+ }else if( res>0 ){
+ res = pPKey2->r2;
+ }else{
+ res = pPKey2->r1;
+ }
+ }else if( res>0 ){
+ res = pPKey2->r2;
+ }else{
+ res = pPKey2->r1;
+ }
+ }
+
+ assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res)
+ || CORRUPT_DB
+ || pPKey2->pKeyInfo->db->mallocFailed
+ );
+ return res;
+}
+
+/*
+** Return a pointer to an sqlite3VdbeRecordCompare() compatible function
+** suitable for comparing serialized records to the unpacked record passed
+** as the only argument.
+*/
+SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
+ /* varintRecordCompareInt() and varintRecordCompareString() both assume
+ ** that the size-of-header varint that occurs at the start of each record
+ ** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt()
+ ** also assumes that it is safe to overread a buffer by at least the
+ ** maximum possible legal header size plus 8 bytes. Because there is
+ ** guaranteed to be at least 74 (but not 136) bytes of padding following each
+ ** buffer passed to varintRecordCompareInt() this makes it convenient to
+ ** limit the size of the header to 64 bytes in cases where the first field
+ ** is an integer.
+ **
+ ** The easiest way to enforce this limit is to consider only records with
+ ** 13 fields or less. If the first field is an integer, the maximum legal
+ ** header size is (12*5 + 1 + 1) bytes. */
+ if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){
+ int flags = p->aMem[0].flags;
+ if( p->pKeyInfo->aSortOrder[0] ){
+ p->r1 = 1;
+ p->r2 = -1;
+ }else{
+ p->r1 = -1;
+ p->r2 = 1;
+ }
+ if( (flags & MEM_Int) ){
+ return vdbeRecordCompareInt;
+ }
+ testcase( flags & MEM_Real );
+ testcase( flags & MEM_Null );
+ testcase( flags & MEM_Blob );
+ if( (flags & (MEM_Real|MEM_Null|MEM_Blob))==0 && p->pKeyInfo->aColl[0]==0 ){
+ assert( flags & MEM_Str );
+ return vdbeRecordCompareString;
+ }
+ }
+
+ return sqlite3VdbeRecordCompare;
+}
/*
** pCur points at an index entry created using the OP_MakeRecord opcode.
@@ -60605,21 +67115,19 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
u32 lenRowid; /* Size of the rowid */
Mem m, v;
- UNUSED_PARAMETER(db);
-
/* Get the size of the index entry. Only indices entries of less
** than 2GiB are support - anything large must be database corruption.
** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so
** this code can safely assume that nCellKey is 32-bits
*/
assert( sqlite3BtreeCursorIsValid(pCur) );
- rc = sqlite3BtreeKeySize(pCur, &nCellKey);
+ VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey);
assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey );
/* Read in the complete content of the index entry */
- memset(&m, 0, sizeof(m));
- rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m);
+ sqlite3VdbeMemInit(&m, db, 0);
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
@@ -60661,7 +67169,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* Jump here if database corruption is detected after m has been
** allocated. Free the m object and return SQLITE_CORRUPT. */
idx_rowid_corruption:
- testcase( m.zMalloc!=0 );
+ testcase( m.szMalloc!=0 );
sqlite3VdbeMemRelease(&m);
return SQLITE_CORRUPT_BKPT;
}
@@ -60678,9 +67186,10 @@ idx_rowid_corruption:
** of the keys prior to the final rowid, not the entire key.
*/
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
- VdbeCursor *pC, /* The cursor to compare against */
- UnpackedRecord *pUnpacked, /* Unpacked version of key to compare against */
- int *res /* Write the comparison result here */
+ sqlite3 *db, /* Database connection */
+ VdbeCursor *pC, /* The cursor to compare against */
+ UnpackedRecord *pUnpacked, /* Unpacked version of key */
+ int *res /* Write the comparison result here */
){
i64 nCellKey = 0;
int rc;
@@ -60688,20 +67197,19 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
Mem m;
assert( sqlite3BtreeCursorIsValid(pCur) );
- rc = sqlite3BtreeKeySize(pCur, &nCellKey);
+ VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey);
assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
- /* nCellKey will always be between 0 and 0xffffffff because of the say
+ /* nCellKey will always be between 0 and 0xffffffff because of the way
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
if( nCellKey<=0 || nCellKey>0x7fffffff ){
*res = 0;
return SQLITE_CORRUPT_BKPT;
}
- memset(&m, 0, sizeof(m));
- rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m);
+ sqlite3VdbeMemInit(&m, db, 0);
+ rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
- assert( pUnpacked->flags & UNPACKED_IGNORE_ROWID );
*res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked);
sqlite3VdbeMemRelease(&m);
return SQLITE_OK;
@@ -60757,7 +67265,7 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe *v){
**
** The returned value must be freed by the caller using sqlite3ValueFree().
*/
-SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
+SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){
assert( iVar>0 );
if( v ){
Mem *pMem = &v->aVar[iVar-1];
@@ -60766,7 +67274,6 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
if( pRet ){
sqlite3VdbeMemCopy((Mem *)pRet, pMem);
sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8);
- sqlite3VdbeMemStoreType((Mem *)pRet);
}
return pRet;
}
@@ -60788,6 +67295,21 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
}
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
+** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
+** in memory obtained from sqlite3DbMalloc).
+*/
+SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
+ sqlite3 *db = p->db;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
+ sqlite3_free(pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
@@ -60861,17 +67383,11 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex;
-#endif
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
-#if SQLITE_THREADSAFE
- mutex = v->db->mutex;
-#endif
- sqlite3_mutex_enter(mutex);
+ sqlite3_mutex_enter(db->mutex);
rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(db);
}
return rc;
}
@@ -60931,7 +67447,6 @@ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){
Mem *p = (Mem*)pVal;
if( p->flags & (MEM_Blob|MEM_Str) ){
sqlite3VdbeMemExpandBlob(p);
- p->flags &= ~MEM_Str;
p->flags |= MEM_Blob;
return p->n ? p->z : 0;
}else{
@@ -60968,16 +67483,53 @@ SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){
- return pVal->type;
+ static const u8 aType[] = {
+ SQLITE_BLOB, /* 0x00 */
+ SQLITE_NULL, /* 0x01 */
+ SQLITE_TEXT, /* 0x02 */
+ SQLITE_NULL, /* 0x03 */
+ SQLITE_INTEGER, /* 0x04 */
+ SQLITE_NULL, /* 0x05 */
+ SQLITE_INTEGER, /* 0x06 */
+ SQLITE_NULL, /* 0x07 */
+ SQLITE_FLOAT, /* 0x08 */
+ SQLITE_NULL, /* 0x09 */
+ SQLITE_FLOAT, /* 0x0a */
+ SQLITE_NULL, /* 0x0b */
+ SQLITE_INTEGER, /* 0x0c */
+ SQLITE_NULL, /* 0x0d */
+ SQLITE_INTEGER, /* 0x0e */
+ SQLITE_NULL, /* 0x0f */
+ SQLITE_BLOB, /* 0x10 */
+ SQLITE_NULL, /* 0x11 */
+ SQLITE_TEXT, /* 0x12 */
+ SQLITE_NULL, /* 0x13 */
+ SQLITE_INTEGER, /* 0x14 */
+ SQLITE_NULL, /* 0x15 */
+ SQLITE_INTEGER, /* 0x16 */
+ SQLITE_NULL, /* 0x17 */
+ SQLITE_FLOAT, /* 0x18 */
+ SQLITE_NULL, /* 0x19 */
+ SQLITE_FLOAT, /* 0x1a */
+ SQLITE_NULL, /* 0x1b */
+ SQLITE_INTEGER, /* 0x1c */
+ SQLITE_NULL, /* 0x1d */
+ SQLITE_INTEGER, /* 0x1e */
+ SQLITE_NULL, /* 0x1f */
+ };
+ return aType[pVal->flags&MEM_AffMask];
}
/**************************** sqlite3_result_ *******************************
** The following routines are used by user-defined functions to specify
** the function result.
**
-** The setStrOrError() funtion calls sqlite3VdbeMemSetStr() to store the
+** The setStrOrError() function calls sqlite3VdbeMemSetStr() to store the
** result as a string or blob but if the string or blob is too large, it
** then sets the error code to SQLITE_TOOBIG
+**
+** The invokeValueDestructor(P,X) routine invokes destructor function X()
+** on value P is not going to be used and need to be destroyed.
*/
static void setResultStrOrError(
sqlite3_context *pCtx, /* Function context */
@@ -60986,10 +67538,26 @@ static void setResultStrOrError(
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
- if( sqlite3VdbeMemSetStr(&pCtx->s, z, n, enc, xDel)==SQLITE_TOOBIG ){
+ if( sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){
sqlite3_result_error_toobig(pCtx);
}
}
+static int invokeValueDestructor(
+ const void *p, /* Value to destroy */
+ void (*xDel)(void*), /* The destructor */
+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */
+){
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( xDel==0 ){
+ /* noop */
+ }else if( xDel==SQLITE_TRANSIENT ){
+ /* noop */
+ }else{
+ xDel((void*)p);
+ }
+ if( pCtx ) sqlite3_result_error_toobig(pCtx);
+ return SQLITE_TOOBIG;
+}
SQLITE_API void sqlite3_result_blob(
sqlite3_context *pCtx,
const void *z,
@@ -60997,36 +67565,52 @@ SQLITE_API void sqlite3_result_blob(
void (*xDel)(void *)
){
assert( n>=0 );
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, 0, xDel);
}
+SQLITE_API void sqlite3_result_blob64(
+ sqlite3_context *pCtx,
+ const void *z,
+ sqlite3_uint64 n,
+ void (*xDel)(void *)
+){
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( n>0x7fffffff ){
+ (void)invokeValueDestructor(z, xDel, pCtx);
+ }else{
+ setResultStrOrError(pCtx, z, (int)n, 0, xDel);
+ }
+}
SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetDouble(&pCtx->s, rVal);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetDouble(pCtx->pOut, rVal);
}
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
- sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
+ pCtx->fErrorOrAux = 1;
+ sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
- sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
+ pCtx->fErrorOrAux = 1;
+ sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetInt64(&pCtx->s, (i64)iVal);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetInt64(&pCtx->s, iVal);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetInt64(pCtx->pOut, iVal);
}
SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetNull(&pCtx->s);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetNull(pCtx->pOut);
}
SQLITE_API void sqlite3_result_text(
sqlite3_context *pCtx,
@@ -61034,9 +67618,25 @@ SQLITE_API void sqlite3_result_text(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
}
+SQLITE_API void sqlite3_result_text64(
+ sqlite3_context *pCtx,
+ const char *z,
+ sqlite3_uint64 n,
+ void (*xDel)(void *),
+ unsigned char enc
+){
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ if( n>0x7fffffff ){
+ (void)invokeValueDestructor(z, xDel, pCtx);
+ }else{
+ setResultStrOrError(pCtx, z, (int)n, enc, xDel);
+ }
+}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_text16(
sqlite3_context *pCtx,
@@ -61044,7 +67644,7 @@ SQLITE_API void sqlite3_result_text16(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel);
}
SQLITE_API void sqlite3_result_text16be(
@@ -61053,7 +67653,7 @@ SQLITE_API void sqlite3_result_text16be(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel);
}
SQLITE_API void sqlite3_result_text16le(
@@ -61062,40 +67662,43 @@ SQLITE_API void sqlite3_result_text16le(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel);
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemCopy(&pCtx->s, pValue);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemCopy(pCtx->pOut, pValue);
}
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetZeroBlob(&pCtx->s, n);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n);
}
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
pCtx->isError = errCode;
- if( pCtx->s.flags & MEM_Null ){
- sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1,
+ pCtx->fErrorOrAux = 1;
+ if( pCtx->pOut->flags & MEM_Null ){
+ sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1,
SQLITE_UTF8, SQLITE_STATIC);
}
}
/* Force an SQLITE_TOOBIG error. */
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
- sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1,
+ pCtx->fErrorOrAux = 1;
+ sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
SQLITE_UTF8, SQLITE_STATIC);
}
/* An SQLITE_NOMEM error. */
SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetNull(&pCtx->s);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetNull(pCtx->pOut);
pCtx->isError = SQLITE_NOMEM;
- pCtx->s.db->mallocFailed = 1;
+ pCtx->fErrorOrAux = 1;
+ pCtx->pOut->db->mallocFailed = 1;
}
/*
@@ -61144,7 +67747,7 @@ static int sqlite3Step(Vdbe *p){
**
** Nevertheless, some published applications that were originally written
** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
- ** returns, and the so were broken by the automatic-reset change. As a
+ ** returns, and those were broken by the automatic-reset change. As a
** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
** legacy behavior of returning SQLITE_MISUSE for cases where the
** previous sqlite3_step() returned something other than a SQLITE_LOCKED
@@ -61178,11 +67781,13 @@ static int sqlite3Step(Vdbe *p){
** reset the interrupt flag. This prevents a call to sqlite3_interrupt
** from interrupting a statement that has not yet started.
*/
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
- assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 );
+ assert( db->nVdbeWrite>0 || db->autoCommit==0
+ || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
+ );
#ifndef SQLITE_OMIT_TRACE
if( db->xProfile && !db->init.busy ){
@@ -61190,8 +67795,9 @@ static int sqlite3Step(Vdbe *p){
}
#endif
- db->activeVdbeCnt++;
- if( p->readOnly==0 ) db->writeVdbeCnt++;
+ db->nVdbeActive++;
+ if( p->readOnly==0 ) db->nVdbeWrite++;
+ if( p->bIsReader ) db->nVdbeRead++;
p->pc = 0;
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -61200,9 +67806,9 @@ static int sqlite3Step(Vdbe *p){
}else
#endif /* SQLITE_OMIT_EXPLAIN */
{
- db->vdbeExecCnt++;
+ db->nVdbeExec++;
rc = sqlite3VdbeExec(p);
- db->vdbeExecCnt--;
+ db->nVdbeExec--;
}
#ifndef SQLITE_OMIT_TRACE
@@ -61241,23 +67847,15 @@ end_of_step:
assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE );
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
/* If this statement was prepared using sqlite3_prepare_v2(), and an
- ** error has occured, then return the error code in p->rc to the
+ ** error has occurred, then return the error code in p->rc to the
** caller. Set the error code in the database handle to the same value.
*/
- rc = db->errCode = p->rc;
+ rc = sqlite3VdbeTransferError(p);
}
return (rc&db->errMask);
}
/*
-** The maximum number of times that a statement will try to reparse
-** itself before giving up and returning SQLITE_SCHEMA.
-*/
-#ifndef SQLITE_MAX_SCHEMA_RETRY
-# define SQLITE_MAX_SCHEMA_RETRY 5
-#endif
-
-/*
** This is the top-level implementation of sqlite3_step(). Call
** sqlite3Step() to do most of the work. If a schema error occurs,
** call sqlite3Reprepare() and try again.
@@ -61274,13 +67872,17 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
}
db = v->db;
sqlite3_mutex_enter(db->mutex);
+ v->doingRerun = 0;
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
- && cnt++ < SQLITE_MAX_SCHEMA_RETRY
- && (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){
+ && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
+ int savedPc = v->pc;
+ rc2 = rc = sqlite3Reprepare(v);
+ if( rc!=SQLITE_OK) break;
sqlite3_reset(pStmt);
- v->expired = 0;
+ if( savedPc>=0 ) v->doingRerun = 1;
+ assert( v->expired==0 );
}
- if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){
+ if( rc2!=SQLITE_OK ){
/* This case occurs after failing to recompile an sql statement.
** The error message from the SQL compiler has already been loaded
** into the database handle. This block copies the error message
@@ -61290,6 +67892,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
** sqlite3_errmsg() and sqlite3_errcode().
*/
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
+ assert( zErr!=0 || db->mallocFailed );
sqlite3DbFree(db, v->zErrMsg);
if( !db->mallocFailed ){
v->zErrMsg = sqlite3DbStrDup(db, zErr);
@@ -61304,6 +67907,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
return rc;
}
+
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
@@ -61325,7 +67929,20 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
*/
SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
assert( p && p->pFunc );
- return p->s.db;
+ return p->pOut->db;
+}
+
+/*
+** Return the current time for a statement
+*/
+SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
+ Vdbe *v = p->pVdbe;
+ int rc;
+ if( v->iCurrentTime==0 ){
+ rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, &v->iCurrentTime);
+ if( rc ) v->iCurrentTime = 0;
+ }
+ return v->iCurrentTime;
}
/*
@@ -61351,50 +67968,59 @@ SQLITE_PRIVATE void sqlite3InvalidFunction(
}
/*
+** Create a new aggregate context for p and return a pointer to
+** its pMem->z element.
+*/
+static SQLITE_NOINLINE void *createAggContext(sqlite3_context *p, int nByte){
+ Mem *pMem = p->pMem;
+ assert( (pMem->flags & MEM_Agg)==0 );
+ if( nByte<=0 ){
+ sqlite3VdbeMemSetNull(pMem);
+ pMem->z = 0;
+ }else{
+ sqlite3VdbeMemClearAndResize(pMem, nByte);
+ pMem->flags = MEM_Agg;
+ pMem->u.pDef = p->pFunc;
+ if( pMem->z ){
+ memset(pMem->z, 0, nByte);
+ }
+ }
+ return (void*)pMem->z;
+}
+
+/*
** Allocate or return the aggregate context for a user function. A new
** context is allocated on the first call. Subsequent calls return the
** same context that was returned on prior calls.
*/
SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
- Mem *pMem;
assert( p && p->pFunc && p->pFunc->xStep );
- assert( sqlite3_mutex_held(p->s.db->mutex) );
- pMem = p->pMem;
+ assert( sqlite3_mutex_held(p->pOut->db->mutex) );
testcase( nByte<0 );
- if( (pMem->flags & MEM_Agg)==0 ){
- if( nByte<=0 ){
- sqlite3VdbeMemReleaseExternal(pMem);
- pMem->flags = MEM_Null;
- pMem->z = 0;
- }else{
- sqlite3VdbeMemGrow(pMem, nByte, 0);
- pMem->flags = MEM_Agg;
- pMem->u.pDef = p->pFunc;
- if( pMem->z ){
- memset(pMem->z, 0, nByte);
- }
- }
+ if( (p->pMem->flags & MEM_Agg)==0 ){
+ return createAggContext(p, nByte);
+ }else{
+ return (void*)p->pMem->z;
}
- return (void*)pMem->z;
}
/*
-** Return the auxilary data pointer, if any, for the iArg'th argument to
+** Return the auxiliary data pointer, if any, for the iArg'th argument to
** the user-function defined by pCtx.
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
- VdbeFunc *pVdbeFunc;
+ AuxData *pAuxData;
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){
- return 0;
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
}
- return pVdbeFunc->apAux[iArg].pAux;
+
+ return (pAuxData ? pAuxData->pAux : 0);
}
/*
-** Set the auxilary data pointer and delete function, for the iArg'th
+** Set the auxiliary data pointer and delete function, for the iArg'th
** argument to the user-function defined by pCtx. Any previous value is
** deleted by calling the delete function specified when it was set.
*/
@@ -61404,29 +68030,30 @@ SQLITE_API void sqlite3_set_auxdata(
void *pAux,
void (*xDelete)(void*)
){
- struct AuxData *pAuxData;
- VdbeFunc *pVdbeFunc;
- if( iArg<0 ) goto failed;
+ AuxData *pAuxData;
+ Vdbe *pVdbe = pCtx->pVdbe;
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){
- int nAux = (pVdbeFunc ? pVdbeFunc->nAux : 0);
- int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg;
- pVdbeFunc = sqlite3DbRealloc(pCtx->s.db, pVdbeFunc, nMalloc);
- if( !pVdbeFunc ){
- goto failed;
- }
- pCtx->pVdbeFunc = pVdbeFunc;
- memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux));
- pVdbeFunc->nAux = iArg+1;
- pVdbeFunc->pFunc = pCtx->pFunc;
- }
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ if( iArg<0 ) goto failed;
- pAuxData = &pVdbeFunc->apAux[iArg];
- if( pAuxData->pAux && pAuxData->xDelete ){
+ for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
+ }
+ if( pAuxData==0 ){
+ pAuxData = sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData));
+ if( !pAuxData ) goto failed;
+ pAuxData->iOp = pCtx->iOp;
+ pAuxData->iArg = iArg;
+ pAuxData->pNext = pVdbe->pAuxData;
+ pVdbe->pAuxData = pAuxData;
+ if( pCtx->fErrorOrAux==0 ){
+ pCtx->isError = 0;
+ pCtx->fErrorOrAux = 1;
+ }
+ }else if( pAuxData->xDelete ){
pAuxData->xDelete(pAuxData->pAux);
}
+
pAuxData->pAux = pAux;
pAuxData->xDelete = xDelete;
return;
@@ -61439,7 +68066,7 @@ failed:
#ifndef SQLITE_OMIT_DEPRECATED
/*
-** Return the number of times the Step function of a aggregate has been
+** Return the number of times the Step function of an aggregate has been
** called.
**
** This function is deprecated. Do not use it for new code. It is
@@ -61471,6 +68098,41 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){
return pVm->nResColumn;
}
+/*
+** Return a pointer to static memory containing an SQL NULL value.
+*/
+static const Mem *columnNullValue(void){
+ /* Even though the Mem structure contains an element
+ ** of type i64, on certain architectures (x86) with certain compiler
+ ** switches (-Os), gcc may align this Mem object on a 4-byte boundary
+ ** instead of an 8-byte one. This all works fine, except that when
+ ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
+ ** that a Mem structure is located on an 8-byte boundary. To prevent
+ ** these assert()s from failing, when building with SQLITE_DEBUG defined
+ ** using gcc, we force nullMem to be 8-byte aligned using the magical
+ ** __attribute__((aligned(8))) macro. */
+ static const Mem nullMem
+#if defined(SQLITE_DEBUG) && defined(__GNUC__)
+ __attribute__((aligned(8)))
+#endif
+ = {
+ /* .u = */ {0},
+ /* .flags = */ MEM_Null,
+ /* .enc = */ 0,
+ /* .n = */ 0,
+ /* .z = */ 0,
+ /* .zMalloc = */ 0,
+ /* .szMalloc = */ 0,
+ /* .iPadding1 = */ 0,
+ /* .db = */ 0,
+ /* .xDel = */ 0,
+#ifdef SQLITE_DEBUG
+ /* .pScopyFrom = */ 0,
+ /* .pFiller = */ 0,
+#endif
+ };
+ return &nullMem;
+}
/*
** Check to see if column iCol of the given statement is valid. If
@@ -61487,32 +68149,11 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
sqlite3_mutex_enter(pVm->db->mutex);
pOut = &pVm->pResultSet[i];
}else{
- /* If the value passed as the second argument is out of range, return
- ** a pointer to the following static Mem object which contains the
- ** value SQL NULL. Even though the Mem structure contains an element
- ** of type i64, on certain architecture (x86) with certain compiler
- ** switches (-Os), gcc may align this Mem object on a 4-byte boundary
- ** instead of an 8-byte one. This all works fine, except that when
- ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
- ** that a Mem structure is located on an 8-byte boundary. To prevent
- ** this assert() from failing, when building with SQLITE_DEBUG defined
- ** using gcc, force nullMem to be 8-byte aligned using the magical
- ** __attribute__((aligned(8))) macro. */
- static const Mem nullMem
-#if defined(SQLITE_DEBUG) && defined(__GNUC__)
- __attribute__((aligned(8)))
-#endif
- = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0,
-#ifdef SQLITE_DEBUG
- 0, 0, /* pScopyFrom, pFiller */
-#endif
- 0, 0 };
-
if( pVm && ALWAYS(pVm->db) ){
sqlite3_mutex_enter(pVm->db->mutex);
- sqlite3Error(pVm->db, SQLITE_RANGE, 0);
+ sqlite3Error(pVm->db, SQLITE_RANGE);
}
- pOut = (Mem*)&nullMem;
+ pOut = (Mem*)columnNullValue();
}
return pOut;
}
@@ -61615,13 +68256,6 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
return iType;
}
-/* The following function is experimental and subject to change or
-** removal */
-/*int sqlite3_column_numeric_type(sqlite3_stmt *pStmt, int i){
-** return sqlite3_value_numeric_type( columnMem(pStmt,i) );
-**}
-*/
-
/*
** Convert the N-th element of pStmt->pColName[] into a string using
** xFunc() then return that string. If N is out of range, return 0.
@@ -61713,7 +68347,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
/*
** Return the name of the database from which a result column derives.
** NULL is returned if the result column is an expression or constant or
-** anything else which is not an unabiguous reference to a database column.
+** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){
return columnName(
@@ -61729,7 +68363,7 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N
/*
** Return the name of the table from which a result column derives.
** NULL is returned if the result column is an expression or constant or
-** anything else which is not an unabiguous reference to a database column.
+** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){
return columnName(
@@ -61745,7 +68379,7 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){
/*
** Return the name of the table column from which a result column derives.
** NULL is returned if the result column is an expression or constant or
-** anything else which is not an unabiguous reference to a database column.
+** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){
return columnName(
@@ -61782,14 +68416,14 @@ static int vdbeUnbind(Vdbe *p, int i){
}
sqlite3_mutex_enter(p->db->mutex);
if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
- sqlite3Error(p->db, SQLITE_MISUSE, 0);
+ sqlite3Error(p->db, SQLITE_MISUSE);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
return SQLITE_MISUSE_BKPT;
}
if( i<1 || i>p->nVar ){
- sqlite3Error(p->db, SQLITE_RANGE, 0);
+ sqlite3Error(p->db, SQLITE_RANGE);
sqlite3_mutex_leave(p->db->mutex);
return SQLITE_RANGE;
}
@@ -61797,7 +68431,7 @@ static int vdbeUnbind(Vdbe *p, int i){
pVar = &p->aVar[i];
sqlite3VdbeMemRelease(pVar);
pVar->flags = MEM_Null;
- sqlite3Error(p->db, SQLITE_OK, 0);
+ sqlite3Error(p->db, SQLITE_OK);
/* If the bit corresponding to this variable in Vdbe.expmask is set, then
** binding a new value to this variable invalidates the current query plan.
@@ -61839,7 +68473,7 @@ static int bindText(
if( rc==SQLITE_OK && encoding!=0 ){
rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
}
- sqlite3Error(p->db, rc, 0);
+ sqlite3Error(p->db, rc);
rc = sqlite3ApiExit(p->db, rc);
}
sqlite3_mutex_leave(p->db->mutex);
@@ -61862,6 +68496,20 @@ SQLITE_API int sqlite3_bind_blob(
){
return bindText(pStmt, i, zData, nData, xDel, 0);
}
+SQLITE_API int sqlite3_bind_blob64(
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ sqlite3_uint64 nData,
+ void (*xDel)(void*)
+){
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( nData>0x7fffffff ){
+ return invokeValueDestructor(zData, xDel, 0);
+ }else{
+ return bindText(pStmt, i, zData, (int)nData, xDel, 0);
+ }
+}
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
@@ -61903,6 +68551,22 @@ SQLITE_API int sqlite3_bind_text(
){
return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8);
}
+SQLITE_API int sqlite3_bind_text64(
+ sqlite3_stmt *pStmt,
+ int i,
+ const char *zData,
+ sqlite3_uint64 nData,
+ void (*xDel)(void*),
+ unsigned char enc
+){
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( nData>0x7fffffff ){
+ return invokeValueDestructor(zData, xDel, 0);
+ }else{
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ return bindText(pStmt, i, zData, (int)nData, xDel, enc);
+ }
+}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API int sqlite3_bind_text16(
sqlite3_stmt *pStmt,
@@ -61916,13 +68580,13 @@ SQLITE_API int sqlite3_bind_text16(
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
int rc;
- switch( pValue->type ){
+ switch( sqlite3_value_type((sqlite3_value*)pValue) ){
case SQLITE_INTEGER: {
rc = sqlite3_bind_int64(pStmt, i, pValue->u.i);
break;
}
case SQLITE_FLOAT: {
- rc = sqlite3_bind_double(pStmt, i, pValue->r);
+ rc = sqlite3_bind_double(pStmt, i, pValue->u.r);
break;
}
case SQLITE_BLOB: {
@@ -61992,7 +68656,7 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nNa
if( zName ){
for(i=0; i<p->nzVar; i++){
const char *z = p->azVar[i];
- if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){
+ if( z && strncmp(z,zName,nName)==0 && z[nName]==0 ){
return i+1;
}
}
@@ -62025,7 +68689,7 @@ SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt
** Deprecated external interface. Internal/core SQLite code
** should call sqlite3TransferBindings.
**
-** Is is misuse to call this routine with statements from different
+** It is misuse to call this routine with statements from different
** database connections. But as this is a deprecated interface, we
** will not bother to check for that condition.
**
@@ -62068,6 +68732,14 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
}
/*
+** Return true if the prepared statement is in need of being reset.
+*/
+SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
+ Vdbe *v = (Vdbe*)pStmt;
+ return v!=0 && v->pc>=0 && v->magic==VDBE_MAGIC_RUN;
+}
+
+/*
** Return a pointer to the next prepared statement after pStmt associated
** with database connection pDb. If pStmt is NULL, return the first
** prepared statement for the database connection. Return NULL if there
@@ -62090,9 +68762,9 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
*/
SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
Vdbe *pVdbe = (Vdbe*)pStmt;
- int v = pVdbe->aCounter[op-1];
- if( resetFlag ) pVdbe->aCounter[op-1] = 0;
- return v;
+ u32 v = pVdbe->aCounter[op];
+ if( resetFlag ) pVdbe->aCounter[op] = 0;
+ return (int)v;
}
/************** End of vdbeapi.c *********************************************/
@@ -62111,6 +68783,8 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
**
** This file contains code used to insert the values of host parameters
** (aka "wildcards") into the SQL text output by sqlite3_trace().
+**
+** The Vdbe parse-tree explainer is also found here.
*/
#ifndef SQLITE_OMIT_TRACE
@@ -62142,19 +68816,24 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
/*
** This function returns a pointer to a nul-terminated string in memory
-** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
+** obtained from sqlite3DbMalloc(). If sqlite3.nVdbeExec is 1, then the
** string contains a copy of zRawSql but with host parameters expanded to
-** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
+** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
+** If the SQLITE_TRACE_SIZE_LIMIT macro is defined to an integer, then
+** then long strings and blobs are truncated to that many bytes. This
+** can be used to prevent unreasonably large trace strings when dealing
+** with large (multi-megabyte) strings and blobs.
+**
** The calling function is responsible for making sure the memory returned
** is eventually freed.
**
** ALGORITHM: Scan the input string looking for host parameters in any of
** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within
** string literals, quoted identifier names, and comments. For text forms,
-** the host parameter index is found by scanning the perpared
+** the host parameter index is found by scanning the prepared
** statement for the corresponding OP_Variable opcode. Once the host
** parameter index is known, locate the value in p->aVar[]. Then render
** the value as a literal in place of the host parameter name.
@@ -62177,11 +68856,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
out.db = db;
- if( db->vdbeExecCnt>1 ){
+ if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
sqlite3StrAccumAppend(&out, "-- ", 3);
+ assert( (zRawSql - zStart) > 0 );
sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
}
}else{
@@ -62214,34 +68894,57 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
if( pVar->flags & MEM_Null ){
sqlite3StrAccumAppend(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
- sqlite3XPrintf(&out, "%lld", pVar->u.i);
+ sqlite3XPrintf(&out, 0, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
- sqlite3XPrintf(&out, "%!.15g", pVar->r);
+ sqlite3XPrintf(&out, 0, "%!.15g", pVar->u.r);
}else if( pVar->flags & MEM_Str ){
+ int nOut; /* Number of bytes of the string text to include in output */
#ifndef SQLITE_OMIT_UTF16
u8 enc = ENC(db);
+ Mem utf8;
if( enc!=SQLITE_UTF8 ){
- Mem utf8;
memset(&utf8, 0, sizeof(utf8));
utf8.db = db;
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
- sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
- sqlite3VdbeMemRelease(&utf8);
- }else
+ pVar = &utf8;
+ }
#endif
- {
- sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
+ nOut = pVar->n;
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut>SQLITE_TRACE_SIZE_LIMIT ){
+ nOut = SQLITE_TRACE_SIZE_LIMIT;
+ while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
+ }
+#endif
+ sqlite3XPrintf(&out, 0, "'%.*q'", nOut, pVar->z);
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
}
+#endif
+#ifndef SQLITE_OMIT_UTF16
+ if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
+#endif
}else if( pVar->flags & MEM_Zero ){
- sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
+ sqlite3XPrintf(&out, 0, "zeroblob(%d)", pVar->u.nZero);
}else{
+ int nOut; /* Number of bytes of the blob to include in output */
assert( pVar->flags & MEM_Blob );
sqlite3StrAccumAppend(&out, "x'", 2);
- for(i=0; i<pVar->n; i++){
- sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
+ nOut = pVar->n;
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
+#endif
+ for(i=0; i<nOut; i++){
+ sqlite3XPrintf(&out, 0, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ }
+#endif
}
}
}
@@ -62263,33 +68966,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
** May you share freely, never taking more than you give.
**
*************************************************************************
-** The code in this file implements execution method of the
-** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c")
-** handles housekeeping details such as creating and deleting
-** VDBE instances. This file is solely interested in executing
-** the VDBE program.
-**
-** In the external interface, an "sqlite3_stmt*" is an opaque pointer
-** to a VDBE.
-**
-** The SQL parser generates a program which is then executed by
-** the VDBE to do the work of the SQL statement. VDBE programs are
-** similar in form to assembly language. The program consists of
-** a linear sequence of operations. Each operation has an opcode
-** and 5 operands. Operands P1, P2, and P3 are integers. Operand P4
-** is a null-terminated string. Operand P5 is an unsigned character.
-** Few opcodes use all 5 operands.
-**
-** Computation results are stored on a set of registers numbered beginning
-** with 1 and going up to Vdbe.nMem. Each register can store
-** either an integer, a null-terminated string, a floating point
-** number, or the SQL "NULL" value. An implicit conversion from one
-** type to the other occurs as necessary.
-**
-** Most of the code in this file is taken up by the sqlite3VdbeExec()
-** function which does the work of interpreting a VDBE program.
-** But other routines are also provided to help in building up
-** a program instruction by instruction.
+** The code in this file implements the function that runs the
+** bytecode of a prepared statement.
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files. The formatting
@@ -62301,10 +68979,14 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
/*
** Invoke this macro on memory cells just prior to changing the
** value of the cell. This macro verifies that shallow copies are
-** not misused.
+** not misused. A shallow copy of a string or blob just copies a
+** pointer to the string or blob, not the content. If the original
+** is changed while the copy is still in use, the string or blob might
+** be changed out from under the copy. This macro verifies that nothing
+** like that ever happens.
*/
#ifdef SQLITE_DEBUG
-# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M)
+# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M)
#else
# define memAboutToChange(P,M)
#endif
@@ -62322,8 +69004,8 @@ SQLITE_API int sqlite3_search_count = 0;
/*
** When this global variable is positive, it gets decremented once before
-** each instruction in the VDBE. When reaches zero, the u1.isInterrupted
-** field of the sqlite3 structure is set in order to simulate and interrupt.
+** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted
+** field of the sqlite3 structure is set in order to simulate an interrupt.
**
** This facility is used for testing purposes only. It does not function
** in an ordinary build.
@@ -62360,7 +69042,7 @@ static void updateMaxBlobsize(Mem *p){
#endif
/*
-** The next global variable is incremented each type the OP_Found opcode
+** The next global variable is incremented each time the OP_Found opcode
** is executed. This is used to test whether or not the foreign key
** operation implemented using OP_FkIsZero is working. This variable
** has no function other than to help verify the correct operation of the
@@ -62381,11 +69063,45 @@ SQLITE_API int sqlite3_found_count = 0;
#endif
/*
+** Invoke the VDBE coverage callback, if that callback is defined. This
+** feature is used for test suite validation only and does not appear an
+** production builds.
+**
+** M is an integer, 2 or 3, that indices how many different ways the
+** branch can go. It is usually 2. "I" is the direction the branch
+** goes. 0 means falls through. 1 means branch is taken. 2 means the
+** second alternative branch is taken.
+**
+** iSrcLine is the source code line (from the __LINE__ macro) that
+** generated the VDBE instruction. This instrumentation assumes that all
+** source code is in a single file (the amalgamation). Special values 1
+** and 2 for the iSrcLine parameter mean that this particular branch is
+** always taken or never taken, respectively.
+*/
+#if !defined(SQLITE_VDBE_COVERAGE)
+# define VdbeBranchTaken(I,M)
+#else
+# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M)
+ static void vdbeTakeBranch(int iSrcLine, u8 I, u8 M){
+ if( iSrcLine<=2 && ALWAYS(iSrcLine>0) ){
+ M = iSrcLine;
+ /* Assert the truth of VdbeCoverageAlwaysTaken() and
+ ** VdbeCoverageNeverTaken() */
+ assert( (M & I)==I );
+ }else{
+ if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
+ sqlite3GlobalConfig.xVdbeBranch(sqlite3GlobalConfig.pVdbeBranchArg,
+ iSrcLine,I,M);
+ }
+ }
+#endif
+
+/*
** Convert the given register into a string if it isn't one
** already. Return non-zero if a malloc() fails.
*/
#define Stringify(P, enc) \
- if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc)) \
+ if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc,0)) \
{ goto no_mem; }
/*
@@ -62397,41 +69113,14 @@ SQLITE_API int sqlite3_found_count = 0;
**
** This routine converts an ephemeral string into a dynamically allocated
** string that the register itself controls. In other words, it
-** converts an MEM_Ephem string into an MEM_Dyn string.
+** converts an MEM_Ephem string into a string with P.z==P.zMalloc.
*/
#define Deephemeralize(P) \
if( ((P)->flags&MEM_Ephem)!=0 \
&& sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
-/*
-** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*)
-** P if required.
-*/
-#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
-
-/*
-** Argument pMem points at a register that will be passed to a
-** user-defined function or returned to the user as the result of a query.
-** This routine sets the pMem->type variable used by the sqlite3_value_*()
-** routines.
-*/
-SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem){
- int flags = pMem->flags;
- if( flags & MEM_Null ){
- pMem->type = SQLITE_NULL;
- }
- else if( flags & MEM_Int ){
- pMem->type = SQLITE_INTEGER;
- }
- else if( flags & MEM_Real ){
- pMem->type = SQLITE_FLOAT;
- }
- else if( flags & MEM_Str ){
- pMem->type = SQLITE_TEXT;
- }else{
- pMem->type = SQLITE_BLOB;
- }
-}
+/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
+#define isSorter(x) ((x)->pSorter!=0)
/*
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
@@ -62441,7 +69130,7 @@ static VdbeCursor *allocateCursor(
Vdbe *p, /* The virtual machine */
int iCur, /* Index of the new VdbeCursor */
int nField, /* Number of fields in the table or index */
- int iDb, /* When database the cursor belongs to, or -1 */
+ int iDb, /* Database the cursor belongs to, or -1 */
int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */
){
/* Find the memory cell that will be used to store the blob of memory
@@ -62467,26 +69156,23 @@ static VdbeCursor *allocateCursor(
int nByte;
VdbeCursor *pCx = 0;
nByte =
- ROUND8(sizeof(VdbeCursor)) +
- (isBtreeCursor?sqlite3BtreeCursorSize():0) +
- 2*nField*sizeof(u32);
+ ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
+ (isBtreeCursor?sqlite3BtreeCursorSize():0);
assert( iCur<p->nCursor );
if( p->apCsr[iCur] ){
sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
p->apCsr[iCur] = 0;
}
- if( SQLITE_OK==sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+ if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
memset(pCx, 0, sizeof(VdbeCursor));
pCx->iDb = iDb;
pCx->nField = nField;
- if( nField ){
- pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))];
- }
+ pCx->aOffset = &pCx->aType[nField];
if( isBtreeCursor ){
pCx->pCursor = (BtCursor*)
- &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)];
+ &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
sqlite3BtreeCursorZero(pCx->pCursor);
}
}
@@ -62498,21 +69184,29 @@ static VdbeCursor *allocateCursor(
** do so without loss of information. In other words, if the string
** looks like a number, convert it into a number. If it does not
** look like a number, leave it alone.
-*/
-static void applyNumericAffinity(Mem *pRec){
- if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
- double rValue;
- i64 iValue;
- u8 enc = pRec->enc;
- if( (pRec->flags&MEM_Str)==0 ) return;
- if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
- if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
- pRec->u.i = iValue;
- pRec->flags |= MEM_Int;
- }else{
- pRec->r = rValue;
- pRec->flags |= MEM_Real;
- }
+**
+** If the bTryForInt flag is true, then extra effort is made to give
+** an integer representation. Strings that look like floating point
+** values but which have no fractional component (example: '48.00')
+** will have a MEM_Int representation when bTryForInt is true.
+**
+** If bTryForInt is false, then if the input string contains a decimal
+** point or exponential notation, the result is only MEM_Real, even
+** if there is an exact integer representation of the quantity.
+*/
+static void applyNumericAffinity(Mem *pRec, int bTryForInt){
+ double rValue;
+ i64 iValue;
+ u8 enc = pRec->enc;
+ assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real))==MEM_Str );
+ if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
+ if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
+ pRec->u.i = iValue;
+ pRec->flags |= MEM_Int;
+ }else{
+ pRec->u.r = rValue;
+ pRec->flags |= MEM_Real;
+ if( bTryForInt ) sqlite3VdbeIntegerAffinity(pRec);
}
}
@@ -62539,21 +69233,23 @@ static void applyAffinity(
char affinity, /* The affinity to be applied */
u8 enc /* Use this text encoding */
){
- if( affinity==SQLITE_AFF_TEXT ){
+ if( affinity>=SQLITE_AFF_NUMERIC ){
+ assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
+ || affinity==SQLITE_AFF_NUMERIC );
+ if( (pRec->flags & MEM_Int)==0 ){
+ if( (pRec->flags & MEM_Real)==0 ){
+ if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1);
+ }else{
+ sqlite3VdbeIntegerAffinity(pRec);
+ }
+ }
+ }else if( affinity==SQLITE_AFF_TEXT ){
/* Only attempt the conversion to TEXT if there is an integer or real
** representation (blob and NULL do not get converted) but no string
** representation.
*/
if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){
- sqlite3VdbeMemStringify(pRec, enc);
- }
- pRec->flags &= ~(MEM_Real|MEM_Int);
- }else if( affinity!=SQLITE_AFF_NONE ){
- assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
- || affinity==SQLITE_AFF_NUMERIC );
- applyNumericAffinity(pRec);
- if( pRec->flags & MEM_Real ){
- sqlite3VdbeIntegerAffinity(pRec);
+ sqlite3VdbeMemStringify(pRec, enc, 1);
}
}
}
@@ -62565,12 +69261,13 @@ static void applyAffinity(
** loss of information and return the revised type of the argument.
*/
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){
- Mem *pMem = (Mem*)pVal;
- if( pMem->type==SQLITE_TEXT ){
- applyNumericAffinity(pMem);
- sqlite3VdbeMemStoreType(pMem);
+ int eType = sqlite3_value_type(pVal);
+ if( eType==SQLITE_TEXT ){
+ Mem *pMem = (Mem*)pVal;
+ applyNumericAffinity(pMem, 0);
+ eType = sqlite3_value_type(pVal);
}
- return pMem->type;
+ return eType;
}
/*
@@ -62585,6 +69282,41 @@ SQLITE_PRIVATE void sqlite3ValueApplyAffinity(
applyAffinity((Mem *)pVal, affinity, enc);
}
+/*
+** pMem currently only holds a string type (or maybe a BLOB that we can
+** interpret as a string if we want to). Compute its corresponding
+** numeric type, if has one. Set the pMem->u.r and pMem->u.i fields
+** accordingly.
+*/
+static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){
+ assert( (pMem->flags & (MEM_Int|MEM_Real))==0 );
+ assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 );
+ if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){
+ return 0;
+ }
+ if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==SQLITE_OK ){
+ return MEM_Int;
+ }
+ return MEM_Real;
+}
+
+/*
+** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
+** none.
+**
+** Unlike applyNumericAffinity(), this routine does not modify pMem->flags.
+** But it does set pMem->u.r and pMem->u.i appropriately.
+*/
+static u16 numericType(Mem *pMem){
+ if( pMem->flags & (MEM_Int|MEM_Real) ){
+ return pMem->flags & (MEM_Int|MEM_Real);
+ }
+ if( pMem->flags & (MEM_Str|MEM_Blob) ){
+ return computeNumericType(pMem);
+ }
+ return 0;
+}
+
#ifdef SQLITE_DEBUG
/*
** Write a nice string representation of the contents of cell pMem
@@ -62672,35 +69404,36 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
/*
** Print the value of a register for tracing purposes:
*/
-static void memTracePrint(FILE *out, Mem *p){
- if( p->flags & MEM_Null ){
- fprintf(out, " NULL");
+static void memTracePrint(Mem *p){
+ if( p->flags & MEM_Undefined ){
+ printf(" undefined");
+ }else if( p->flags & MEM_Null ){
+ printf(" NULL");
}else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
- fprintf(out, " si:%lld", p->u.i);
+ printf(" si:%lld", p->u.i);
}else if( p->flags & MEM_Int ){
- fprintf(out, " i:%lld", p->u.i);
+ printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
- fprintf(out, " r:%g", p->r);
+ printf(" r:%g", p->u.r);
#endif
}else if( p->flags & MEM_RowSet ){
- fprintf(out, " (rowset)");
+ printf(" (rowset)");
}else{
char zBuf[200];
sqlite3VdbeMemPrettyPrint(p, zBuf);
- fprintf(out, " ");
- fprintf(out, "%s", zBuf);
+ printf(" %s", zBuf);
}
}
-static void registerTrace(FILE *out, int iReg, Mem *p){
- fprintf(out, "REG[%d] = ", iReg);
- memTracePrint(out, p);
- fprintf(out, "\n");
+static void registerTrace(int iReg, Mem *p){
+ printf("REG[%d] = ", iReg);
+ memTracePrint(p);
+ printf("\n");
}
#endif
#ifdef SQLITE_DEBUG
-# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M)
+# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M)
#else
# define REGISTER_TRACE(R,M)
#endif
@@ -62805,20 +69538,6 @@ SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
#endif
-/*
-** The CHECK_FOR_INTERRUPT macro defined here looks to see if the
-** sqlite3_interrupt() routine has been called. If it has been, then
-** processing of the VDBE program is interrupted.
-**
-** This macro added to every instruction that does a jump in order to
-** implement a loop. This test used to be on every single instruction,
-** but that meant we more testing that we needed. By only testing the
-** flag on jump instructions, we get a (small) speed improvement.
-*/
-#define CHECK_FOR_INTERRUPT \
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
-
-
#ifndef NDEBUG
/*
** This function is only called from within an assert() expression. It
@@ -62839,50 +69558,10 @@ static int checkSavepointCount(sqlite3 *db){
}
#endif
-/*
-** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
-** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
-** in memory obtained from sqlite3DbMalloc).
-*/
-static void importVtabErrMsg(Vdbe *p, sqlite3_vtab *pVtab){
- sqlite3 *db = p->db;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
- pVtab->zErrMsg = 0;
-}
-
/*
-** Execute as much of a VDBE program as we can then return.
-**
-** sqlite3VdbeMakeReady() must be called before this routine in order to
-** close the program with a final OP_Halt and to set up the callbacks
-** and the error message pointer.
-**
-** Whenever a row or result data is available, this routine will either
-** invoke the result callback (if there is one) or return with
-** SQLITE_ROW.
-**
-** If an attempt is made to open a locked database, then this routine
-** will either invoke the busy callback (if there is one) or it will
-** return SQLITE_BUSY.
-**
-** If an error occurs, an error message is written to memory obtained
-** from sqlite3_malloc() and p->zErrMsg is made to point to that memory.
-** The error code is stored in p->rc and this routine returns SQLITE_ERROR.
-**
-** If the callback ever returns non-zero, then the program exits
-** immediately. There will be no error message but the p->rc field is
-** set to SQLITE_ABORT and this routine will return SQLITE_ERROR.
-**
-** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this
-** routine to return SQLITE_ERROR.
-**
-** Other fatal errors return SQLITE_ERROR.
-**
-** After this routine has finished, sqlite3VdbeFinalize() should be
-** used to clean up the mess that was left behind.
+** Execute as much of a VDBE program as we can.
+** This is the core of sqlite3_step().
*/
SQLITE_PRIVATE int sqlite3VdbeExec(
Vdbe *p /* The VDBE */
@@ -62894,431 +69573,22 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
+ int iCompare = 0; /* Result of last OP_Compare operation */
+ unsigned nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- int checkProgress; /* True if progress callbacks are enabled */
- int nProgressOps = 0; /* Opcodes executed since progress callback. */
+ unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
#endif
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
- int iCompare = 0; /* Result of last OP_Compare operation */
int *aPermute = 0; /* Permutation of columns for OP_Compare */
i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
#ifdef VDBE_PROFILE
u64 start; /* CPU clock count at start of opcode */
- int origPc; /* Program counter at start of opcode */
#endif
- /********************************************************************
- ** Automatically generated code
- **
- ** The following union is automatically generated by the
- ** vdbe-compress.tcl script. The purpose of this union is to
- ** reduce the amount of stack space required by this function.
- ** See comments in the vdbe-compress.tcl script for details.
- */
- union vdbeExecUnion {
- struct OP_Yield_stack_vars {
- int pcDest;
- } aa;
- struct OP_Variable_stack_vars {
- Mem *pVar; /* Value being transferred */
- } ab;
- struct OP_Move_stack_vars {
- char *zMalloc; /* Holding variable for allocated memory */
- int n; /* Number of registers left to copy */
- int p1; /* Register to copy from */
- int p2; /* Register to copy to */
- } ac;
- struct OP_ResultRow_stack_vars {
- Mem *pMem;
- int i;
- } ad;
- struct OP_Concat_stack_vars {
- i64 nByte;
- } ae;
- struct OP_Remainder_stack_vars {
- int flags; /* Combined MEM_* flags from both inputs */
- i64 iA; /* Integer value of left operand */
- i64 iB; /* Integer value of right operand */
- double rA; /* Real value of left operand */
- double rB; /* Real value of right operand */
- } af;
- struct OP_Function_stack_vars {
- int i;
- Mem *pArg;
- sqlite3_context ctx;
- sqlite3_value **apVal;
- int n;
- } ag;
- struct OP_ShiftRight_stack_vars {
- i64 iA;
- u64 uA;
- i64 iB;
- u8 op;
- } ah;
- struct OP_Ge_stack_vars {
- int res; /* Result of the comparison of pIn1 against pIn3 */
- char affinity; /* Affinity to use for comparison */
- u16 flags1; /* Copy of initial value of pIn1->flags */
- u16 flags3; /* Copy of initial value of pIn3->flags */
- } ai;
- struct OP_Compare_stack_vars {
- int n;
- int i;
- int p1;
- int p2;
- const KeyInfo *pKeyInfo;
- int idx;
- CollSeq *pColl; /* Collating sequence to use on this term */
- int bRev; /* True for DESCENDING sort order */
- } aj;
- struct OP_Or_stack_vars {
- int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
- int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
- } ak;
- struct OP_IfNot_stack_vars {
- int c;
- } al;
- struct OP_Column_stack_vars {
- u32 payloadSize; /* Number of bytes in the record */
- i64 payloadSize64; /* Number of bytes in the record */
- int p1; /* P1 value of the opcode */
- int p2; /* column number to retrieve */
- VdbeCursor *pC; /* The VDBE cursor */
- char *zRec; /* Pointer to complete record-data */
- BtCursor *pCrsr; /* The BTree cursor */
- u32 *aType; /* aType[i] holds the numeric type of the i-th column */
- u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
- int nField; /* number of fields in the record */
- int len; /* The length of the serialized data for the column */
- int i; /* Loop counter */
- char *zData; /* Part of the record being decoded */
- Mem *pDest; /* Where to write the extracted value */
- Mem sMem; /* For storing the record being decoded */
- u8 *zIdx; /* Index into header */
- u8 *zEndHdr; /* Pointer to first byte after the header */
- u32 offset; /* Offset into the data */
- u32 szField; /* Number of bytes in the content of a field */
- int szHdr; /* Size of the header size field at start of record */
- int avail; /* Number of bytes of available data */
- Mem *pReg; /* PseudoTable input register */
- } am;
- struct OP_Affinity_stack_vars {
- const char *zAffinity; /* The affinity to be applied */
- char cAff; /* A single character of affinity */
- } an;
- struct OP_MakeRecord_stack_vars {
- u8 *zNewRecord; /* A buffer to hold the data for the new record */
- Mem *pRec; /* The new record */
- u64 nData; /* Number of bytes of data space */
- int nHdr; /* Number of bytes of header space */
- i64 nByte; /* Data space required for this record */
- int nZero; /* Number of zero bytes at the end of the record */
- int nVarint; /* Number of bytes in a varint */
- u32 serial_type; /* Type field */
- Mem *pData0; /* First field to be combined into the record */
- Mem *pLast; /* Last field of the record */
- int nField; /* Number of fields in the record */
- char *zAffinity; /* The affinity string for the record */
- int file_format; /* File format to use for encoding */
- int i; /* Space used in zNewRecord[] */
- int len; /* Length of a field */
- } ao;
- struct OP_Count_stack_vars {
- i64 nEntry;
- BtCursor *pCrsr;
- } ap;
- struct OP_Savepoint_stack_vars {
- int p1; /* Value of P1 operand */
- char *zName; /* Name of savepoint */
- int nName;
- Savepoint *pNew;
- Savepoint *pSavepoint;
- Savepoint *pTmp;
- int iSavepoint;
- int ii;
- } aq;
- struct OP_AutoCommit_stack_vars {
- int desiredAutoCommit;
- int iRollback;
- int turnOnAC;
- } ar;
- struct OP_Transaction_stack_vars {
- Btree *pBt;
- } as;
- struct OP_ReadCookie_stack_vars {
- int iMeta;
- int iDb;
- int iCookie;
- } at;
- struct OP_SetCookie_stack_vars {
- Db *pDb;
- } au;
- struct OP_VerifyCookie_stack_vars {
- int iMeta;
- int iGen;
- Btree *pBt;
- } av;
- struct OP_OpenWrite_stack_vars {
- int nField;
- KeyInfo *pKeyInfo;
- int p2;
- int iDb;
- int wrFlag;
- Btree *pX;
- VdbeCursor *pCur;
- Db *pDb;
- } aw;
- struct OP_OpenEphemeral_stack_vars {
- VdbeCursor *pCx;
- } ax;
- struct OP_OpenPseudo_stack_vars {
- VdbeCursor *pCx;
- } ay;
- struct OP_SeekGt_stack_vars {
- int res;
- int oc;
- VdbeCursor *pC;
- UnpackedRecord r;
- int nField;
- i64 iKey; /* The rowid we are to seek to */
- } az;
- struct OP_Seek_stack_vars {
- VdbeCursor *pC;
- } ba;
- struct OP_Found_stack_vars {
- int alreadyExists;
- VdbeCursor *pC;
- int res;
- UnpackedRecord *pIdxKey;
- UnpackedRecord r;
- char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
- } bb;
- struct OP_IsUnique_stack_vars {
- u16 ii;
- VdbeCursor *pCx;
- BtCursor *pCrsr;
- u16 nField;
- Mem *aMx;
- UnpackedRecord r; /* B-Tree index search key */
- i64 R; /* Rowid stored in register P3 */
- } bc;
- struct OP_NotExists_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- u64 iKey;
- } bd;
- struct OP_NewRowid_stack_vars {
- i64 v; /* The new rowid */
- VdbeCursor *pC; /* Cursor of table to get the new rowid */
- int res; /* Result of an sqlite3BtreeLast() */
- int cnt; /* Counter to limit the number of searches */
- Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
- VdbeFrame *pFrame; /* Root frame of VDBE */
- } be;
- struct OP_InsertInt_stack_vars {
- Mem *pData; /* MEM cell holding data for the record to be inserted */
- Mem *pKey; /* MEM cell holding key for the record */
- i64 iKey; /* The integer ROWID or key for the record to be inserted */
- VdbeCursor *pC; /* Cursor to table into which insert is written */
- int nZero; /* Number of zero-bytes to append */
- int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
- const char *zDb; /* database name - used by the update hook */
- const char *zTbl; /* Table name - used by the opdate hook */
- int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
- } bf;
- struct OP_Delete_stack_vars {
- i64 iKey;
- VdbeCursor *pC;
- } bg;
- struct OP_RowData_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- u32 n;
- i64 n64;
- } bh;
- struct OP_Rowid_stack_vars {
- VdbeCursor *pC;
- i64 v;
- sqlite3_vtab *pVtab;
- const sqlite3_module *pModule;
- } bi;
- struct OP_NullRow_stack_vars {
- VdbeCursor *pC;
- } bj;
- struct OP_Last_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- } bk;
- struct OP_Rewind_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- } bl;
- struct OP_Next_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- } bm;
- struct OP_IdxInsert_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int nKey;
- const char *zKey;
- } bn;
- struct OP_IdxDelete_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- UnpackedRecord r;
- } bo;
- struct OP_IdxRowid_stack_vars {
- BtCursor *pCrsr;
- VdbeCursor *pC;
- i64 rowid;
- } bp;
- struct OP_IdxGE_stack_vars {
- VdbeCursor *pC;
- int res;
- UnpackedRecord r;
- } bq;
- struct OP_Destroy_stack_vars {
- int iMoved;
- int iCnt;
- Vdbe *pVdbe;
- int iDb;
- } br;
- struct OP_Clear_stack_vars {
- int nChange;
- } bs;
- struct OP_CreateTable_stack_vars {
- int pgno;
- int flags;
- Db *pDb;
- } bt;
- struct OP_ParseSchema_stack_vars {
- int iDb;
- const char *zMaster;
- char *zSql;
- InitData initData;
- } bu;
- struct OP_IntegrityCk_stack_vars {
- int nRoot; /* Number of tables to check. (Number of root pages.) */
- int *aRoot; /* Array of rootpage numbers for tables to be checked */
- int j; /* Loop counter */
- int nErr; /* Number of errors reported */
- char *z; /* Text of the error report */
- Mem *pnErr; /* Register keeping track of errors remaining */
- } bv;
- struct OP_RowSetRead_stack_vars {
- i64 val;
- } bw;
- struct OP_RowSetTest_stack_vars {
- int iSet;
- int exists;
- } bx;
- struct OP_Program_stack_vars {
- int nMem; /* Number of memory registers for sub-program */
- int nByte; /* Bytes of runtime space required for sub-program */
- Mem *pRt; /* Register to allocate runtime space */
- Mem *pMem; /* Used to iterate through memory cells */
- Mem *pEnd; /* Last memory cell in new array */
- VdbeFrame *pFrame; /* New vdbe frame to execute in */
- SubProgram *pProgram; /* Sub-program to execute */
- void *t; /* Token identifying trigger */
- } by;
- struct OP_Param_stack_vars {
- VdbeFrame *pFrame;
- Mem *pIn;
- } bz;
- struct OP_MemMax_stack_vars {
- Mem *pIn1;
- VdbeFrame *pFrame;
- } ca;
- struct OP_AggStep_stack_vars {
- int n;
- int i;
- Mem *pMem;
- Mem *pRec;
- sqlite3_context ctx;
- sqlite3_value **apVal;
- } cb;
- struct OP_AggFinal_stack_vars {
- Mem *pMem;
- } cc;
- struct OP_Checkpoint_stack_vars {
- int i; /* Loop counter */
- int aRes[3]; /* Results */
- Mem *pMem; /* Write results here */
- } cd;
- struct OP_JournalMode_stack_vars {
- Btree *pBt; /* Btree to change journal mode of */
- Pager *pPager; /* Pager associated with pBt */
- int eNew; /* New journal mode */
- int eOld; /* The old journal mode */
- const char *zFilename; /* Name of database file for pPager */
- } ce;
- struct OP_IncrVacuum_stack_vars {
- Btree *pBt;
- } cf;
- struct OP_VBegin_stack_vars {
- VTable *pVTab;
- } cg;
- struct OP_VOpen_stack_vars {
- VdbeCursor *pCur;
- sqlite3_vtab_cursor *pVtabCursor;
- sqlite3_vtab *pVtab;
- sqlite3_module *pModule;
- } ch;
- struct OP_VFilter_stack_vars {
- int nArg;
- int iQuery;
- const sqlite3_module *pModule;
- Mem *pQuery;
- Mem *pArgc;
- sqlite3_vtab_cursor *pVtabCursor;
- sqlite3_vtab *pVtab;
- VdbeCursor *pCur;
- int res;
- int i;
- Mem **apArg;
- } ci;
- struct OP_VColumn_stack_vars {
- sqlite3_vtab *pVtab;
- const sqlite3_module *pModule;
- Mem *pDest;
- sqlite3_context sContext;
- } cj;
- struct OP_VNext_stack_vars {
- sqlite3_vtab *pVtab;
- const sqlite3_module *pModule;
- int res;
- VdbeCursor *pCur;
- } ck;
- struct OP_VRename_stack_vars {
- sqlite3_vtab *pVtab;
- Mem *pName;
- } cl;
- struct OP_VUpdate_stack_vars {
- sqlite3_vtab *pVtab;
- sqlite3_module *pModule;
- int nArg;
- int i;
- sqlite_int64 rowid;
- Mem **apArg;
- Mem *pX;
- } cm;
- struct OP_Trace_stack_vars {
- char *zTrace;
- char *z;
- } cn;
- } u;
- /* End automatically generated code
- ********************************************************************/
+ /*** INSERT STACK UNION HERE ***/
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
sqlite3VdbeEnter(p);
@@ -63328,24 +69598,49 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
goto no_mem;
}
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
+ p->iCurrentTime = 0;
assert( p->explain==0 );
p->pResultSet = 0;
db->busyHandler.nBusy = 0;
- CHECK_FOR_INTERRUPT;
+ if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
sqlite3VdbeIOTraceSql(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- checkProgress = db->xProgress!=0;
+ if( db->xProgress ){
+ assert( 0 < db->nProgressOps );
+ nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
+ if( nProgressLimit==0 ){
+ nProgressLimit = db->nProgressOps;
+ }else{
+ nProgressLimit %= (unsigned)db->nProgressOps;
+ }
+ }
#endif
#ifdef SQLITE_DEBUG
sqlite3BeginBenignMalloc();
- if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){
+ if( p->pc==0
+ && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
+ ){
int i;
- printf("VDBE Program Listing:\n");
+ int once = 1;
sqlite3VdbePrintSql(p);
- for(i=0; i<p->nOp; i++){
- sqlite3VdbePrintOp(stdout, i, &aOp[i]);
+ if( p->db->flags & SQLITE_VdbeListing ){
+ printf("VDBE Program Listing:\n");
+ for(i=0; i<p->nOp; i++){
+ sqlite3VdbePrintOp(stdout, i, &aOp[i]);
+ }
+ }
+ if( p->db->flags & SQLITE_VdbeEQP ){
+ for(i=0; i<p->nOp; i++){
+ if( aOp[i].opcode==OP_Explain ){
+ if( once ) printf("VDBE Query Plan:\n");
+ printf("%s\n", aOp[i].p4.z);
+ once = 0;
+ }
+ }
}
+ if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n");
}
sqlite3EndBenignMalloc();
#endif
@@ -63353,20 +69648,16 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( pc>=0 && pc<p->nOp );
if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
- origPc = pc;
start = sqlite3Hwtime();
#endif
+ nVmStep++;
pOp = &aOp[pc];
/* Only allow tracing if SQLITE_DEBUG is defined.
*/
#ifdef SQLITE_DEBUG
- if( p->trace ){
- if( pc==0 ){
- printf("VDBE Execution Trace:\n");
- sqlite3VdbePrintSql(p);
- }
- sqlite3VdbePrintOp(p->trace, pc, pOp);
+ if( db->flags & SQLITE_VdbeTrace ){
+ sqlite3VdbePrintOp(stdout, pc, pOp);
}
#endif
@@ -63383,28 +69674,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
#endif
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- /* Call the progress callback if it is configured and the required number
- ** of VDBE ops have been executed (either since this invocation of
- ** sqlite3VdbeExec() or since last time the progress callback was called).
- ** If the progress callback returns non-zero, exit the virtual machine with
- ** a return code SQLITE_ABORT.
- */
- if( checkProgress ){
- if( db->nProgressOps==nProgressOps ){
- int prc;
- prc = db->xProgress(db->pProgressArg);
- if( prc!=0 ){
- rc = SQLITE_INTERRUPT;
- goto vdbe_error_halt;
- }
- nProgressOps = 0;
- }
- nProgressOps++;
- }
-#endif
-
- /* On any opcode with the "out2-prerelase" tag, free any
+ /* On any opcode with the "out2-prerelease" tag, free any
** external allocations out of mem[p2] and set mem[p2] to be
** an undefined integer. Opcodes will either fill in the integer
** value or convert mem[p2] to a different type.
@@ -63412,10 +69682,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] );
if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
- sqlite3VdbeMemReleaseExternal(pOut);
+ if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut);
pOut->flags = MEM_Int;
}
@@ -63423,30 +69693,33 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
#ifdef SQLITE_DEBUG
if( (pOp->opflags & OPFLG_IN1)!=0 ){
assert( pOp->p1>0 );
- assert( pOp->p1<=p->nMem );
+ assert( pOp->p1<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p1]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) );
REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
}
if( (pOp->opflags & OPFLG_IN2)!=0 ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p2]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) );
REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_IN3)!=0 ){
assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p3]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) );
REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
}
if( (pOp->opflags & OPFLG_OUT2)!=0 ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
memAboutToChange(p, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_OUT3)!=0 ){
assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
memAboutToChange(p, &aMem[pOp->p3]);
}
#endif
@@ -63494,10 +69767,44 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
** The next instruction executed will be
** the one at index P2 from the beginning of
** the program.
+**
+** The P1 parameter is not actually used by this opcode. However, it
+** is sometimes set to 1 instead of 0 as a hint to the command-line shell
+** that this Goto is the bottom of a loop and that the lines from P2 down
+** to the current line should be indented for EXPLAIN output.
*/
case OP_Goto: { /* jump */
- CHECK_FOR_INTERRUPT;
pc = pOp->p2 - 1;
+
+ /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev,
+ ** OP_VNext, OP_RowSetNext, or OP_SorterNext) all jump here upon
+ ** completion. Check to see if sqlite3_interrupt() has been called
+ ** or if the progress callback needs to be invoked.
+ **
+ ** This code uses unstructured "goto" statements and does not look clean.
+ ** But that is not due to sloppy coding habits. The code is written this
+ ** way for performance, to avoid having to run the interrupt and progress
+ ** checks on every opcode. This helps sqlite3_step() to run about 1.5%
+ ** faster according to "valgrind --tool=cachegrind" */
+check_for_interrupt:
+ if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Call the progress callback if it is configured and the required number
+ ** of VDBE ops have been executed (either since this invocation of
+ ** sqlite3VdbeExec() or since last time the progress callback was called).
+ ** If the progress callback returns non-zero, exit the virtual machine with
+ ** a return code SQLITE_ABORT.
+ */
+ if( db->xProgress!=0 && nVmStep>=nProgressLimit ){
+ assert( db->nProgressOps!=0 );
+ nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
+ if( db->xProgress(db->pProgressArg) ){
+ rc = SQLITE_INTERRUPT;
+ goto vdbe_error_halt;
+ }
+ }
+#endif
+
break;
}
@@ -63506,9 +69813,10 @@ case OP_Goto: { /* jump */
** Write the current address onto register P1
** and then jump to address P2.
*/
-case OP_Gosub: { /* jump, in1 */
+case OP_Gosub: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
pIn1 = &aMem[pOp->p1];
- assert( (pIn1->flags & MEM_Dyn)==0 );
+ assert( VdbeMemDynamic(pIn1)==0 );
memAboutToChange(p, pIn1);
pIn1->flags = MEM_Int;
pIn1->u.i = pc;
@@ -63519,38 +69827,93 @@ case OP_Gosub: { /* jump, in1 */
/* Opcode: Return P1 * * * *
**
-** Jump to the next instruction after the address in register P1.
+** Jump to the next instruction after the address in register P1. After
+** the jump, register P1 becomes undefined.
*/
case OP_Return: { /* in1 */
pIn1 = &aMem[pOp->p1];
- assert( pIn1->flags & MEM_Int );
+ assert( pIn1->flags==MEM_Int );
pc = (int)pIn1->u.i;
+ pIn1->flags = MEM_Undefined;
break;
}
-/* Opcode: Yield P1 * * * *
+/* Opcode: InitCoroutine P1 P2 P3 * *
**
-** Swap the program counter with the value in register P1.
+** Set up register P1 so that it will Yield to the coroutine
+** located at address P3.
+**
+** If P2!=0 then the coroutine implementation immediately follows
+** this opcode. So jump over the coroutine implementation to
+** address P2.
+**
+** See also: EndCoroutine
*/
-case OP_Yield: { /* in1 */
-#if 0 /* local variables moved into u.aa */
+case OP_InitCoroutine: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ assert( pOp->p2>=0 && pOp->p2<p->nOp );
+ assert( pOp->p3>=0 && pOp->p3<p->nOp );
+ pOut = &aMem[pOp->p1];
+ assert( !VdbeMemDynamic(pOut) );
+ pOut->u.i = pOp->p3 - 1;
+ pOut->flags = MEM_Int;
+ if( pOp->p2 ) pc = pOp->p2 - 1;
+ break;
+}
+
+/* Opcode: EndCoroutine P1 * * * *
+**
+** The instruction at the address in register P1 is a Yield.
+** Jump to the P2 parameter of that Yield.
+** After the jump, register P1 becomes undefined.
+**
+** See also: InitCoroutine
+*/
+case OP_EndCoroutine: { /* in1 */
+ VdbeOp *pCaller;
+ pIn1 = &aMem[pOp->p1];
+ assert( pIn1->flags==MEM_Int );
+ assert( pIn1->u.i>=0 && pIn1->u.i<p->nOp );
+ pCaller = &aOp[pIn1->u.i];
+ assert( pCaller->opcode==OP_Yield );
+ assert( pCaller->p2>=0 && pCaller->p2<p->nOp );
+ pc = pCaller->p2 - 1;
+ pIn1->flags = MEM_Undefined;
+ break;
+}
+
+/* Opcode: Yield P1 P2 * * *
+**
+** Swap the program counter with the value in register P1. This
+** has the effect of yielding to a coroutine.
+**
+** If the coroutine that is launched by this instruction ends with
+** Yield or Return then continue to the next instruction. But if
+** the coroutine launched by this instruction ends with
+** EndCoroutine, then jump to P2 rather than continuing with the
+** next instruction.
+**
+** See also: InitCoroutine
+*/
+case OP_Yield: { /* in1, jump */
int pcDest;
-#endif /* local variables moved into u.aa */
pIn1 = &aMem[pOp->p1];
- assert( (pIn1->flags & MEM_Dyn)==0 );
+ assert( VdbeMemDynamic(pIn1)==0 );
pIn1->flags = MEM_Int;
- u.aa.pcDest = (int)pIn1->u.i;
+ pcDest = (int)pIn1->u.i;
pIn1->u.i = pc;
REGISTER_TRACE(pOp->p1, pIn1);
- pc = u.aa.pcDest;
+ pc = pcDest;
break;
}
-/* Opcode: HaltIfNull P1 P2 P3 P4 *
+/* Opcode: HaltIfNull P1 P2 P3 P4 P5
+** Synopsis: if r[P3]=null halt
**
** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
** value in register P3 is not NULL, then this routine is a no-op.
+** The P5 parameter should be 1.
*/
case OP_HaltIfNull: { /* in3 */
pIn3 = &aMem[pOp->p3];
@@ -63558,7 +69921,7 @@ case OP_HaltIfNull: { /* in3 */
/* Fall through into OP_Halt */
}
-/* Opcode: Halt P1 P2 * P4 *
+/* Opcode: Halt P1 P2 * P4 P5
**
** Exit immediately. All open cursors, etc are closed
** automatically.
@@ -63573,11 +69936,25 @@ case OP_HaltIfNull: { /* in3 */
**
** If P4 is not null then it is an error message string.
**
+** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
+**
+** 0: (no change)
+** 1: NOT NULL contraint failed: P4
+** 2: UNIQUE constraint failed: P4
+** 3: CHECK constraint failed: P4
+** 4: FOREIGN KEY constraint failed: P4
+**
+** If P5 is not zero and P4 is NULL, then everything after the ":" is
+** omitted.
+**
** There is an implied "Halt 0 0 0" instruction inserted at the very end of
** every program. So a jump past the last instruction of the program
** is the same as executing Halt.
*/
case OP_Halt: {
+ const char *zType;
+ const char *zLogFmt;
+
if( pOp->p1==SQLITE_OK && p->pFrame ){
/* Halt the sub-program. Return control to the parent frame. */
VdbeFrame *pFrame = p->pFrame;
@@ -63598,32 +69975,48 @@ case OP_Halt: {
aMem = p->aMem;
break;
}
-
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
p->pc = pc;
- if( pOp->p4.z ){
- assert( p->rc!=SQLITE_OK );
- sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z);
- }else if( p->rc ){
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql);
+ if( p->rc ){
+ if( pOp->p5 ){
+ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
+ "FOREIGN KEY" };
+ assert( pOp->p5>=1 && pOp->p5<=4 );
+ testcase( pOp->p5==1 );
+ testcase( pOp->p5==2 );
+ testcase( pOp->p5==3 );
+ testcase( pOp->p5==4 );
+ zType = azType[pOp->p5-1];
+ }else{
+ zType = 0;
+ }
+ assert( zType!=0 || pOp->p4.z!=0 );
+ zLogFmt = "abort at %d in [%s]: %s";
+ if( zType && pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s",
+ zType, pOp->p4.z);
+ }else if( pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
+ }else{
+ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType);
+ }
+ sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
if( rc==SQLITE_BUSY ){
p->rc = rc = SQLITE_BUSY;
}else{
- assert( rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT );
- assert( rc==SQLITE_OK || db->nDeferredCons>0 );
+ assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT );
+ assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 );
rc = p->rc ? SQLITE_ERROR : SQLITE_DONE;
}
goto vdbe_return;
}
/* Opcode: Integer P1 P2 * * *
+** Synopsis: r[P2]=P1
**
** The 32-bit integer value P1 is written into register P2.
*/
@@ -63633,6 +70026,7 @@ case OP_Integer: { /* out2-prerelease */
}
/* Opcode: Int64 * P2 * P4 *
+** Synopsis: r[P2]=P4
**
** P4 is a pointer to a 64-bit integer value.
** Write that value into register P2.
@@ -63645,6 +70039,7 @@ case OP_Int64: { /* out2-prerelease */
#ifndef SQLITE_OMIT_FLOATING_POINT
/* Opcode: Real * P2 * P4 *
+** Synopsis: r[P2]=P4
**
** P4 is a pointer to a 64-bit floating point value.
** Write that value into register P2.
@@ -63652,15 +70047,18 @@ case OP_Int64: { /* out2-prerelease */
case OP_Real: { /* same as TK_FLOAT, out2-prerelease */
pOut->flags = MEM_Real;
assert( !sqlite3IsNaN(*pOp->p4.pReal) );
- pOut->r = *pOp->p4.pReal;
+ pOut->u.r = *pOp->p4.pReal;
break;
}
#endif
/* Opcode: String8 * P2 * P4 *
+** Synopsis: r[P2]='P4'
**
** P4 points to a nul terminated UTF-8 string. This opcode is transformed
-** into an OP_String before it is executed for the first time.
+** into a String before it is executed for the first time. During
+** this transformation, the length of string P4 is computed and stored
+** as the P1 parameter.
*/
case OP_String8: { /* same as TK_STRING, out2-prerelease */
assert( pOp->p4.z!=0 );
@@ -63672,11 +70070,10 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */
rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC);
if( rc==SQLITE_TOOBIG ) goto too_big;
if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
- assert( pOut->zMalloc==pOut->z );
- assert( pOut->flags & MEM_Dyn );
- pOut->zMalloc = 0;
+ assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z );
+ assert( VdbeMemDynamic(pOut)==0 );
+ pOut->szMalloc = 0;
pOut->flags |= MEM_Static;
- pOut->flags &= ~MEM_Dyn;
if( pOp->p4type==P4_DYNAMIC ){
sqlite3DbFree(db, pOp->p4.z);
}
@@ -63692,6 +70089,7 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */
}
/* Opcode: String P1 P2 * P4 *
+** Synopsis: r[P2]='P4' (len=P1)
**
** The string value P4 of length P1 (bytes) is stored in register P2.
*/
@@ -63705,17 +70103,51 @@ case OP_String: { /* out2-prerelease */
break;
}
-/* Opcode: Null * P2 * * *
+/* Opcode: Null P1 P2 P3 * *
+** Synopsis: r[P2..P3]=NULL
**
-** Write a NULL into register P2.
+** Write a NULL into registers P2. If P3 greater than P2, then also write
+** NULL into register P3 and every register in between P2 and P3. If P3
+** is less than P2 (typically P3 is zero) then only register P2 is
+** set to NULL.
+**
+** If the P1 value is non-zero, then also set the MEM_Cleared flag so that
+** NULL values will not compare equal even if SQLITE_NULLEQ is set on
+** OP_Ne or OP_Eq.
*/
case OP_Null: { /* out2-prerelease */
- pOut->flags = MEM_Null;
+ int cnt;
+ u16 nullFlag;
+ cnt = pOp->p3-pOp->p2;
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
+ pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
+ while( cnt>0 ){
+ pOut++;
+ memAboutToChange(p, pOut);
+ sqlite3VdbeMemSetNull(pOut);
+ pOut->flags = nullFlag;
+ cnt--;
+ }
break;
}
+/* Opcode: SoftNull P1 * * * *
+** Synopsis: r[P1]=NULL
+**
+** Set register P1 to have the value NULL as seen by the OP_MakeRecord
+** instruction, but do not free any string or blob memory associated with
+** the register, so that if the value was a string or blob that was
+** previously copied using OP_SCopy, the copies will continue to be valid.
+*/
+case OP_SoftNull: {
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ pOut = &aMem[pOp->p1];
+ pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined;
+ break;
+}
-/* Opcode: Blob P1 P2 * P4
+/* Opcode: Blob P1 P2 * P4 *
+** Synopsis: r[P2]=P4 (len=P1)
**
** P4 points to a blob of data P1 bytes long. Store this
** blob in register P2.
@@ -63729,85 +70161,98 @@ case OP_Blob: { /* out2-prerelease */
}
/* Opcode: Variable P1 P2 * P4 *
+** Synopsis: r[P2]=parameter(P1,P4)
**
** Transfer the values of bound parameter P1 into register P2
**
-** If the parameter is named, then its name appears in P4 and P3==1.
+** If the parameter is named, then its name appears in P4.
** The P4 value is used by sqlite3_bind_parameter_name().
*/
case OP_Variable: { /* out2-prerelease */
-#if 0 /* local variables moved into u.ab */
Mem *pVar; /* Value being transferred */
-#endif /* local variables moved into u.ab */
assert( pOp->p1>0 && pOp->p1<=p->nVar );
assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] );
- u.ab.pVar = &p->aVar[pOp->p1 - 1];
- if( sqlite3VdbeMemTooBig(u.ab.pVar) ){
+ pVar = &p->aVar[pOp->p1 - 1];
+ if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
}
- sqlite3VdbeMemShallowCopy(pOut, u.ab.pVar, MEM_Static);
+ sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
/* Opcode: Move P1 P2 P3 * *
+** Synopsis: r[P2@P3]=r[P1@P3]
**
-** Move the values in register P1..P1+P3-1 over into
-** registers P2..P2+P3-1. Registers P1..P1+P1-1 are
+** Move the P3 values in register P1..P1+P3-1 over into
+** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
** left holding a NULL. It is an error for register ranges
-** P1..P1+P3-1 and P2..P2+P3-1 to overlap.
+** P1..P1+P3-1 and P2..P2+P3-1 to overlap. It is an error
+** for P3 to be less than 1.
*/
case OP_Move: {
-#if 0 /* local variables moved into u.ac */
- char *zMalloc; /* Holding variable for allocated memory */
int n; /* Number of registers left to copy */
int p1; /* Register to copy from */
int p2; /* Register to copy to */
-#endif /* local variables moved into u.ac */
-
- u.ac.n = pOp->p3;
- u.ac.p1 = pOp->p1;
- u.ac.p2 = pOp->p2;
- assert( u.ac.n>0 && u.ac.p1>0 && u.ac.p2>0 );
- assert( u.ac.p1+u.ac.n<=u.ac.p2 || u.ac.p2+u.ac.n<=u.ac.p1 );
-
- pIn1 = &aMem[u.ac.p1];
- pOut = &aMem[u.ac.p2];
- while( u.ac.n-- ){
- assert( pOut<=&aMem[p->nMem] );
- assert( pIn1<=&aMem[p->nMem] );
+
+ n = pOp->p3;
+ p1 = pOp->p1;
+ p2 = pOp->p2;
+ assert( n>0 && p1>0 && p2>0 );
+ assert( p1+n<=p2 || p2+n<=p1 );
+
+ pIn1 = &aMem[p1];
+ pOut = &aMem[p2];
+ do{
+ assert( pOut<=&aMem[(p->nMem-p->nCursor)] );
+ assert( pIn1<=&aMem[(p->nMem-p->nCursor)] );
assert( memIsValid(pIn1) );
memAboutToChange(p, pOut);
- u.ac.zMalloc = pOut->zMalloc;
- pOut->zMalloc = 0;
sqlite3VdbeMemMove(pOut, pIn1);
- pIn1->zMalloc = u.ac.zMalloc;
- REGISTER_TRACE(u.ac.p2++, pOut);
+#ifdef SQLITE_DEBUG
+ if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){
+ pOut->pScopyFrom += p1 - pOp->p2;
+ }
+#endif
+ REGISTER_TRACE(p2++, pOut);
pIn1++;
pOut++;
- }
+ }while( --n );
break;
}
-/* Opcode: Copy P1 P2 * * *
+/* Opcode: Copy P1 P2 P3 * *
+** Synopsis: r[P2@P3+1]=r[P1@P3+1]
**
-** Make a copy of register P1 into register P2.
+** Make a copy of registers P1..P1+P3 into registers P2..P2+P3.
**
** This instruction makes a deep copy of the value. A duplicate
** is made of any string or blob constant. See also OP_SCopy.
*/
-case OP_Copy: { /* in1, out2 */
+case OP_Copy: {
+ int n;
+
+ n = pOp->p3;
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
assert( pOut!=pIn1 );
- sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
- Deephemeralize(pOut);
- REGISTER_TRACE(pOp->p2, pOut);
+ while( 1 ){
+ sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
+ Deephemeralize(pOut);
+#ifdef SQLITE_DEBUG
+ pOut->pScopyFrom = 0;
+#endif
+ REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut);
+ if( (n--)==0 ) break;
+ pOut++;
+ pIn1++;
+ }
break;
}
/* Opcode: SCopy P1 P2 * * *
+** Synopsis: r[P2]=r[P1]
**
** Make a shallow copy of register P1 into register P2.
**
@@ -63819,7 +70264,7 @@ case OP_Copy: { /* in1, out2 */
** during the lifetime of the copy. Use OP_Copy to make a complete
** copy.
*/
-case OP_SCopy: { /* in1, out2 */
+case OP_SCopy: { /* out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
assert( pOut!=pIn1 );
@@ -63827,26 +70272,36 @@ case OP_SCopy: { /* in1, out2 */
#ifdef SQLITE_DEBUG
if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
#endif
- REGISTER_TRACE(pOp->p2, pOut);
break;
}
/* Opcode: ResultRow P1 P2 * * *
+** Synopsis: output=r[P1@P2]
**
** The registers P1 through P1+P2-1 contain a single row of
** results. This opcode causes the sqlite3_step() call to terminate
** with an SQLITE_ROW return code and it sets up the sqlite3_stmt
-** structure to provide access to the top P1 values as the result
-** row.
+** structure to provide access to the r(P1)..r(P1+P2-1) values as
+** the result row.
*/
case OP_ResultRow: {
-#if 0 /* local variables moved into u.ad */
Mem *pMem;
int i;
-#endif /* local variables moved into u.ad */
assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 );
- assert( pOp->p1+pOp->p2<=p->nMem+1 );
+ assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 );
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Run the progress counter just before returning.
+ */
+ if( db->xProgress!=0
+ && nVmStep>=nProgressLimit
+ && db->xProgress(db->pProgressArg)!=0
+ ){
+ rc = SQLITE_INTERRUPT;
+ goto vdbe_error_halt;
+ }
+#endif
/* If this statement has violated immediate foreign key constraints, do
** not return the number of rows modified. And do not RELEASE the statement
@@ -63857,8 +70312,8 @@ case OP_ResultRow: {
break;
}
- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
- ** DML statements invoke this opcode to return the number of rows
+ /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
+ ** DML statements invoke this opcode to return the number of rows
** modified to the user. This is the only way that a VM that
** opens a statement transaction may invoke this opcode.
**
@@ -63883,17 +70338,16 @@ case OP_ResultRow: {
/* Make sure the results of the current row are \000 terminated
** and have an assigned type. The results are de-ephemeralized as
- ** as side effect.
- */
- u.ad.pMem = p->pResultSet = &aMem[pOp->p1];
- for(u.ad.i=0; u.ad.i<pOp->p2; u.ad.i++){
- assert( memIsValid(&u.ad.pMem[u.ad.i]) );
- Deephemeralize(&u.ad.pMem[u.ad.i]);
- assert( (u.ad.pMem[u.ad.i].flags & MEM_Ephem)==0
- || (u.ad.pMem[u.ad.i].flags & (MEM_Str|MEM_Blob))==0 );
- sqlite3VdbeMemNulTerminate(&u.ad.pMem[u.ad.i]);
- sqlite3VdbeMemStoreType(&u.ad.pMem[u.ad.i]);
- REGISTER_TRACE(pOp->p1+u.ad.i, &u.ad.pMem[u.ad.i]);
+ ** a side effect.
+ */
+ pMem = p->pResultSet = &aMem[pOp->p1];
+ for(i=0; i<pOp->p2; i++){
+ assert( memIsValid(&pMem[i]) );
+ Deephemeralize(&pMem[i]);
+ assert( (pMem[i].flags & MEM_Ephem)==0
+ || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
+ sqlite3VdbeMemNulTerminate(&pMem[i]);
+ REGISTER_TRACE(pOp->p1+i, &pMem[i]);
}
if( db->mallocFailed ) goto no_mem;
@@ -63905,6 +70359,7 @@ case OP_ResultRow: {
}
/* Opcode: Concat P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]+r[P1]
**
** Add the text in register P1 onto the end of the text in
** register P2 and store the result in register P3.
@@ -63917,9 +70372,7 @@ case OP_ResultRow: {
** to avoid a memcpy().
*/
case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
-#if 0 /* local variables moved into u.ae */
i64 nByte;
-#endif /* local variables moved into u.ae */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
@@ -63932,34 +70385,36 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem;
Stringify(pIn1, encoding);
Stringify(pIn2, encoding);
- u.ae.nByte = pIn1->n + pIn2->n;
- if( u.ae.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ nByte = pIn1->n + pIn2->n;
+ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- MemSetTypeFlag(pOut, MEM_Str);
- if( sqlite3VdbeMemGrow(pOut, (int)u.ae.nByte+2, pOut==pIn2) ){
+ if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
goto no_mem;
}
+ MemSetTypeFlag(pOut, MEM_Str);
if( pOut!=pIn2 ){
memcpy(pOut->z, pIn2->z, pIn2->n);
}
memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n);
- pOut->z[u.ae.nByte] = 0;
- pOut->z[u.ae.nByte+1] = 0;
+ pOut->z[nByte]=0;
+ pOut->z[nByte+1] = 0;
pOut->flags |= MEM_Term;
- pOut->n = (int)u.ae.nByte;
+ pOut->n = (int)nByte;
pOut->enc = encoding;
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
/* Opcode: Add P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]+r[P2]
**
** Add the value in register P1 to the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Multiply P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]*r[P2]
**
**
** Multiply the value in register P1 by the value in register P2
@@ -63967,12 +70422,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** If either input is NULL, the result is NULL.
*/
/* Opcode: Subtract P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]-r[P1]
**
** Subtract the value in register P1 from the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Divide P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]/r[P1]
**
** Divide the value in register P1 by the value in register P2
** and store the result in register P3 (P3=P2/P1). If the value in
@@ -63980,10 +70437,11 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** NULL, the result is NULL.
*/
/* Opcode: Remainder P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]%r[P1]
**
-** Compute the remainder after integer division of the value in
-** register P1 by the value in register P2 and store the result in P3.
-** If the value in register P2 is zero the result is NULL.
+** Compute the remainder after integer register P2 is divided by
+** register P1 and store the result in register P3.
+** If the value in register P1 is zero the result is NULL.
** If either operand is NULL, the result is NULL.
*/
case OP_Add: /* same as TK_PLUS, in1, in2, out3 */
@@ -63991,76 +70449,79 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */
case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */
case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
-#if 0 /* local variables moved into u.af */
- int flags; /* Combined MEM_* flags from both inputs */
+ char bIntint; /* Started out as two integer operands */
+ u16 flags; /* Combined MEM_* flags from both inputs */
+ u16 type1; /* Numeric type of left operand */
+ u16 type2; /* Numeric type of right operand */
i64 iA; /* Integer value of left operand */
i64 iB; /* Integer value of right operand */
double rA; /* Real value of left operand */
double rB; /* Real value of right operand */
-#endif /* local variables moved into u.af */
pIn1 = &aMem[pOp->p1];
- applyNumericAffinity(pIn1);
+ type1 = numericType(pIn1);
pIn2 = &aMem[pOp->p2];
- applyNumericAffinity(pIn2);
+ type2 = numericType(pIn2);
pOut = &aMem[pOp->p3];
- u.af.flags = pIn1->flags | pIn2->flags;
- if( (u.af.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
- if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){
- u.af.iA = pIn1->u.i;
- u.af.iB = pIn2->u.i;
+ flags = pIn1->flags | pIn2->flags;
+ if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
+ if( (type1 & type2 & MEM_Int)!=0 ){
+ iA = pIn1->u.i;
+ iB = pIn2->u.i;
+ bIntint = 1;
switch( pOp->opcode ){
- case OP_Add: if( sqlite3AddInt64(&u.af.iB,u.af.iA) ) goto fp_math; break;
- case OP_Subtract: if( sqlite3SubInt64(&u.af.iB,u.af.iA) ) goto fp_math; break;
- case OP_Multiply: if( sqlite3MulInt64(&u.af.iB,u.af.iA) ) goto fp_math; break;
+ case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break;
+ case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break;
+ case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break;
case OP_Divide: {
- if( u.af.iA==0 ) goto arithmetic_result_is_null;
- if( u.af.iA==-1 && u.af.iB==SMALLEST_INT64 ) goto fp_math;
- u.af.iB /= u.af.iA;
+ if( iA==0 ) goto arithmetic_result_is_null;
+ if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math;
+ iB /= iA;
break;
}
default: {
- if( u.af.iA==0 ) goto arithmetic_result_is_null;
- if( u.af.iA==-1 ) u.af.iA = 1;
- u.af.iB %= u.af.iA;
+ if( iA==0 ) goto arithmetic_result_is_null;
+ if( iA==-1 ) iA = 1;
+ iB %= iA;
break;
}
}
- pOut->u.i = u.af.iB;
+ pOut->u.i = iB;
MemSetTypeFlag(pOut, MEM_Int);
}else{
+ bIntint = 0;
fp_math:
- u.af.rA = sqlite3VdbeRealValue(pIn1);
- u.af.rB = sqlite3VdbeRealValue(pIn2);
+ rA = sqlite3VdbeRealValue(pIn1);
+ rB = sqlite3VdbeRealValue(pIn2);
switch( pOp->opcode ){
- case OP_Add: u.af.rB += u.af.rA; break;
- case OP_Subtract: u.af.rB -= u.af.rA; break;
- case OP_Multiply: u.af.rB *= u.af.rA; break;
+ case OP_Add: rB += rA; break;
+ case OP_Subtract: rB -= rA; break;
+ case OP_Multiply: rB *= rA; break;
case OP_Divide: {
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
- if( u.af.rA==(double)0 ) goto arithmetic_result_is_null;
- u.af.rB /= u.af.rA;
+ if( rA==(double)0 ) goto arithmetic_result_is_null;
+ rB /= rA;
break;
}
default: {
- u.af.iA = (i64)u.af.rA;
- u.af.iB = (i64)u.af.rB;
- if( u.af.iA==0 ) goto arithmetic_result_is_null;
- if( u.af.iA==-1 ) u.af.iA = 1;
- u.af.rB = (double)(u.af.iB % u.af.iA);
+ iA = (i64)rA;
+ iB = (i64)rB;
+ if( iA==0 ) goto arithmetic_result_is_null;
+ if( iA==-1 ) iA = 1;
+ rB = (double)(iB % iA);
break;
}
}
#ifdef SQLITE_OMIT_FLOATING_POINT
- pOut->u.i = u.af.rB;
+ pOut->u.i = rB;
MemSetTypeFlag(pOut, MEM_Int);
#else
- if( sqlite3IsNaN(u.af.rB) ){
+ if( sqlite3IsNaN(rB) ){
goto arithmetic_result_is_null;
}
- pOut->r = u.af.rB;
+ pOut->u.r = rB;
MemSetTypeFlag(pOut, MEM_Real);
- if( (u.af.flags & MEM_Real)==0 ){
+ if( ((type1|type2)&MEM_Real)==0 && !bIntint ){
sqlite3VdbeIntegerAffinity(pOut);
}
#endif
@@ -64072,23 +70533,31 @@ arithmetic_result_is_null:
break;
}
-/* Opcode: CollSeq * * P4
+/* Opcode: CollSeq P1 * * P4
**
** P4 is a pointer to a CollSeq struct. If the next call to a user function
** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will
** be returned. This is used by the built-in min(), max() and nullif()
** functions.
**
+** If P1 is not zero, then it is a register that a subsequent min() or
+** max() aggregate will set to 1 if the current row is not the minimum or
+** maximum. The P1 register is initialized to 0 by this instruction.
+**
** The interface used by the implementation of the aforementioned functions
** to retrieve the collation sequence set by this opcode is not available
** publicly, only to user functions defined in func.c.
*/
case OP_CollSeq: {
assert( pOp->p4type==P4_COLLSEQ );
+ if( pOp->p1 ){
+ sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0);
+ }
break;
}
/* Opcode: Function P1 P2 P3 P4 P5
+** Synopsis: r[P3]=func(r[P2@P5])
**
** Invoke a user function (P4 is a pointer to a Function structure that
** defines the function) with P5 arguments taken from register P2 and
@@ -64105,122 +70574,75 @@ case OP_CollSeq: {
** See also: AggStep and AggFinal
*/
case OP_Function: {
-#if 0 /* local variables moved into u.ag */
int i;
Mem *pArg;
sqlite3_context ctx;
sqlite3_value **apVal;
int n;
-#endif /* local variables moved into u.ag */
-
- u.ag.n = pOp->p5;
- u.ag.apVal = p->apArg;
- assert( u.ag.apVal || u.ag.n==0 );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- pOut = &aMem[pOp->p3];
- memAboutToChange(p, pOut);
-
- assert( u.ag.n==0 || (pOp->p2>0 && pOp->p2+u.ag.n<=p->nMem+1) );
- assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+u.ag.n );
- u.ag.pArg = &aMem[pOp->p2];
- for(u.ag.i=0; u.ag.i<u.ag.n; u.ag.i++, u.ag.pArg++){
- assert( memIsValid(u.ag.pArg) );
- u.ag.apVal[u.ag.i] = u.ag.pArg;
- Deephemeralize(u.ag.pArg);
- sqlite3VdbeMemStoreType(u.ag.pArg);
- REGISTER_TRACE(pOp->p2+u.ag.i, u.ag.pArg);
- }
- assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC );
- if( pOp->p4type==P4_FUNCDEF ){
- u.ag.ctx.pFunc = pOp->p4.pFunc;
- u.ag.ctx.pVdbeFunc = 0;
- }else{
- u.ag.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc;
- u.ag.ctx.pFunc = u.ag.ctx.pVdbeFunc->pFunc;
- }
-
- u.ag.ctx.s.flags = MEM_Null;
- u.ag.ctx.s.db = db;
- u.ag.ctx.s.xDel = 0;
- u.ag.ctx.s.zMalloc = 0;
-
- /* The output cell may already have a buffer allocated. Move
- ** the pointer to u.ag.ctx.s so in case the user-function can use
- ** the already allocated buffer instead of allocating a new one.
- */
- sqlite3VdbeMemMove(&u.ag.ctx.s, pOut);
- MemSetTypeFlag(&u.ag.ctx.s, MEM_Null);
-
- u.ag.ctx.isError = 0;
- if( u.ag.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
- assert( pOp>aOp );
- assert( pOp[-1].p4type==P4_COLLSEQ );
- assert( pOp[-1].opcode==OP_CollSeq );
- u.ag.ctx.pColl = pOp[-1].p4.pColl;
- }
+ n = pOp->p5;
+ apVal = p->apArg;
+ assert( apVal || n==0 );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+ ctx.pOut = &aMem[pOp->p3];
+ memAboutToChange(p, ctx.pOut);
+
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) );
+ assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
+ pArg = &aMem[pOp->p2];
+ for(i=0; i<n; i++, pArg++){
+ assert( memIsValid(pArg) );
+ apVal[i] = pArg;
+ Deephemeralize(pArg);
+ REGISTER_TRACE(pOp->p2+i, pArg);
+ }
+
+ assert( pOp->p4type==P4_FUNCDEF );
+ ctx.pFunc = pOp->p4.pFunc;
+ ctx.iOp = pc;
+ ctx.pVdbe = p;
+ MemSetTypeFlag(ctx.pOut, MEM_Null);
+ ctx.fErrorOrAux = 0;
db->lastRowid = lastRowid;
- (*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */
- lastRowid = db->lastRowid;
-
- /* If any auxiliary data functions have been called by this user function,
- ** immediately call the destructor for any non-static values.
- */
- if( u.ag.ctx.pVdbeFunc ){
- sqlite3VdbeDeleteAuxData(u.ag.ctx.pVdbeFunc, pOp->p1);
- pOp->p4.pVdbeFunc = u.ag.ctx.pVdbeFunc;
- pOp->p4type = P4_VDBEFUNC;
- }
-
- if( db->mallocFailed ){
- /* Even though a malloc() has failed, the implementation of the
- ** user function may have called an sqlite3_result_XXX() function
- ** to return a value. The following call releases any resources
- ** associated with such a value.
- */
- sqlite3VdbeMemRelease(&u.ag.ctx.s);
- goto no_mem;
- }
+ (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
+ lastRowid = db->lastRowid; /* Remember rowid changes made by xFunc */
/* If the function returned an error, throw an exception */
- if( u.ag.ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ag.ctx.s));
- rc = u.ag.ctx.isError;
+ if( ctx.fErrorOrAux ){
+ if( ctx.isError ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(ctx.pOut));
+ rc = ctx.isError;
+ }
+ sqlite3VdbeDeleteAuxData(p, pc, pOp->p1);
}
/* Copy the result of the function into register P3 */
- sqlite3VdbeChangeEncoding(&u.ag.ctx.s, encoding);
- sqlite3VdbeMemMove(pOut, &u.ag.ctx.s);
- if( sqlite3VdbeMemTooBig(pOut) ){
+ sqlite3VdbeChangeEncoding(ctx.pOut, encoding);
+ if( sqlite3VdbeMemTooBig(ctx.pOut) ){
goto too_big;
}
-#if 0
- /* The app-defined function has done something that as caused this
- ** statement to expire. (Perhaps the function called sqlite3_exec()
- ** with a CREATE TABLE statement.)
- */
- if( p->expired ) rc = SQLITE_ABORT;
-#endif
-
- REGISTER_TRACE(pOp->p3, pOut);
- UPDATE_MAX_BLOBSIZE(pOut);
+ REGISTER_TRACE(pOp->p3, ctx.pOut);
+ UPDATE_MAX_BLOBSIZE(ctx.pOut);
break;
}
/* Opcode: BitAnd P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]&r[P2]
**
** Take the bit-wise AND of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: BitOr P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]|r[P2]
**
** Take the bit-wise OR of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftLeft P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]<<r[P1]
**
** Shift the integer value in register P2 to the left by the
** number of bits specified by the integer in register P1.
@@ -64228,6 +70650,7 @@ case OP_Function: {
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftRight P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]>>r[P1]
**
** Shift the integer value in register P2 to the right by the
** number of bits specified by the integer in register P1.
@@ -64238,12 +70661,10 @@ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */
case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */
case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */
case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
-#if 0 /* local variables moved into u.ah */
i64 iA;
u64 uA;
i64 iB;
u8 op;
-#endif /* local variables moved into u.ah */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
@@ -64252,43 +70673,44 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
sqlite3VdbeMemSetNull(pOut);
break;
}
- u.ah.iA = sqlite3VdbeIntValue(pIn2);
- u.ah.iB = sqlite3VdbeIntValue(pIn1);
- u.ah.op = pOp->opcode;
- if( u.ah.op==OP_BitAnd ){
- u.ah.iA &= u.ah.iB;
- }else if( u.ah.op==OP_BitOr ){
- u.ah.iA |= u.ah.iB;
- }else if( u.ah.iB!=0 ){
- assert( u.ah.op==OP_ShiftRight || u.ah.op==OP_ShiftLeft );
+ iA = sqlite3VdbeIntValue(pIn2);
+ iB = sqlite3VdbeIntValue(pIn1);
+ op = pOp->opcode;
+ if( op==OP_BitAnd ){
+ iA &= iB;
+ }else if( op==OP_BitOr ){
+ iA |= iB;
+ }else if( iB!=0 ){
+ assert( op==OP_ShiftRight || op==OP_ShiftLeft );
/* If shifting by a negative amount, shift in the other direction */
- if( u.ah.iB<0 ){
+ if( iB<0 ){
assert( OP_ShiftRight==OP_ShiftLeft+1 );
- u.ah.op = 2*OP_ShiftLeft + 1 - u.ah.op;
- u.ah.iB = u.ah.iB>(-64) ? -u.ah.iB : 64;
+ op = 2*OP_ShiftLeft + 1 - op;
+ iB = iB>(-64) ? -iB : 64;
}
- if( u.ah.iB>=64 ){
- u.ah.iA = (u.ah.iA>=0 || u.ah.op==OP_ShiftLeft) ? 0 : -1;
+ if( iB>=64 ){
+ iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1;
}else{
- memcpy(&u.ah.uA, &u.ah.iA, sizeof(u.ah.uA));
- if( u.ah.op==OP_ShiftLeft ){
- u.ah.uA <<= u.ah.iB;
+ memcpy(&uA, &iA, sizeof(uA));
+ if( op==OP_ShiftLeft ){
+ uA <<= iB;
}else{
- u.ah.uA >>= u.ah.iB;
+ uA >>= iB;
/* Sign-extend on a right shift of a negative number */
- if( u.ah.iA<0 ) u.ah.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.ah.iB);
+ if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB);
}
- memcpy(&u.ah.iA, &u.ah.uA, sizeof(u.ah.iA));
+ memcpy(&iA, &uA, sizeof(iA));
}
}
- pOut->u.i = u.ah.iA;
+ pOut->u.i = iA;
MemSetTypeFlag(pOut, MEM_Int);
break;
}
/* Opcode: AddImm P1 P2 * * *
+** Synopsis: r[P1]=r[P1]+P2
**
** Add the constant P2 to the value in register P1.
** The result is always an integer.
@@ -64312,17 +70734,20 @@ case OP_AddImm: { /* in1 */
*/
case OP_MustBeInt: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
- applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
if( (pIn1->flags & MEM_Int)==0 ){
- if( pOp->p2==0 ){
- rc = SQLITE_MISMATCH;
- goto abort_due_to_error;
- }else{
- pc = pOp->p2 - 1;
+ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
+ VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
+ if( (pIn1->flags & MEM_Int)==0 ){
+ if( pOp->p2==0 ){
+ rc = SQLITE_MISMATCH;
+ goto abort_due_to_error;
+ }else{
+ pc = pOp->p2 - 1;
+ break;
+ }
}
- }else{
- MemSetTypeFlag(pIn1, MEM_Int);
}
+ MemSetTypeFlag(pIn1, MEM_Int);
break;
}
@@ -64346,107 +70771,39 @@ case OP_RealAffinity: { /* in1 */
#endif
#ifndef SQLITE_OMIT_CAST
-/* Opcode: ToText P1 * * * *
+/* Opcode: Cast P1 P2 * * *
+** Synopsis: affinity(r[P1])
**
-** Force the value in register P1 to be text.
-** If the value is numeric, convert it to a string using the
-** equivalent of printf(). Blob values are unchanged and
-** are afterwards simply interpreted as text.
+** Force the value in register P1 to be the type defined by P2.
+**
+** <ul>
+** <li value="97"> TEXT
+** <li value="98"> BLOB
+** <li value="99"> NUMERIC
+** <li value="100"> INTEGER
+** <li value="101"> REAL
+** </ul>
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToText: { /* same as TK_TO_TEXT, in1 */
+case OP_Cast: { /* in1 */
+ assert( pOp->p2>=SQLITE_AFF_NONE && pOp->p2<=SQLITE_AFF_REAL );
+ testcase( pOp->p2==SQLITE_AFF_TEXT );
+ testcase( pOp->p2==SQLITE_AFF_NONE );
+ testcase( pOp->p2==SQLITE_AFF_NUMERIC );
+ testcase( pOp->p2==SQLITE_AFF_INTEGER );
+ testcase( pOp->p2==SQLITE_AFF_REAL );
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
- if( pIn1->flags & MEM_Null ) break;
- assert( MEM_Str==(MEM_Blob>>3) );
- pIn1->flags |= (pIn1->flags&MEM_Blob)>>3;
- applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
rc = ExpandBlob(pIn1);
- assert( pIn1->flags & MEM_Str || db->mallocFailed );
- pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero);
- UPDATE_MAX_BLOBSIZE(pIn1);
- break;
-}
-
-/* Opcode: ToBlob P1 * * * *
-**
-** Force the value in register P1 to be a BLOB.
-** If the value is numeric, convert it to a string first.
-** Strings are simply reinterpreted as blobs with no change
-** to the underlying data.
-**
-** A NULL value is not changed by this routine. It remains NULL.
-*/
-case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
- pIn1 = &aMem[pOp->p1];
- if( pIn1->flags & MEM_Null ) break;
- if( (pIn1->flags & MEM_Blob)==0 ){
- applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
- assert( pIn1->flags & MEM_Str || db->mallocFailed );
- MemSetTypeFlag(pIn1, MEM_Blob);
- }else{
- pIn1->flags &= ~(MEM_TypeMask&~MEM_Blob);
- }
+ sqlite3VdbeMemCast(pIn1, pOp->p2, encoding);
UPDATE_MAX_BLOBSIZE(pIn1);
break;
}
-
-/* Opcode: ToNumeric P1 * * * *
-**
-** Force the value in register P1 to be numeric (either an
-** integer or a floating-point number.)
-** If the value is text or blob, try to convert it to an using the
-** equivalent of atoi() or atof() and store 0 if no such conversion
-** is possible.
-**
-** A NULL value is not changed by this routine. It remains NULL.
-*/
-case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
- pIn1 = &aMem[pOp->p1];
- sqlite3VdbeMemNumerify(pIn1);
- break;
-}
#endif /* SQLITE_OMIT_CAST */
-/* Opcode: ToInt P1 * * * *
-**
-** Force the value in register P1 to be an integer. If
-** The value is currently a real number, drop its fractional part.
-** If the value is text or blob, try to convert it to an integer using the
-** equivalent of atoi() and store 0 if no such conversion is possible.
-**
-** A NULL value is not changed by this routine. It remains NULL.
-*/
-case OP_ToInt: { /* same as TK_TO_INT, in1 */
- pIn1 = &aMem[pOp->p1];
- if( (pIn1->flags & MEM_Null)==0 ){
- sqlite3VdbeMemIntegerify(pIn1);
- }
- break;
-}
-
-#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT)
-/* Opcode: ToReal P1 * * * *
-**
-** Force the value in register P1 to be a floating point number.
-** If The value is currently an integer, convert it.
-** If the value is text or blob, try to convert it to an integer using the
-** equivalent of atoi() and store 0.0 if no such conversion is possible.
-**
-** A NULL value is not changed by this routine. It remains NULL.
-*/
-case OP_ToReal: { /* same as TK_TO_REAL, in1 */
- pIn1 = &aMem[pOp->p1];
- memAboutToChange(p, pIn1);
- if( (pIn1->flags & MEM_Null)==0 ){
- sqlite3VdbeMemRealify(pIn1);
- }
- break;
-}
-#endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */
-
/* Opcode: Lt P1 P2 P3 P4 P5
+** Synopsis: if r[P1]<r[P3] goto P2
**
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
** jump to address P2.
@@ -64475,8 +70832,13 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
**
** If the SQLITE_STOREP2 bit of P5 is set, then do not jump. Instead,
** store a boolean result (either 0, or 1, or NULL) in register P2.
+**
+** If the SQLITE_NULLEQ bit is set in P5, then NULL values are considered
+** equal to one another, provided that they do not have their MEM_Cleared
+** bit set.
*/
/* Opcode: Ne P1 P2 P3 P4 P5
+** Synopsis: if r[P1]!=r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the operands in registers P1 and P3 are not equal. See the Lt opcode for
@@ -64489,6 +70851,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Eq P1 P2 P3 P4 P5
+** Synopsis: if r[P1]==r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the operands in registers P1 and P3 are equal.
@@ -64501,18 +70864,21 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Le P1 P2 P3 P4 P5
+** Synopsis: if r[P1]<=r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is less than or equal to the content of
** register P1. See the Lt opcode for additional information.
*/
/* Opcode: Gt P1 P2 P3 P4 P5
+** Synopsis: if r[P1]>r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is greater than the content of
** register P1. See the Lt opcode for additional information.
*/
/* Opcode: Ge P1 P2 P3 P4 P5
+** Synopsis: if r[P1]>=r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is greater than or equal to the content of
@@ -64524,18 +70890,16 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */
case OP_Le: /* same as TK_LE, jump, in1, in3 */
case OP_Gt: /* same as TK_GT, jump, in1, in3 */
case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
-#if 0 /* local variables moved into u.ai */
int res; /* Result of the comparison of pIn1 against pIn3 */
char affinity; /* Affinity to use for comparison */
u16 flags1; /* Copy of initial value of pIn1->flags */
u16 flags3; /* Copy of initial value of pIn3->flags */
-#endif /* local variables moved into u.ai */
pIn1 = &aMem[pOp->p1];
pIn3 = &aMem[pOp->p3];
- u.ai.flags1 = pIn1->flags;
- u.ai.flags3 = pIn3->flags;
- if( (u.ai.flags1 | u.ai.flags3)&MEM_Null ){
+ flags1 = pIn1->flags;
+ flags3 = pIn3->flags;
+ if( (flags1 | flags3)&MEM_Null ){
/* One or both operands are NULL */
if( pOp->p5 & SQLITE_NULLEQ ){
/* If SQLITE_NULLEQ is set (which will only happen if the operator is
@@ -64543,7 +70907,16 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** or not both operands are null.
*/
assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
- u.ai.res = (u.ai.flags1 & u.ai.flags3 & MEM_Null)==0;
+ assert( (flags1 & MEM_Cleared)==0 );
+ assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 );
+ if( (flags1&MEM_Null)!=0
+ && (flags3&MEM_Null)!=0
+ && (flags3&MEM_Cleared)==0
+ ){
+ res = 0; /* Results are equal */
+ }else{
+ res = 1; /* Results are not equal */
+ }
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
** then the result is always NULL.
@@ -64553,47 +70926,72 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
pOut = &aMem[pOp->p2];
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
- }else if( pOp->p5 & SQLITE_JUMPIFNULL ){
- pc = pOp->p2-1;
+ }else{
+ VdbeBranchTaken(2,3);
+ if( pOp->p5 & SQLITE_JUMPIFNULL ){
+ pc = pOp->p2-1;
+ }
}
break;
}
}else{
/* Neither operand is NULL. Do a comparison. */
- u.ai.affinity = pOp->p5 & SQLITE_AFF_MASK;
- if( u.ai.affinity ){
- applyAffinity(pIn1, u.ai.affinity, encoding);
- applyAffinity(pIn3, u.ai.affinity, encoding);
- if( db->mallocFailed ) goto no_mem;
+ affinity = pOp->p5 & SQLITE_AFF_MASK;
+ if( affinity>=SQLITE_AFF_NUMERIC ){
+ if( (pIn1->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn1,0);
+ }
+ if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn3,0);
+ }
+ }else if( affinity==SQLITE_AFF_TEXT ){
+ if( (pIn1->flags & MEM_Str)==0 && (pIn1->flags & (MEM_Int|MEM_Real))!=0 ){
+ testcase( pIn1->flags & MEM_Int );
+ testcase( pIn1->flags & MEM_Real );
+ sqlite3VdbeMemStringify(pIn1, encoding, 1);
+ }
+ if( (pIn3->flags & MEM_Str)==0 && (pIn3->flags & (MEM_Int|MEM_Real))!=0 ){
+ testcase( pIn3->flags & MEM_Int );
+ testcase( pIn3->flags & MEM_Real );
+ sqlite3VdbeMemStringify(pIn3, encoding, 1);
+ }
}
-
assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
- ExpandBlob(pIn1);
- ExpandBlob(pIn3);
- u.ai.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
+ if( pIn1->flags & MEM_Zero ){
+ sqlite3VdbeMemExpandBlob(pIn1);
+ flags1 &= ~MEM_Zero;
+ }
+ if( pIn3->flags & MEM_Zero ){
+ sqlite3VdbeMemExpandBlob(pIn3);
+ flags3 &= ~MEM_Zero;
+ }
+ if( db->mallocFailed ) goto no_mem;
+ res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
switch( pOp->opcode ){
- case OP_Eq: u.ai.res = u.ai.res==0; break;
- case OP_Ne: u.ai.res = u.ai.res!=0; break;
- case OP_Lt: u.ai.res = u.ai.res<0; break;
- case OP_Le: u.ai.res = u.ai.res<=0; break;
- case OP_Gt: u.ai.res = u.ai.res>0; break;
- default: u.ai.res = u.ai.res>=0; break;
+ case OP_Eq: res = res==0; break;
+ case OP_Ne: res = res!=0; break;
+ case OP_Lt: res = res<0; break;
+ case OP_Le: res = res<=0; break;
+ case OP_Gt: res = res>0; break;
+ default: res = res>=0; break;
}
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Int);
- pOut->u.i = u.ai.res;
+ pOut->u.i = res;
REGISTER_TRACE(pOp->p2, pOut);
- }else if( u.ai.res ){
- pc = pOp->p2-1;
+ }else{
+ VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ if( res ){
+ pc = pOp->p2-1;
+ }
}
-
/* Undo any changes made by applyAffinity() to the input registers. */
- pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (u.ai.flags1&MEM_TypeMask);
- pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (u.ai.flags3&MEM_TypeMask);
+ pIn1->flags = flags1;
+ pIn3->flags = flags3;
break;
}
@@ -64602,9 +71000,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** Set the permutation used by the OP_Compare operator to be the array
** of integers in P4.
**
-** The permutation is only valid until the next OP_Permutation, OP_Compare,
-** OP_Halt, or OP_ResultRow. Typically the OP_Permutation should occur
-** immediately prior to the OP_Compare.
+** The permutation is only valid until the next OP_Compare that has
+** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
+** occur immediately prior to the OP_Compare.
*/
case OP_Permutation: {
assert( pOp->p4type==P4_INTARRAY );
@@ -64613,12 +71011,18 @@ case OP_Permutation: {
break;
}
-/* Opcode: Compare P1 P2 P3 P4 *
+/* Opcode: Compare P1 P2 P3 P4 P5
+** Synopsis: r[P1@P3] <-> r[P2@P3]
**
** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this
** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
** the comparison for use by the next OP_Jump instruct.
**
+** If P5 has the OPFLAG_PERMUTE bit set, then the order of comparison is
+** determined by the most recent OP_Permutation operator. If the
+** OPFLAG_PERMUTE bit is clear, then register are compared in sequential
+** order.
+**
** P4 is a KeyInfo structure that defines collating sequences and sort
** orders for the comparison. The permutation applies to registers
** only. The KeyInfo elements are used sequentially.
@@ -64628,7 +71032,6 @@ case OP_Permutation: {
** and strings are less than blobs.
*/
case OP_Compare: {
-#if 0 /* local variables moved into u.aj */
int n;
int i;
int p1;
@@ -64637,37 +71040,37 @@ case OP_Compare: {
int idx;
CollSeq *pColl; /* Collating sequence to use on this term */
int bRev; /* True for DESCENDING sort order */
-#endif /* local variables moved into u.aj */
-
- u.aj.n = pOp->p3;
- u.aj.pKeyInfo = pOp->p4.pKeyInfo;
- assert( u.aj.n>0 );
- assert( u.aj.pKeyInfo!=0 );
- u.aj.p1 = pOp->p1;
- u.aj.p2 = pOp->p2;
+
+ if( (pOp->p5 & OPFLAG_PERMUTE)==0 ) aPermute = 0;
+ n = pOp->p3;
+ pKeyInfo = pOp->p4.pKeyInfo;
+ assert( n>0 );
+ assert( pKeyInfo!=0 );
+ p1 = pOp->p1;
+ p2 = pOp->p2;
#if SQLITE_DEBUG
if( aPermute ){
int k, mx = 0;
- for(k=0; k<u.aj.n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
- assert( u.aj.p1>0 && u.aj.p1+mx<=p->nMem+1 );
- assert( u.aj.p2>0 && u.aj.p2+mx<=p->nMem+1 );
+ for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
+ assert( p1>0 && p1+mx<=(p->nMem-p->nCursor)+1 );
+ assert( p2>0 && p2+mx<=(p->nMem-p->nCursor)+1 );
}else{
- assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 );
- assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 );
+ assert( p1>0 && p1+n<=(p->nMem-p->nCursor)+1 );
+ assert( p2>0 && p2+n<=(p->nMem-p->nCursor)+1 );
}
#endif /* SQLITE_DEBUG */
- for(u.aj.i=0; u.aj.i<u.aj.n; u.aj.i++){
- u.aj.idx = aPermute ? aPermute[u.aj.i] : u.aj.i;
- assert( memIsValid(&aMem[u.aj.p1+u.aj.idx]) );
- assert( memIsValid(&aMem[u.aj.p2+u.aj.idx]) );
- REGISTER_TRACE(u.aj.p1+u.aj.idx, &aMem[u.aj.p1+u.aj.idx]);
- REGISTER_TRACE(u.aj.p2+u.aj.idx, &aMem[u.aj.p2+u.aj.idx]);
- assert( u.aj.i<u.aj.pKeyInfo->nField );
- u.aj.pColl = u.aj.pKeyInfo->aColl[u.aj.i];
- u.aj.bRev = u.aj.pKeyInfo->aSortOrder[u.aj.i];
- iCompare = sqlite3MemCompare(&aMem[u.aj.p1+u.aj.idx], &aMem[u.aj.p2+u.aj.idx], u.aj.pColl);
+ for(i=0; i<n; i++){
+ idx = aPermute ? aPermute[i] : i;
+ assert( memIsValid(&aMem[p1+idx]) );
+ assert( memIsValid(&aMem[p2+idx]) );
+ REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
+ REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
+ assert( i<pKeyInfo->nField );
+ pColl = pKeyInfo->aColl[i];
+ bRev = pKeyInfo->aSortOrder[i];
+ iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
if( iCompare ){
- if( u.aj.bRev ) iCompare = -iCompare;
+ if( bRev ) iCompare = -iCompare;
break;
}
}
@@ -64683,16 +71086,17 @@ case OP_Compare: {
*/
case OP_Jump: { /* jump */
if( iCompare<0 ){
- pc = pOp->p1 - 1;
+ pc = pOp->p1 - 1; VdbeBranchTaken(0,3);
}else if( iCompare==0 ){
- pc = pOp->p2 - 1;
+ pc = pOp->p2 - 1; VdbeBranchTaken(1,3);
}else{
- pc = pOp->p3 - 1;
+ pc = pOp->p3 - 1; VdbeBranchTaken(2,3);
}
break;
}
/* Opcode: And P1 P2 P3 * *
+** Synopsis: r[P3]=(r[P1] && r[P2])
**
** Take the logical AND of the values in registers P1 and P2 and
** write the result into register P3.
@@ -64702,6 +71106,7 @@ case OP_Jump: { /* jump */
** a NULL output.
*/
/* Opcode: Or P1 P2 P3 * *
+** Synopsis: r[P3]=(r[P1] || r[P2])
**
** Take the logical OR of the values in register P1 and P2 and
** store the answer in register P3.
@@ -64712,41 +71117,40 @@ case OP_Jump: { /* jump */
*/
case OP_And: /* same as TK_AND, in1, in2, out3 */
case OP_Or: { /* same as TK_OR, in1, in2, out3 */
-#if 0 /* local variables moved into u.ak */
int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
-#endif /* local variables moved into u.ak */
pIn1 = &aMem[pOp->p1];
if( pIn1->flags & MEM_Null ){
- u.ak.v1 = 2;
+ v1 = 2;
}else{
- u.ak.v1 = sqlite3VdbeIntValue(pIn1)!=0;
+ v1 = sqlite3VdbeIntValue(pIn1)!=0;
}
pIn2 = &aMem[pOp->p2];
if( pIn2->flags & MEM_Null ){
- u.ak.v2 = 2;
+ v2 = 2;
}else{
- u.ak.v2 = sqlite3VdbeIntValue(pIn2)!=0;
+ v2 = sqlite3VdbeIntValue(pIn2)!=0;
}
if( pOp->opcode==OP_And ){
static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
- u.ak.v1 = and_logic[u.ak.v1*3+u.ak.v2];
+ v1 = and_logic[v1*3+v2];
}else{
static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
- u.ak.v1 = or_logic[u.ak.v1*3+u.ak.v2];
+ v1 = or_logic[v1*3+v2];
}
pOut = &aMem[pOp->p3];
- if( u.ak.v1==2 ){
+ if( v1==2 ){
MemSetTypeFlag(pOut, MEM_Null);
}else{
- pOut->u.i = u.ak.v1;
+ pOut->u.i = v1;
MemSetTypeFlag(pOut, MEM_Int);
}
break;
}
/* Opcode: Not P1 P2 * * *
+** Synopsis: r[P2]= !r[P1]
**
** Interpret the value in register P1 as a boolean value. Store the
** boolean complement in register P2. If the value in register P1 is
@@ -64755,15 +71159,16 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */
case OP_Not: { /* same as TK_NOT, in1, out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
- if( pIn1->flags & MEM_Null ){
- sqlite3VdbeMemSetNull(pOut);
- }else{
- sqlite3VdbeMemSetInt64(pOut, !sqlite3VdbeIntValue(pIn1));
+ sqlite3VdbeMemSetNull(pOut);
+ if( (pIn1->flags & MEM_Null)==0 ){
+ pOut->flags = MEM_Int;
+ pOut->u.i = !sqlite3VdbeIntValue(pIn1);
}
break;
}
/* Opcode: BitNot P1 P2 * * *
+** Synopsis: r[P1]= ~r[P1]
**
** Interpret the content of register P1 as an integer. Store the
** ones-complement of the P1 value into register P2. If P1 holds
@@ -64772,10 +71177,32 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */
case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
- if( pIn1->flags & MEM_Null ){
- sqlite3VdbeMemSetNull(pOut);
+ sqlite3VdbeMemSetNull(pOut);
+ if( (pIn1->flags & MEM_Null)==0 ){
+ pOut->flags = MEM_Int;
+ pOut->u.i = ~sqlite3VdbeIntValue(pIn1);
+ }
+ break;
+}
+
+/* Opcode: Once P1 P2 * * *
+**
+** Check the "once" flag number P1. If it is set, jump to instruction P2.
+** Otherwise, set the flag and fall through to the next instruction.
+** In other words, this opcode causes all following opcodes up through P2
+** (but not including P2) to run just once and to be skipped on subsequent
+** times through the loop.
+**
+** All "once" flags are initially cleared whenever a prepared statement
+** first begins to run.
+*/
+case OP_Once: { /* jump */
+ assert( pOp->p1<p->nOnceFlag );
+ VdbeBranchTaken(p->aOnceFlag[pOp->p1]!=0, 2);
+ if( p->aOnceFlag[pOp->p1] ){
+ pc = pOp->p2-1;
}else{
- sqlite3VdbeMemSetInt64(pOut, ~sqlite3VdbeIntValue(pIn1));
+ p->aOnceFlag[pOp->p1] = 1;
}
break;
}
@@ -64784,42 +71211,43 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
**
** Jump to P2 if the value in register P1 is true. The value
** is considered true if it is numeric and non-zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** in P1 is NULL then take the jump if and only if P3 is non-zero.
*/
/* Opcode: IfNot P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is False. The value
-** is considered true if it has a numeric value of zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** is considered false if it has a numeric value of zero. If the value
+** in P1 is NULL then take the jump if and only if P3 is non-zero.
*/
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
-#if 0 /* local variables moved into u.al */
int c;
-#endif /* local variables moved into u.al */
pIn1 = &aMem[pOp->p1];
if( pIn1->flags & MEM_Null ){
- u.al.c = pOp->p3;
+ c = pOp->p3;
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
- u.al.c = sqlite3VdbeIntValue(pIn1)!=0;
+ c = sqlite3VdbeIntValue(pIn1)!=0;
#else
- u.al.c = sqlite3VdbeRealValue(pIn1)!=0.0;
+ c = sqlite3VdbeRealValue(pIn1)!=0.0;
#endif
- if( pOp->opcode==OP_IfNot ) u.al.c = !u.al.c;
+ if( pOp->opcode==OP_IfNot ) c = !c;
}
- if( u.al.c ){
+ VdbeBranchTaken(c!=0, 2);
+ if( c ){
pc = pOp->p2-1;
}
break;
}
/* Opcode: IsNull P1 P2 * * *
+** Synopsis: if r[P1]==NULL goto P2
**
** Jump to P2 if the value in register P1 is NULL.
*/
case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
+ VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2);
if( (pIn1->flags & MEM_Null)!=0 ){
pc = pOp->p2 - 1;
}
@@ -64827,11 +71255,13 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
}
/* Opcode: NotNull P1 P2 * * *
+** Synopsis: if r[P1]!=NULL goto P2
**
** Jump to P2 if the value in register P1 is not NULL.
*/
case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
+ VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2);
if( (pIn1->flags & MEM_Null)==0 ){
pc = pOp->p2 - 1;
}
@@ -64839,6 +71269,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
}
/* Opcode: Column P1 P2 P3 P4 P5
+** Synopsis: r[P3]=PX
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
@@ -64856,151 +71287,96 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** then the cache of the cursor is reset prior to extracting the column.
** The first OP_Column against a pseudo-table after the value of the content
** register has changed should have this bit set.
+**
+** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when
+** the result is guaranteed to only be used as the argument of a length()
+** or typeof() function, respectively. The loading of large blobs can be
+** skipped for length() and all content loading can be skipped for typeof().
*/
case OP_Column: {
-#if 0 /* local variables moved into u.am */
- u32 payloadSize; /* Number of bytes in the record */
i64 payloadSize64; /* Number of bytes in the record */
- int p1; /* P1 value of the opcode */
int p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
- char *zRec; /* Pointer to complete record-data */
BtCursor *pCrsr; /* The BTree cursor */
- u32 *aType; /* aType[i] holds the numeric type of the i-th column */
u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
- int nField; /* number of fields in the record */
int len; /* The length of the serialized data for the column */
int i; /* Loop counter */
- char *zData; /* Part of the record being decoded */
Mem *pDest; /* Where to write the extracted value */
Mem sMem; /* For storing the record being decoded */
- u8 *zIdx; /* Index into header */
- u8 *zEndHdr; /* Pointer to first byte after the header */
+ const u8 *zData; /* Part of the record being decoded */
+ const u8 *zHdr; /* Next unparsed byte of the header */
+ const u8 *zEndHdr; /* Pointer to first byte after the header */
u32 offset; /* Offset into the data */
u32 szField; /* Number of bytes in the content of a field */
- int szHdr; /* Size of the header size field at start of record */
- int avail; /* Number of bytes of available data */
+ u32 avail; /* Number of bytes of available data */
+ u32 t; /* A type code from the record header */
+ u16 fx; /* pDest->flags value */
Mem *pReg; /* PseudoTable input register */
-#endif /* local variables moved into u.am */
-
-
- u.am.p1 = pOp->p1;
- u.am.p2 = pOp->p2;
- u.am.pC = 0;
- memset(&u.am.sMem, 0, sizeof(u.am.sMem));
- assert( u.am.p1<p->nCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.am.pDest = &aMem[pOp->p3];
- memAboutToChange(p, u.am.pDest);
- MemSetTypeFlag(u.am.pDest, MEM_Null);
- u.am.zRec = 0;
-
- /* This block sets the variable u.am.payloadSize to be the total number of
- ** bytes in the record.
- **
- ** u.am.zRec is set to be the complete text of the record if it is available.
- ** The complete record text is always available for pseudo-tables
- ** If the record is stored in a cursor, the complete record text
- ** might be available in the u.am.pC->aRow cache. Or it might not be.
- ** If the data is unavailable, u.am.zRec is set to NULL.
- **
- ** We also compute the number of columns in the record. For cursors,
- ** the number of columns is stored in the VdbeCursor.nField element.
- */
- u.am.pC = p->apCsr[u.am.p1];
- assert( u.am.pC!=0 );
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- assert( u.am.pC->pVtabCursor==0 );
-#endif
- u.am.pCrsr = u.am.pC->pCursor;
- if( u.am.pCrsr!=0 ){
- /* The record is stored in a B-Tree */
- rc = sqlite3VdbeCursorMoveto(u.am.pC);
- if( rc ) goto abort_due_to_error;
- if( u.am.pC->nullRow ){
- u.am.payloadSize = 0;
- }else if( u.am.pC->cacheStatus==p->cacheCtr ){
- u.am.payloadSize = u.am.pC->payloadSize;
- u.am.zRec = (char*)u.am.pC->aRow;
- }else if( u.am.pC->isIndex ){
- assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) );
- rc = sqlite3BtreeKeySize(u.am.pCrsr, &u.am.payloadSize64);
- assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
- /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the
- ** payload size, so it is impossible for u.am.payloadSize64 to be
- ** larger than 32 bits. */
- assert( (u.am.payloadSize64 & SQLITE_MAX_U32)==(u64)u.am.payloadSize64 );
- u.am.payloadSize = (u32)u.am.payloadSize64;
- }else{
- assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) );
- rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize);
- assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
- }
- }else if( u.am.pC->pseudoTableReg>0 ){
- u.am.pReg = &aMem[u.am.pC->pseudoTableReg];
- assert( u.am.pReg->flags & MEM_Blob );
- assert( memIsValid(u.am.pReg) );
- u.am.payloadSize = u.am.pReg->n;
- u.am.zRec = u.am.pReg->z;
- u.am.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr;
- assert( u.am.payloadSize==0 || u.am.zRec!=0 );
- }else{
- /* Consider the row to be NULL */
- u.am.payloadSize = 0;
- }
-
- /* If u.am.payloadSize is 0, then just store a NULL */
- if( u.am.payloadSize==0 ){
- assert( u.am.pDest->flags&MEM_Null );
- goto op_column_out;
- }
- assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 );
- if( u.am.payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
- u.am.nField = u.am.pC->nField;
- assert( u.am.p2<u.am.nField );
-
- /* Read and parse the table header. Store the results of the parse
- ** into the record header cache fields of the cursor.
- */
- u.am.aType = u.am.pC->aType;
- if( u.am.pC->cacheStatus==p->cacheCtr ){
- u.am.aOffset = u.am.pC->aOffset;
- }else{
- assert(u.am.aType);
- u.am.avail = 0;
- u.am.pC->aOffset = u.am.aOffset = &u.am.aType[u.am.nField];
- u.am.pC->payloadSize = u.am.payloadSize;
- u.am.pC->cacheStatus = p->cacheCtr;
-
- /* Figure out how many bytes are in the header */
- if( u.am.zRec ){
- u.am.zData = u.am.zRec;
- }else{
- if( u.am.pC->isIndex ){
- u.am.zData = (char*)sqlite3BtreeKeyFetch(u.am.pCrsr, &u.am.avail);
+ p2 = pOp->p2;
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+ pDest = &aMem[pOp->p3];
+ memAboutToChange(p, pDest);
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( p2<pC->nField );
+ aOffset = pC->aOffset;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */
+#endif
+ pCrsr = pC->pCursor;
+ assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */
+ assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */
+
+ /* If the cursor cache is stale, bring it up-to-date */
+ rc = sqlite3VdbeCursorMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ if( pC->cacheStatus!=p->cacheCtr ){
+ if( pC->nullRow ){
+ if( pCrsr==0 ){
+ assert( pC->pseudoTableReg>0 );
+ pReg = &aMem[pC->pseudoTableReg];
+ assert( pReg->flags & MEM_Blob );
+ assert( memIsValid(pReg) );
+ pC->payloadSize = pC->szRow = avail = pReg->n;
+ pC->aRow = (u8*)pReg->z;
}else{
- u.am.zData = (char*)sqlite3BtreeDataFetch(u.am.pCrsr, &u.am.avail);
+ sqlite3VdbeMemSetNull(pDest);
+ goto op_column_out;
}
- /* If KeyFetch()/DataFetch() managed to get the entire payload,
- ** save the payload in the u.am.pC->aRow cache. That will save us from
- ** having to make additional calls to fetch the content portion of
- ** the record.
- */
- assert( u.am.avail>=0 );
- if( u.am.payloadSize <= (u32)u.am.avail ){
- u.am.zRec = u.am.zData;
- u.am.pC->aRow = (u8*)u.am.zData;
+ }else{
+ assert( pCrsr );
+ if( pC->isTable==0 ){
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+ VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64);
+ assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
+ /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the
+ ** payload size, so it is impossible for payloadSize64 to be
+ ** larger than 32 bits. */
+ assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 );
+ pC->aRow = sqlite3BtreeKeyFetch(pCrsr, &avail);
+ pC->payloadSize = (u32)payloadSize64;
+ }else{
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+ VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &pC->payloadSize);
+ assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
+ pC->aRow = sqlite3BtreeDataFetch(pCrsr, &avail);
+ }
+ assert( avail<=65536 ); /* Maximum page size is 64KiB */
+ if( pC->payloadSize <= (u32)avail ){
+ pC->szRow = pC->payloadSize;
}else{
- u.am.pC->aRow = 0;
+ pC->szRow = avail;
+ }
+ if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
}
}
- /* The following assert is true in all cases accept when
- ** the database file has been corrupted externally.
- ** assert( u.am.zRec!=0 || u.am.avail>=u.am.payloadSize || u.am.avail>=9 ); */
- u.am.szHdr = getVarint32((u8*)u.am.zData, u.am.offset);
+ pC->cacheStatus = p->cacheCtr;
+ pC->iHdrOffset = getVarint32(pC->aRow, offset);
+ pC->nHdrParsed = 0;
+ aOffset[0] = offset;
/* Make sure a corrupt database has not given us an oversize header.
** Do this now to avoid an oversize memory allocation.
@@ -65011,140 +71387,174 @@ case OP_Column: {
** 3-byte type for each of the maximum of 32768 columns plus three
** extra bytes for the header length itself. 32768*3 + 3 = 98307.
*/
- if( u.am.offset > 98307 ){
+ if( offset > 98307 || offset > pC->payloadSize ){
rc = SQLITE_CORRUPT_BKPT;
- goto op_column_out;
+ goto op_column_error;
}
- /* Compute in u.am.len the number of bytes of data we need to read in order
- ** to get u.am.nField type values. u.am.offset is an upper bound on this. But
- ** u.am.nField might be significantly less than the true number of columns
- ** in the table, and in that case, 5*u.am.nField+3 might be smaller than u.am.offset.
- ** We want to minimize u.am.len in order to limit the size of the memory
- ** allocation, especially if a corrupt database file has caused u.am.offset
- ** to be oversized. Offset is limited to 98307 above. But 98307 might
- ** still exceed Robson memory allocation limits on some configurations.
- ** On systems that cannot tolerate large memory allocations, u.am.nField*5+3
- ** will likely be much smaller since u.am.nField will likely be less than
- ** 20 or so. This insures that Robson memory allocation limits are
- ** not exceeded even for corrupt database files.
- */
- u.am.len = u.am.nField*5 + 3;
- if( u.am.len > (int)u.am.offset ) u.am.len = (int)u.am.offset;
-
- /* The KeyFetch() or DataFetch() above are fast and will get the entire
- ** record header in most cases. But they will fail to get the complete
- ** record header if the record header does not fit on a single page
- ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to
- ** acquire the complete header text.
- */
- if( !u.am.zRec && u.am.avail<u.am.len ){
- u.am.sMem.flags = 0;
- u.am.sMem.db = 0;
- rc = sqlite3VdbeMemFromBtree(u.am.pCrsr, 0, u.am.len, u.am.pC->isIndex, &u.am.sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_out;
- }
- u.am.zData = u.am.sMem.z;
+ if( avail<offset ){
+ /* pC->aRow does not have to hold the entire row, but it does at least
+ ** need to cover the header of the record. If pC->aRow does not contain
+ ** the complete header, then set it to zero, forcing the header to be
+ ** dynamically allocated. */
+ pC->aRow = 0;
+ pC->szRow = 0;
}
- u.am.zEndHdr = (u8 *)&u.am.zData[u.am.len];
- u.am.zIdx = (u8 *)&u.am.zData[u.am.szHdr];
- /* Scan the header and use it to fill in the u.am.aType[] and u.am.aOffset[]
- ** arrays. u.am.aType[u.am.i] will contain the type integer for the u.am.i-th
- ** column and u.am.aOffset[u.am.i] will contain the u.am.offset from the beginning
- ** of the record to the start of the data for the u.am.i-th column
+ /* The following goto is an optimization. It can be omitted and
+ ** everything will still work. But OP_Column is measurably faster
+ ** by skipping the subsequent conditional, which is always true.
*/
- for(u.am.i=0; u.am.i<u.am.nField; u.am.i++){
- if( u.am.zIdx<u.am.zEndHdr ){
- u.am.aOffset[u.am.i] = u.am.offset;
- u.am.zIdx += getVarint32(u.am.zIdx, u.am.aType[u.am.i]);
- u.am.szField = sqlite3VdbeSerialTypeLen(u.am.aType[u.am.i]);
- u.am.offset += u.am.szField;
- if( u.am.offset<u.am.szField ){ /* True if u.am.offset overflows */
- u.am.zIdx = &u.am.zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
- break;
+ assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
+ goto op_column_read_header;
+ }
+
+ /* Make sure at least the first p2+1 entries of the header have been
+ ** parsed and valid information is in aOffset[] and pC->aType[].
+ */
+ if( pC->nHdrParsed<=p2 ){
+ /* If there is more header available for parsing in the record, try
+ ** to extract additional fields up through the p2+1-th field
+ */
+ op_column_read_header:
+ if( pC->iHdrOffset<aOffset[0] ){
+ /* Make sure zData points to enough of the record to cover the header. */
+ if( pC->aRow==0 ){
+ memset(&sMem, 0, sizeof(sMem));
+ rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0],
+ !pC->isTable, &sMem);
+ if( rc!=SQLITE_OK ){
+ goto op_column_error;
}
+ zData = (u8*)sMem.z;
}else{
- /* If u.am.i is less that u.am.nField, then there are less fields in this
- ** record than SetNumColumns indicated there are columns in the
- ** table. Set the u.am.offset for any extra columns not present in
- ** the record to 0. This tells code below to store a NULL
- ** instead of deserializing a value from the record.
- */
- u.am.aOffset[u.am.i] = 0;
+ zData = pC->aRow;
+ }
+
+ /* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */
+ i = pC->nHdrParsed;
+ offset = aOffset[i];
+ zHdr = zData + pC->iHdrOffset;
+ zEndHdr = zData + aOffset[0];
+ assert( i<=p2 && zHdr<zEndHdr );
+ do{
+ if( zHdr[0]<0x80 ){
+ t = zHdr[0];
+ zHdr++;
+ }else{
+ zHdr += sqlite3GetVarint32(zHdr, &t);
+ }
+ pC->aType[i] = t;
+ szField = sqlite3VdbeSerialTypeLen(t);
+ offset += szField;
+ if( offset<szField ){ /* True if offset overflows */
+ zHdr = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
+ break;
+ }
+ i++;
+ aOffset[i] = offset;
+ }while( i<=p2 && zHdr<zEndHdr );
+ pC->nHdrParsed = i;
+ pC->iHdrOffset = (u32)(zHdr - zData);
+ if( pC->aRow==0 ){
+ sqlite3VdbeMemRelease(&sMem);
+ sMem.flags = MEM_Null;
+ }
+
+ /* The record is corrupt if any of the following are true:
+ ** (1) the bytes of the header extend past the declared header size
+ ** (zHdr>zEndHdr)
+ ** (2) the entire header was used but not all data was used
+ ** (zHdr==zEndHdr && offset!=pC->payloadSize)
+ ** (3) the end of the data extends beyond the end of the record.
+ ** (offset > pC->payloadSize)
+ */
+ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset!=pC->payloadSize))
+ || (offset > pC->payloadSize)
+ ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto op_column_error;
}
}
- sqlite3VdbeMemRelease(&u.am.sMem);
- u.am.sMem.flags = MEM_Null;
- /* If we have read more header data than was contained in the header,
- ** or if the end of the last field appears to be past the end of the
- ** record, or if the end of the last field appears to be before the end
- ** of the record (when all fields present), then we must be dealing
- ** with a corrupt database.
+ /* If after trying to extra new entries from the header, nHdrParsed is
+ ** still not up to p2, that means that the record has fewer than p2
+ ** columns. So the result will be either the default value or a NULL.
*/
- if( (u.am.zIdx > u.am.zEndHdr) || (u.am.offset > u.am.payloadSize)
- || (u.am.zIdx==u.am.zEndHdr && u.am.offset!=u.am.payloadSize) ){
- rc = SQLITE_CORRUPT_BKPT;
+ if( pC->nHdrParsed<=p2 ){
+ if( pOp->p4type==P4_MEM ){
+ sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
+ }else{
+ sqlite3VdbeMemSetNull(pDest);
+ }
goto op_column_out;
}
}
- /* Get the column information. If u.am.aOffset[u.am.p2] is non-zero, then
- ** deserialize the value from the record. If u.am.aOffset[u.am.p2] is zero,
- ** then there are not enough fields in the record to satisfy the
- ** request. In this case, set the value NULL or to P4 if P4 is
- ** a pointer to a Mem object.
+ /* Extract the content for the p2+1-th column. Control can only
+ ** reach this point if aOffset[p2], aOffset[p2+1], and pC->aType[p2] are
+ ** all valid.
*/
- if( u.am.aOffset[u.am.p2] ){
- assert( rc==SQLITE_OK );
- if( u.am.zRec ){
- sqlite3VdbeMemReleaseExternal(u.am.pDest);
- sqlite3VdbeSerialGet((u8 *)&u.am.zRec[u.am.aOffset[u.am.p2]], u.am.aType[u.am.p2], u.am.pDest);
+ assert( p2<pC->nHdrParsed );
+ assert( rc==SQLITE_OK );
+ assert( sqlite3VdbeCheckMemInvariants(pDest) );
+ if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest);
+ t = pC->aType[p2];
+ if( pC->szRow>=aOffset[p2+1] ){
+ /* This is the common case where the desired content fits on the original
+ ** page - where the content is not on an overflow page */
+ sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], t, pDest);
+ }else{
+ /* This branch happens only when content is on overflow pages */
+ if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
+ && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
+ || (len = sqlite3VdbeSerialTypeLen(t))==0
+ ){
+ /* Content is irrelevant for
+ ** 1. the typeof() function,
+ ** 2. the length(X) function if X is a blob, and
+ ** 3. if the content length is zero.
+ ** So we might as well use bogus content rather than reading
+ ** content from disk. NULL will work for the value for strings
+ ** and blobs and whatever is in the payloadSize64 variable
+ ** will work for everything else. */
+ sqlite3VdbeSerialGet(t<=13 ? (u8*)&payloadSize64 : 0, t, pDest);
}else{
- u.am.len = sqlite3VdbeSerialTypeLen(u.am.aType[u.am.p2]);
- sqlite3VdbeMemMove(&u.am.sMem, u.am.pDest);
- rc = sqlite3VdbeMemFromBtree(u.am.pCrsr, u.am.aOffset[u.am.p2], u.am.len, u.am.pC->isIndex, &u.am.sMem);
+ rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable,
+ pDest);
if( rc!=SQLITE_OK ){
- goto op_column_out;
+ goto op_column_error;
}
- u.am.zData = u.am.sMem.z;
- sqlite3VdbeSerialGet((u8*)u.am.zData, u.am.aType[u.am.p2], u.am.pDest);
+ sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest);
+ pDest->flags &= ~MEM_Ephem;
}
- u.am.pDest->enc = encoding;
- }else{
- if( pOp->p4type==P4_MEM ){
- sqlite3VdbeMemShallowCopy(u.am.pDest, pOp->p4.pMem, MEM_Static);
- }else{
- assert( u.am.pDest->flags&MEM_Null );
- }
- }
-
- /* If we dynamically allocated space to hold the data (in the
- ** sqlite3VdbeMemFromBtree() call above) then transfer control of that
- ** dynamically allocated space over to the u.am.pDest structure.
- ** This prevents a memory copy.
- */
- if( u.am.sMem.zMalloc ){
- assert( u.am.sMem.z==u.am.sMem.zMalloc );
- assert( !(u.am.pDest->flags & MEM_Dyn) );
- assert( !(u.am.pDest->flags & (MEM_Blob|MEM_Str)) || u.am.pDest->z==u.am.sMem.z );
- u.am.pDest->flags &= ~(MEM_Ephem|MEM_Static);
- u.am.pDest->flags |= MEM_Term;
- u.am.pDest->z = u.am.sMem.z;
- u.am.pDest->zMalloc = u.am.sMem.zMalloc;
}
-
- rc = sqlite3VdbeMemMakeWriteable(u.am.pDest);
+ pDest->enc = encoding;
op_column_out:
- UPDATE_MAX_BLOBSIZE(u.am.pDest);
- REGISTER_TRACE(pOp->p3, u.am.pDest);
+ /* If the column value is an ephemeral string, go ahead and persist
+ ** that string in case the cursor moves before the column value is
+ ** used. The following code does the equivalent of Deephemeralize()
+ ** but does it faster. */
+ if( (pDest->flags & MEM_Ephem)!=0 && pDest->z ){
+ fx = pDest->flags & (MEM_Str|MEM_Blob);
+ assert( fx!=0 );
+ zData = (const u8*)pDest->z;
+ len = pDest->n;
+ if( sqlite3VdbeMemClearAndResize(pDest, len+2) ) goto no_mem;
+ memcpy(pDest->z, zData, len);
+ pDest->z[len] = 0;
+ pDest->z[len+1] = 0;
+ pDest->flags = fx|MEM_Term;
+ }
+op_column_error:
+ UPDATE_MAX_BLOBSIZE(pDest);
+ REGISTER_TRACE(pOp->p3, pDest);
break;
}
/* Opcode: Affinity P1 P2 * P4 *
+** Synopsis: affinity(r[P1@P2])
**
** Apply affinities to a range of P2 registers starting with P1.
**
@@ -65153,26 +71563,24 @@ op_column_out:
** memory cell in the range.
*/
case OP_Affinity: {
-#if 0 /* local variables moved into u.an */
const char *zAffinity; /* The affinity to be applied */
char cAff; /* A single character of affinity */
-#endif /* local variables moved into u.an */
- u.an.zAffinity = pOp->p4.z;
- assert( u.an.zAffinity!=0 );
- assert( u.an.zAffinity[pOp->p2]==0 );
+ zAffinity = pOp->p4.z;
+ assert( zAffinity!=0 );
+ assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
- while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){
- assert( pIn1 <= &p->aMem[p->nMem] );
+ while( (cAff = *(zAffinity++))!=0 ){
+ assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] );
assert( memIsValid(pIn1) );
- ExpandBlob(pIn1);
- applyAffinity(pIn1, u.an.cAff, encoding);
+ applyAffinity(pIn1, cAff, encoding);
pIn1++;
}
break;
}
/* Opcode: MakeRecord P1 P2 P3 P4 *
+** Synopsis: r[P3]=mkrec(r[P1@P2])
**
** Convert P2 registers beginning with P1 into the [record format]
** use as a data record in a database table or as a key
@@ -65188,7 +71596,6 @@ case OP_Affinity: {
** If P4 is NULL then all index fields have the affinity NONE.
*/
case OP_MakeRecord: {
-#if 0 /* local variables moved into u.ao */
u8 *zNewRecord; /* A buffer to hold the data for the new record */
Mem *pRec; /* The new record */
u64 nData; /* Number of bytes of data space */
@@ -65202,102 +71609,119 @@ case OP_MakeRecord: {
int nField; /* Number of fields in the record */
char *zAffinity; /* The affinity string for the record */
int file_format; /* File format to use for encoding */
- int i; /* Space used in zNewRecord[] */
+ int i; /* Space used in zNewRecord[] header */
+ int j; /* Space used in zNewRecord[] content */
int len; /* Length of a field */
-#endif /* local variables moved into u.ao */
/* Assuming the record contains N fields, the record format looks
** like this:
**
** ------------------------------------------------------------------------
- ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
+ ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
** ------------------------------------------------------------------------
**
** Data(0) is taken from register P1. Data(1) comes from register P1+1
- ** and so froth.
+ ** and so forth.
**
- ** Each type field is a varint representing the serial type of the
+ ** Each type field is a varint representing the serial type of the
** corresponding data element (see sqlite3VdbeSerialType()). The
** hdr-size field is also a varint which is the offset from the beginning
** of the record to data0.
*/
- u.ao.nData = 0; /* Number of bytes of data space */
- u.ao.nHdr = 0; /* Number of bytes of header space */
- u.ao.nZero = 0; /* Number of zero bytes at the end of the record */
- u.ao.nField = pOp->p1;
- u.ao.zAffinity = pOp->p4.z;
- assert( u.ao.nField>0 && pOp->p2>0 && pOp->p2+u.ao.nField<=p->nMem+1 );
- u.ao.pData0 = &aMem[u.ao.nField];
- u.ao.nField = pOp->p2;
- u.ao.pLast = &u.ao.pData0[u.ao.nField-1];
- u.ao.file_format = p->minWriteFileFormat;
+ nData = 0; /* Number of bytes of data space */
+ nHdr = 0; /* Number of bytes of header space */
+ nZero = 0; /* Number of zero bytes at the end of the record */
+ nField = pOp->p1;
+ zAffinity = pOp->p4.z;
+ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem-p->nCursor)+1 );
+ pData0 = &aMem[nField];
+ nField = pOp->p2;
+ pLast = &pData0[nField-1];
+ file_format = p->minWriteFileFormat;
/* Identify the output register */
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
+ /* Apply the requested affinity to all inputs
+ */
+ assert( pData0<=pLast );
+ if( zAffinity ){
+ pRec = pData0;
+ do{
+ applyAffinity(pRec++, *(zAffinity++), encoding);
+ assert( zAffinity[0]==0 || pRec<=pLast );
+ }while( zAffinity[0] );
+ }
+
/* Loop through the elements that will make up the record to figure
** out how much space is required for the new record.
*/
- for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){
- assert( memIsValid(u.ao.pRec) );
- if( u.ao.zAffinity ){
- applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding);
- }
- if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){
- sqlite3VdbeMemExpandBlob(u.ao.pRec);
- }
- u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format);
- u.ao.len = sqlite3VdbeSerialTypeLen(u.ao.serial_type);
- u.ao.nData += u.ao.len;
- u.ao.nHdr += sqlite3VarintLen(u.ao.serial_type);
- if( u.ao.pRec->flags & MEM_Zero ){
- /* Only pure zero-filled BLOBs can be input to this Opcode.
- ** We do not allow blobs with a prefix and a zero-filled tail. */
- u.ao.nZero += u.ao.pRec->u.nZero;
- }else if( u.ao.len ){
- u.ao.nZero = 0;
+ pRec = pLast;
+ do{
+ assert( memIsValid(pRec) );
+ pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format);
+ len = sqlite3VdbeSerialTypeLen(serial_type);
+ if( pRec->flags & MEM_Zero ){
+ if( nData ){
+ sqlite3VdbeMemExpandBlob(pRec);
+ }else{
+ nZero += pRec->u.nZero;
+ len -= pRec->u.nZero;
+ }
}
- }
+ nData += len;
+ testcase( serial_type==127 );
+ testcase( serial_type==128 );
+ nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
+ }while( (--pRec)>=pData0 );
/* Add the initial header varint and total the size */
- u.ao.nHdr += u.ao.nVarint = sqlite3VarintLen(u.ao.nHdr);
- if( u.ao.nVarint<sqlite3VarintLen(u.ao.nHdr) ){
- u.ao.nHdr++;
+ testcase( nHdr==126 );
+ testcase( nHdr==127 );
+ if( nHdr<=126 ){
+ /* The common case */
+ nHdr += 1;
+ }else{
+ /* Rare case of a really large header */
+ nVarint = sqlite3VarintLen(nHdr);
+ nHdr += nVarint;
+ if( nVarint<sqlite3VarintLen(nHdr) ) nHdr++;
}
- u.ao.nByte = u.ao.nHdr+u.ao.nData-u.ao.nZero;
- if( u.ao.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ nByte = nHdr+nData;
+ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- /* Make sure the output register has a buffer large enough to store
+ /* Make sure the output register has a buffer large enough to store
** the new record. The output register (pOp->p3) is not allowed to
** be one of the input registers (because the following call to
- ** sqlite3VdbeMemGrow() could clobber the value before it is used).
+ ** sqlite3VdbeMemClearAndResize() could clobber the value before it is used).
*/
- if( sqlite3VdbeMemGrow(pOut, (int)u.ao.nByte, 0) ){
+ if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){
goto no_mem;
}
- u.ao.zNewRecord = (u8 *)pOut->z;
+ zNewRecord = (u8 *)pOut->z;
/* Write the record */
- u.ao.i = putVarint32(u.ao.zNewRecord, u.ao.nHdr);
- for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){
- u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format);
- u.ao.i += putVarint32(&u.ao.zNewRecord[u.ao.i], u.ao.serial_type); /* serial type */
- }
- for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ /* serial data */
- u.ao.i += sqlite3VdbeSerialPut(&u.ao.zNewRecord[u.ao.i], (int)(u.ao.nByte-u.ao.i), u.ao.pRec,u.ao.file_format);
- }
- assert( u.ao.i==u.ao.nByte );
-
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- pOut->n = (int)u.ao.nByte;
- pOut->flags = MEM_Blob | MEM_Dyn;
- pOut->xDel = 0;
- if( u.ao.nZero ){
- pOut->u.nZero = u.ao.nZero;
+ i = putVarint32(zNewRecord, nHdr);
+ j = nHdr;
+ assert( pData0<=pLast );
+ pRec = pData0;
+ do{
+ serial_type = pRec->uTemp;
+ i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
+ j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
+ }while( (++pRec)<=pLast );
+ assert( i==nHdr );
+ assert( j==nByte );
+
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
+ pOut->n = (int)nByte;
+ pOut->flags = MEM_Blob;
+ if( nZero ){
+ pOut->u.nZero = nZero;
pOut->flags |= MEM_Zero;
}
pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */
@@ -65307,24 +71731,21 @@ case OP_MakeRecord: {
}
/* Opcode: Count P1 P2 * * *
+** Synopsis: r[P2]=count()
**
** Store the number of entries (an integer value) in the table or index
** opened by cursor P1 in register P2
*/
#ifndef SQLITE_OMIT_BTREECOUNT
case OP_Count: { /* out2-prerelease */
-#if 0 /* local variables moved into u.ap */
i64 nEntry;
BtCursor *pCrsr;
-#endif /* local variables moved into u.ap */
- u.ap.pCrsr = p->apCsr[pOp->p1]->pCursor;
- if( u.ap.pCrsr ){
- rc = sqlite3BtreeCount(u.ap.pCrsr, &u.ap.nEntry);
- }else{
- u.ap.nEntry = 0;
- }
- pOut->u.i = u.ap.nEntry;
+ pCrsr = p->apCsr[pOp->p1]->pCursor;
+ assert( pCrsr );
+ nEntry = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3BtreeCount(pCrsr, &nEntry);
+ pOut->u.i = nEntry;
break;
}
#endif
@@ -65336,7 +71757,6 @@ case OP_Count: { /* out2-prerelease */
** existing savepoint, P1==1, or to rollback an existing savepoint P1==2.
*/
case OP_Savepoint: {
-#if 0 /* local variables moved into u.aq */
int p1; /* Value of P1 operand */
char *zName; /* Name of savepoint */
int nName;
@@ -65345,29 +71765,29 @@ case OP_Savepoint: {
Savepoint *pTmp;
int iSavepoint;
int ii;
-#endif /* local variables moved into u.aq */
- u.aq.p1 = pOp->p1;
- u.aq.zName = pOp->p4.z;
+ p1 = pOp->p1;
+ zName = pOp->p4.z;
- /* Assert that the u.aq.p1 parameter is valid. Also that if there is no open
- ** transaction, then there cannot be any savepoints.
+ /* Assert that the p1 parameter is valid. Also that if there is no open
+ ** transaction, then there cannot be any savepoints.
*/
assert( db->pSavepoint==0 || db->autoCommit==0 );
- assert( u.aq.p1==SAVEPOINT_BEGIN||u.aq.p1==SAVEPOINT_RELEASE||u.aq.p1==SAVEPOINT_ROLLBACK );
+ assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK );
assert( db->pSavepoint || db->isTransactionSavepoint==0 );
assert( checkSavepointCount(db) );
+ assert( p->bIsReader );
- if( u.aq.p1==SAVEPOINT_BEGIN ){
- if( db->writeVdbeCnt>0 ){
- /* A new savepoint cannot be created if there are active write
+ if( p1==SAVEPOINT_BEGIN ){
+ if( db->nVdbeWrite>0 ){
+ /* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - "
"SQL statements in progress");
rc = SQLITE_BUSY;
}else{
- u.aq.nName = sqlite3Strlen30(u.aq.zName);
+ nName = sqlite3Strlen30(zName);
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* This call is Ok even if this savepoint is actually a transaction
@@ -65381,11 +71801,11 @@ case OP_Savepoint: {
#endif
/* Create a new savepoint structure. */
- u.aq.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.aq.nName+1);
- if( u.aq.pNew ){
- u.aq.pNew->zName = (char *)&u.aq.pNew[1];
- memcpy(u.aq.pNew->zName, u.aq.zName, u.aq.nName+1);
-
+ pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+nName+1);
+ if( pNew ){
+ pNew->zName = (char *)&pNew[1];
+ memcpy(pNew->zName, zName, nName+1);
+
/* If there is no open transaction, then mark this as a special
** "transaction savepoint". */
if( db->autoCommit ){
@@ -65394,48 +71814,45 @@ case OP_Savepoint: {
}else{
db->nSavepoint++;
}
-
+
/* Link the new savepoint into the database handle's list. */
- u.aq.pNew->pNext = db->pSavepoint;
- db->pSavepoint = u.aq.pNew;
- u.aq.pNew->nDeferredCons = db->nDeferredCons;
+ pNew->pNext = db->pSavepoint;
+ db->pSavepoint = pNew;
+ pNew->nDeferredCons = db->nDeferredCons;
+ pNew->nDeferredImmCons = db->nDeferredImmCons;
}
}
}else{
- u.aq.iSavepoint = 0;
+ iSavepoint = 0;
/* Find the named savepoint. If there is no such savepoint, then an
** an error is returned to the user. */
for(
- u.aq.pSavepoint = db->pSavepoint;
- u.aq.pSavepoint && sqlite3StrICmp(u.aq.pSavepoint->zName, u.aq.zName);
- u.aq.pSavepoint = u.aq.pSavepoint->pNext
+ pSavepoint = db->pSavepoint;
+ pSavepoint && sqlite3StrICmp(pSavepoint->zName, zName);
+ pSavepoint = pSavepoint->pNext
){
- u.aq.iSavepoint++;
+ iSavepoint++;
}
- if( !u.aq.pSavepoint ){
- sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.aq.zName);
+ if( !pSavepoint ){
+ sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
rc = SQLITE_ERROR;
- }else if(
- db->writeVdbeCnt>0 || (u.aq.p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1)
- ){
- /* It is not possible to release (commit) a savepoint if there are
- ** active write statements. It is not possible to rollback a savepoint
- ** if there are any active statements at all.
+ }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
+ /* It is not possible to release (commit) a savepoint if there are
+ ** active write statements.
*/
- sqlite3SetString(&p->zErrMsg, db,
- "cannot %s savepoint - SQL statements in progress",
- (u.aq.p1==SAVEPOINT_ROLLBACK ? "rollback": "release")
+ sqlite3SetString(&p->zErrMsg, db,
+ "cannot release savepoint - SQL statements in progress"
);
rc = SQLITE_BUSY;
}else{
/* Determine whether or not this is a transaction savepoint. If so,
- ** and this is a RELEASE command, then the current transaction
- ** is committed.
+ ** and this is a RELEASE command, then the current transaction
+ ** is committed.
*/
- int isTransaction = u.aq.pSavepoint->pNext==0 && db->isTransactionSavepoint;
- if( isTransaction && u.aq.p1==SAVEPOINT_RELEASE ){
+ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
+ if( isTransaction && p1==SAVEPOINT_RELEASE ){
if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
}
@@ -65449,46 +71866,59 @@ case OP_Savepoint: {
db->isTransactionSavepoint = 0;
rc = p->rc;
}else{
- u.aq.iSavepoint = db->nSavepoint - u.aq.iSavepoint - 1;
- for(u.aq.ii=0; u.aq.ii<db->nDb; u.aq.ii++){
- rc = sqlite3BtreeSavepoint(db->aDb[u.aq.ii].pBt, u.aq.p1, u.aq.iSavepoint);
+ int isSchemaChange;
+ iSavepoint = db->nSavepoint - iSavepoint - 1;
+ if( p1==SAVEPOINT_ROLLBACK ){
+ isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
+ for(ii=0; ii<db->nDb; ii++){
+ rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
+ SQLITE_ABORT_ROLLBACK,
+ isSchemaChange==0);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ }
+ }else{
+ isSchemaChange = 0;
+ }
+ for(ii=0; ii<db->nDb; ii++){
+ rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
}
- if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
+ if( isSchemaChange ){
sqlite3ExpirePreparedStatements(db);
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
db->flags = (db->flags | SQLITE_InternChanges);
}
}
-
- /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all
+
+ /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all
** savepoints nested inside of the savepoint being operated on. */
- while( db->pSavepoint!=u.aq.pSavepoint ){
- u.aq.pTmp = db->pSavepoint;
- db->pSavepoint = u.aq.pTmp->pNext;
- sqlite3DbFree(db, u.aq.pTmp);
+ while( db->pSavepoint!=pSavepoint ){
+ pTmp = db->pSavepoint;
+ db->pSavepoint = pTmp->pNext;
+ sqlite3DbFree(db, pTmp);
db->nSavepoint--;
}
- /* If it is a RELEASE, then destroy the savepoint being operated on
- ** too. If it is a ROLLBACK TO, then set the number of deferred
+ /* If it is a RELEASE, then destroy the savepoint being operated on
+ ** too. If it is a ROLLBACK TO, then set the number of deferred
** constraint violations present in the database to the value stored
** when the savepoint was created. */
- if( u.aq.p1==SAVEPOINT_RELEASE ){
- assert( u.aq.pSavepoint==db->pSavepoint );
- db->pSavepoint = u.aq.pSavepoint->pNext;
- sqlite3DbFree(db, u.aq.pSavepoint);
+ if( p1==SAVEPOINT_RELEASE ){
+ assert( pSavepoint==db->pSavepoint );
+ db->pSavepoint = pSavepoint->pNext;
+ sqlite3DbFree(db, pSavepoint);
if( !isTransaction ){
db->nSavepoint--;
}
}else{
- db->nDeferredCons = u.aq.pSavepoint->nDeferredCons;
+ db->nDeferredCons = pSavepoint->nDeferredCons;
+ db->nDeferredImmCons = pSavepoint->nDeferredImmCons;
}
if( !isTransaction ){
- rc = sqlite3VtabSavepoint(db, u.aq.p1, u.aq.iSavepoint);
+ rc = sqlite3VtabSavepoint(db, p1, iSavepoint);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
}
@@ -65507,46 +71937,48 @@ case OP_Savepoint: {
** This instruction causes the VM to halt.
*/
case OP_AutoCommit: {
-#if 0 /* local variables moved into u.ar */
int desiredAutoCommit;
int iRollback;
int turnOnAC;
-#endif /* local variables moved into u.ar */
- u.ar.desiredAutoCommit = pOp->p1;
- u.ar.iRollback = pOp->p2;
- u.ar.turnOnAC = u.ar.desiredAutoCommit && !db->autoCommit;
- assert( u.ar.desiredAutoCommit==1 || u.ar.desiredAutoCommit==0 );
- assert( u.ar.desiredAutoCommit==1 || u.ar.iRollback==0 );
- assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
+ desiredAutoCommit = pOp->p1;
+ iRollback = pOp->p2;
+ turnOnAC = desiredAutoCommit && !db->autoCommit;
+ assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
+ assert( desiredAutoCommit==1 || iRollback==0 );
+ assert( db->nVdbeActive>0 ); /* At least this one VM is active */
+ assert( p->bIsReader );
- if( u.ar.turnOnAC && u.ar.iRollback && db->activeVdbeCnt>1 ){
+#if 0
+ if( turnOnAC && iRollback && db->nVdbeActive>1 ){
/* If this instruction implements a ROLLBACK and other VMs are
** still running, and a transaction is active, return an error indicating
- ** that the other VMs must complete first.
+ ** that the other VMs must complete first.
*/
sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
- }else if( u.ar.turnOnAC && !u.ar.iRollback && db->writeVdbeCnt>0 ){
+ }else
+#endif
+ if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
- ** return an error indicating that the other VMs must complete first.
+ ** return an error indicating that the other VMs must complete first.
*/
sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
- }else if( u.ar.desiredAutoCommit!=db->autoCommit ){
- if( u.ar.iRollback ){
- assert( u.ar.desiredAutoCommit==1 );
- sqlite3RollbackAll(db);
+ }else if( desiredAutoCommit!=db->autoCommit ){
+ if( iRollback ){
+ assert( desiredAutoCommit==1 );
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
}else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
}else{
- db->autoCommit = (u8)u.ar.desiredAutoCommit;
+ db->autoCommit = (u8)desiredAutoCommit;
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
p->pc = pc;
- db->autoCommit = (u8)(1-u.ar.desiredAutoCommit);
+ db->autoCommit = (u8)(1-desiredAutoCommit);
p->rc = rc = SQLITE_BUSY;
goto vdbe_return;
}
@@ -65561,57 +71993,66 @@ case OP_AutoCommit: {
goto vdbe_return;
}else{
sqlite3SetString(&p->zErrMsg, db,
- (!u.ar.desiredAutoCommit)?"cannot start a transaction within a transaction":(
- (u.ar.iRollback)?"cannot rollback - no transaction is active":
+ (!desiredAutoCommit)?"cannot start a transaction within a transaction":(
+ (iRollback)?"cannot rollback - no transaction is active":
"cannot commit - no transaction is active"));
-
+
rc = SQLITE_ERROR;
}
break;
}
-/* Opcode: Transaction P1 P2 * * *
+/* Opcode: Transaction P1 P2 P3 P4 P5
**
-** Begin a transaction. The transaction ends when a Commit or Rollback
-** opcode is encountered. Depending on the ON CONFLICT setting, the
-** transaction might also be rolled back if an error is encountered.
+** Begin a transaction on database P1 if a transaction is not already
+** active.
+** If P2 is non-zero, then a write-transaction is started, or if a
+** read-transaction is already active, it is upgraded to a write-transaction.
+** If P2 is zero, then a read-transaction is started.
**
** P1 is the index of the database file on which the transaction is
** started. Index 0 is the main database file and index 1 is the
** file used for temporary tables. Indices of 2 or more are used for
** attached databases.
**
-** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is
-** obtained on the database file when a write-transaction is started. No
-** other process can start another write transaction while this transaction is
-** underway. Starting a write transaction also creates a rollback journal. A
-** write transaction must be started before any changes can be made to the
-** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
-** on the file.
-**
** If a write-transaction is started and the Vdbe.usesStmtJournal flag is
** true (this flag is set if the Vdbe may modify more than one row and may
** throw an ABORT exception), a statement transaction may also be opened.
** More specifically, a statement transaction is opened iff the database
** connection is currently not in autocommit mode, or if there are other
-** active statements. A statement transaction allows the affects of this
+** active statements. A statement transaction allows the changes made by this
** VDBE to be rolled back after an error without having to roll back the
** entire transaction. If no error is encountered, the statement transaction
** will automatically commit when the VDBE halts.
**
-** If P2 is zero, then a read-lock is obtained on the database file.
+** If P5!=0 then this opcode also checks the schema cookie against P3
+** and the schema generation counter against P4.
+** The cookie changes its value whenever the database schema changes.
+** This operation is used to detect when that the cookie has changed
+** and that the current process needs to reread the schema. If the schema
+** cookie in P3 differs from the schema cookie in the database header or
+** if the schema generation counter in P4 differs from the current
+** generation counter, then an SQLITE_SCHEMA error is raised and execution
+** halts. The sqlite3_step() wrapper function might then reprepare the
+** statement and rerun it from the beginning.
*/
case OP_Transaction: {
-#if 0 /* local variables moved into u.as */
Btree *pBt;
-#endif /* local variables moved into u.as */
+ int iMeta;
+ int iGen;
+ assert( p->bIsReader );
+ assert( p->readOnly==0 || pOp->p2==0 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.as.pBt = db->aDb[pOp->p1].pBt;
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){
+ rc = SQLITE_READONLY;
+ goto abort_due_to_error;
+ }
+ pBt = db->aDb[pOp->p1].pBt;
- if( u.as.pBt ){
- rc = sqlite3BtreeBeginTrans(u.as.pBt, pOp->p2);
+ if( pBt ){
+ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
if( rc==SQLITE_BUSY ){
p->pc = pc;
p->rc = rc = SQLITE_BUSY;
@@ -65621,26 +72062,56 @@ case OP_Transaction: {
goto abort_due_to_error;
}
- if( pOp->p2 && p->usesStmtJournal
- && (db->autoCommit==0 || db->activeVdbeCnt>1)
+ if( pOp->p2 && p->usesStmtJournal
+ && (db->autoCommit==0 || db->nVdbeRead>1)
){
- assert( sqlite3BtreeIsInTrans(u.as.pBt) );
+ assert( sqlite3BtreeIsInTrans(pBt) );
if( p->iStatement==0 ){
assert( db->nStatement>=0 && db->nSavepoint>=0 );
- db->nStatement++;
+ db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement;
}
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1);
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement);
+ rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
}
/* Store the current value of the database handles deferred constraint
** counter. If the statement transaction needs to be rolled back,
** the value of this counter needs to be restored too. */
p->nStmtDefCons = db->nDeferredCons;
+ p->nStmtDefImmCons = db->nDeferredImmCons;
}
+
+ /* Gather the schema version number for checking */
+ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
+ iGen = db->aDb[pOp->p1].pSchema->iGeneration;
+ }else{
+ iGen = iMeta = 0;
+ }
+ assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
+ if( pOp->p5 && (iMeta!=pOp->p3 || iGen!=pOp->p4.i) ){
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
+ /* If the schema-cookie from the database file matches the cookie
+ ** stored with the in-memory representation of the schema, do
+ ** not reload the schema from the database file.
+ **
+ ** If virtual-tables are in use, this is not just an optimization.
+ ** Often, v-tables store their data in other SQLite tables, which
+ ** are queried from within xNext() and other v-table methods using
+ ** prepared queries. If such a query is out-of-date, we do not want to
+ ** discard the database schema, as the user code implementing the
+ ** v-table would have to be ready for the sqlite3_vtab structure itself
+ ** to be invalidated whenever sqlite3_step() is called from within
+ ** a v-table method.
+ */
+ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
+ sqlite3ResetOneSchema(db, pOp->p1);
+ }
+ p->expired = 1;
+ rc = SQLITE_SCHEMA;
}
break;
}
@@ -65658,21 +72129,20 @@ case OP_Transaction: {
** executing this instruction.
*/
case OP_ReadCookie: { /* out2-prerelease */
-#if 0 /* local variables moved into u.at */
int iMeta;
int iDb;
int iCookie;
-#endif /* local variables moved into u.at */
- u.at.iDb = pOp->p1;
- u.at.iCookie = pOp->p3;
+ assert( p->bIsReader );
+ iDb = pOp->p1;
+ iCookie = pOp->p3;
assert( pOp->p3<SQLITE_N_BTREE_META );
- assert( u.at.iDb>=0 && u.at.iDb<db->nDb );
- assert( db->aDb[u.at.iDb].pBt!=0 );
- assert( (p->btreeMask & (((yDbMask)1)<<u.at.iDb))!=0 );
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( db->aDb[iDb].pBt!=0 );
+ assert( DbMaskTest(p->btreeMask, iDb) );
- sqlite3BtreeGetMeta(db->aDb[u.at.iDb].pBt, u.at.iCookie, (u32 *)&u.at.iMeta);
- pOut->u.i = u.at.iMeta;
+ sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta);
+ pOut->u.i = iMeta;
break;
}
@@ -65687,26 +72157,25 @@ case OP_ReadCookie: { /* out2-prerelease */
** A transaction must be started before executing this opcode.
*/
case OP_SetCookie: { /* in3 */
-#if 0 /* local variables moved into u.au */
Db *pDb;
-#endif /* local variables moved into u.au */
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.au.pDb = &db->aDb[pOp->p1];
- assert( u.au.pDb->pBt!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
+ pDb = &db->aDb[pOp->p1];
+ assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
pIn3 = &aMem[pOp->p3];
sqlite3VdbeMemIntegerify(pIn3);
/* See note about index shifting on OP_ReadCookie */
- rc = sqlite3BtreeUpdateMeta(u.au.pDb->pBt, pOp->p2, (int)pIn3->u.i);
+ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
- u.au.pDb->pSchema->schema_cookie = (int)pIn3->u.i;
+ pDb->pSchema->schema_cookie = (int)pIn3->u.i;
db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
- u.au.pDb->pSchema->file_format = (u8)pIn3->u.i;
+ pDb->pSchema->file_format = (u8)pIn3->u.i;
}
if( pOp->p1==1 ){
/* Invalidate all prepared statements whenever the TEMP database
@@ -65717,68 +72186,8 @@ case OP_SetCookie: { /* in3 */
break;
}
-/* Opcode: VerifyCookie P1 P2 P3 * *
-**
-** Check the value of global database parameter number 0 (the
-** schema version) and make sure it is equal to P2 and that the
-** generation counter on the local schema parse equals P3.
-**
-** P1 is the database number which is 0 for the main database file
-** and 1 for the file holding temporary tables and some higher number
-** for auxiliary databases.
-**
-** The cookie changes its value whenever the database schema changes.
-** This operation is used to detect when that the cookie has changed
-** and that the current process needs to reread the schema.
-**
-** Either a transaction needs to have been started or an OP_Open needs
-** to be executed (to establish a read lock) before this opcode is
-** invoked.
-*/
-case OP_VerifyCookie: {
-#if 0 /* local variables moved into u.av */
- int iMeta;
- int iGen;
- Btree *pBt;
-#endif /* local variables moved into u.av */
-
- assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
- u.av.pBt = db->aDb[pOp->p1].pBt;
- if( u.av.pBt ){
- sqlite3BtreeGetMeta(u.av.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.av.iMeta);
- u.av.iGen = db->aDb[pOp->p1].pSchema->iGeneration;
- }else{
- u.av.iGen = u.av.iMeta = 0;
- }
- if( u.av.iMeta!=pOp->p2 || u.av.iGen!=pOp->p3 ){
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
- /* If the schema-cookie from the database file matches the cookie
- ** stored with the in-memory representation of the schema, do
- ** not reload the schema from the database file.
- **
- ** If virtual-tables are in use, this is not just an optimization.
- ** Often, v-tables store their data in other SQLite tables, which
- ** are queried from within xNext() and other v-table methods using
- ** prepared queries. If such a query is out-of-date, we do not want to
- ** discard the database schema, as the user code implementing the
- ** v-table would have to be ready for the sqlite3_vtab structure itself
- ** to be invalidated whenever sqlite3_step() is called from within
- ** a v-table method.
- */
- if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.av.iMeta ){
- sqlite3ResetInternalSchema(db, pOp->p1);
- }
-
- p->expired = 1;
- rc = SQLITE_SCHEMA;
- }
- break;
-}
-
/* Opcode: OpenRead P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
**
** Open a read-only cursor for the database table whose root page is
** P2 in a database file. The database file is determined by P3.
@@ -65806,9 +72215,24 @@ case OP_VerifyCookie: {
** sequence of the index being opened. Otherwise, if P4 is an integer
** value, it is set to the number of columns in the table.
**
-** See also OpenWrite.
+** See also: OpenWrite, ReopenIdx
+*/
+/* Opcode: ReopenIdx P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
+**
+** The ReopenIdx opcode works exactly like ReadOpen except that it first
+** checks to see if the cursor on P1 is already open with a root page
+** number of P2 and if it is this opcode becomes a no-op. In other words,
+** if the cursor is already open, do not reopen it.
+**
+** The ReopenIdx opcode may only be used with P5==0 and with P4 being
+** a P4_KEYINFO object. Furthermore, the P3 value must be the same as
+** every other ReopenIdx or OpenRead for the same cursor number.
+**
+** See the OpenRead opcode documentation for additional information.
*/
/* Opcode: OpenWrite P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
**
** Open a read/write cursor named P1 on the table or index whose root
** page is P2. Or if P5!=0 use the content of register P2 to find the
@@ -65827,9 +72251,21 @@ case OP_VerifyCookie: {
**
** See also OpenRead.
*/
+case OP_ReopenIdx: {
+ VdbeCursor *pCur;
+
+ assert( pOp->p5==0 );
+ assert( pOp->p4type==P4_KEYINFO );
+ pCur = p->apCsr[pOp->p1];
+ if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){
+ assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */
+ break;
+ }
+ /* If the cursor is not currently open or is open on a different
+ ** index, then fall through into OP_OpenRead to force a reopen */
+}
case OP_OpenRead:
case OP_OpenWrite: {
-#if 0 /* local variables moved into u.aw */
int nField;
KeyInfo *pKeyInfo;
int p2;
@@ -65838,83 +72274,84 @@ case OP_OpenWrite: {
Btree *pX;
VdbeCursor *pCur;
Db *pDb;
-#endif /* local variables moved into u.aw */
+
+ assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
+ assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
+ assert( p->bIsReader );
+ assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
+ || p->readOnly==0 );
if( p->expired ){
- rc = SQLITE_ABORT;
+ rc = SQLITE_ABORT_ROLLBACK;
break;
}
- u.aw.nField = 0;
- u.aw.pKeyInfo = 0;
- u.aw.p2 = pOp->p2;
- u.aw.iDb = pOp->p3;
- assert( u.aw.iDb>=0 && u.aw.iDb<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<u.aw.iDb))!=0 );
- u.aw.pDb = &db->aDb[u.aw.iDb];
- u.aw.pX = u.aw.pDb->pBt;
- assert( u.aw.pX!=0 );
+ nField = 0;
+ pKeyInfo = 0;
+ p2 = pOp->p2;
+ iDb = pOp->p3;
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( DbMaskTest(p->btreeMask, iDb) );
+ pDb = &db->aDb[iDb];
+ pX = pDb->pBt;
+ assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
- u.aw.wrFlag = 1;
- assert( sqlite3SchemaMutexHeld(db, u.aw.iDb, 0) );
- if( u.aw.pDb->pSchema->file_format < p->minWriteFileFormat ){
- p->minWriteFileFormat = u.aw.pDb->pSchema->file_format;
+ wrFlag = 1;
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ if( pDb->pSchema->file_format < p->minWriteFileFormat ){
+ p->minWriteFileFormat = pDb->pSchema->file_format;
}
}else{
- u.aw.wrFlag = 0;
+ wrFlag = 0;
}
- if( pOp->p5 ){
- assert( u.aw.p2>0 );
- assert( u.aw.p2<=p->nMem );
- pIn2 = &aMem[u.aw.p2];
+ if( pOp->p5 & OPFLAG_P2ISREG ){
+ assert( p2>0 );
+ assert( p2<=(p->nMem-p->nCursor) );
+ pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
sqlite3VdbeMemIntegerify(pIn2);
- u.aw.p2 = (int)pIn2->u.i;
- /* The u.aw.p2 value always comes from a prior OP_CreateTable opcode and
- ** that opcode will always set the u.aw.p2 value to 2 or more or else fail.
+ p2 = (int)pIn2->u.i;
+ /* The p2 value always comes from a prior OP_CreateTable opcode and
+ ** that opcode will always set the p2 value to 2 or more or else fail.
** If there were a failure, the prepared statement would have halted
** before reaching this instruction. */
- if( NEVER(u.aw.p2<2) ) {
+ if( NEVER(p2<2) ) {
rc = SQLITE_CORRUPT_BKPT;
goto abort_due_to_error;
}
}
if( pOp->p4type==P4_KEYINFO ){
- u.aw.pKeyInfo = pOp->p4.pKeyInfo;
- u.aw.pKeyInfo->enc = ENC(p->db);
- u.aw.nField = u.aw.pKeyInfo->nField+1;
+ pKeyInfo = pOp->p4.pKeyInfo;
+ assert( pKeyInfo->enc==ENC(db) );
+ assert( pKeyInfo->db==db );
+ nField = pKeyInfo->nField+pKeyInfo->nXField;
}else if( pOp->p4type==P4_INT32 ){
- u.aw.nField = pOp->p4.i;
+ nField = pOp->p4.i;
}
assert( pOp->p1>=0 );
- u.aw.pCur = allocateCursor(p, pOp->p1, u.aw.nField, u.aw.iDb, 1);
- if( u.aw.pCur==0 ) goto no_mem;
- u.aw.pCur->nullRow = 1;
- u.aw.pCur->isOrdered = 1;
- rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor);
- u.aw.pCur->pKeyInfo = u.aw.pKeyInfo;
-
- /* Since it performs no memory allocation or IO, the only values that
- ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK.
- ** SQLITE_EMPTY is only returned when attempting to open the table
- ** rooted at page 1 of a zero-byte database. */
- assert( rc==SQLITE_EMPTY || rc==SQLITE_OK );
- if( rc==SQLITE_EMPTY ){
- u.aw.pCur->pCursor = 0;
- rc = SQLITE_OK;
- }
+ assert( nField>=0 );
+ testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */
+ pCur = allocateCursor(p, pOp->p1, nField, iDb, 1);
+ if( pCur==0 ) goto no_mem;
+ pCur->nullRow = 1;
+ pCur->isOrdered = 1;
+ pCur->pgnoRoot = p2;
+ rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
+ pCur->pKeyInfo = pKeyInfo;
+ assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
+ sqlite3BtreeCursorHints(pCur->pCursor, (pOp->p5 & OPFLAG_BULKCSR));
- /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of
+ /* Set the VdbeCursor.isTable variable. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
** and report database corruption if they were not, but this check has
- ** since moved into the btree layer. */
- u.aw.pCur->isTable = pOp->p4type!=P4_KEYINFO;
- u.aw.pCur->isIndex = !u.aw.pCur->isTable;
+ ** since moved into the btree layer. */
+ pCur->isTable = pOp->p4type!=P4_KEYINFO;
break;
}
-/* Opcode: OpenEphemeral P1 P2 * P4 *
+/* Opcode: OpenEphemeral P1 P2 * P4 P5
+** Synopsis: nColumn=P2
**
** Open a new cursor P1 to a transient table.
** The cursor is always opened read/write even if
@@ -65926,13 +72363,13 @@ case OP_OpenWrite: {
** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure
** that defines the format of keys in the index.
**
-** This opcode was once called OpenTemp. But that created
-** confusion because the term "temp table", might refer either
-** to a TEMP table at the SQL level, or to a table opened by
-** this opcode. Then this opcode was call OpenVirtual. But
-** that created confusion with the whole virtual-table idea.
+** The P5 parameter can be a mask of the BTREE_* flags defined
+** in btree.h. These flags control aspects of the operation of
+** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
+** added automatically.
*/
/* Opcode: OpenAutoindex P1 P2 * P4 *
+** Synopsis: nColumn=P2
**
** This opcode works the same as OP_OpenEphemeral. It has a
** different name to distinguish its use. Tables created using
@@ -65941,24 +72378,25 @@ case OP_OpenWrite: {
*/
case OP_OpenAutoindex:
case OP_OpenEphemeral: {
-#if 0 /* local variables moved into u.ax */
VdbeCursor *pCx;
-#endif /* local variables moved into u.ax */
- static const int vfsFlags =
+ KeyInfo *pKeyInfo;
+
+ static const int vfsFlags =
SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE |
SQLITE_OPEN_DELETEONCLOSE |
SQLITE_OPEN_TRANSIENT_DB;
-
assert( pOp->p1>=0 );
- u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
- if( u.ax.pCx==0 ) goto no_mem;
- u.ax.pCx->nullRow = 1;
- rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.ax.pCx->pBt,
+ assert( pOp->p2>=0 );
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
+ if( pCx==0 ) goto no_mem;
+ pCx->nullRow = 1;
+ pCx->isEphemeral = 1;
+ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1);
+ rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
}
if( rc==SQLITE_OK ){
/* If a transient index is required, create it by calling
@@ -65966,32 +72404,74 @@ case OP_OpenEphemeral: {
** opening it. If a transient table is required, just use the
** automatically created table with root-page 1 (an BLOB_INTKEY table).
*/
- if( pOp->p4.pKeyInfo ){
+ if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
int pgno;