├── .gitignore
├── LICENSE
├── README.md
├── bookmanagement
├── README.md
└── jpg
│ ├── 1.png
│ ├── 10.jpg
│ ├── 11.jpg
│ ├── 12.jpg
│ ├── 13.jpg
│ ├── 14.jpg
│ ├── 15.jpg
│ ├── 16.jpg
│ ├── 17.jpg
│ ├── 18.png
│ ├── 19.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.jpg
│ ├── 6.jpg
│ ├── 7.png
│ ├── 8.jpg
│ ├── 9.jpg
│ ├── IMG_9281.jpg
│ ├── IMG_9359.jpg
│ ├── IMG_9360.jpg
│ ├── IMG_9362.jpg
│ ├── IMG_9363.jpg
│ ├── IMG_9365.jpg
│ └── IMG_9366.jpg
└── miniSQL
├── README.md
├── jpg
├── 1 2.jpg
├── 1.jpg
├── 1.png
├── 10.png
├── 11.png
├── 12.png
├── 13.png
├── 14.png
├── 15.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
├── 8.png
└── 9.png
└── miniSQL
├── APIManager
└── api.py
├── CatalogManager
└── catalog.py
├── IndexManager
└── index.py
├── MiniSQL.py
└── dbfiles
├── catalog_files
├── indexs_catalog.msql
└── tables_catalog.msql
└── index_files
└── tables_B-plus_tree.msql
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 | .DS_Store
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # pyenv
77 | .python-version
78 |
79 | # celery beat schedule file
80 | celerybeat-schedule
81 |
82 | # SageMath parsed files
83 | *.sage.py
84 |
85 | # Environments
86 | .env
87 | .venv
88 | env/
89 | venv/
90 | ENV/
91 | env.bak/
92 | venv.bak/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MiniSQL
2 | MiniSQL数据库以及图书管理系统,基于python和UWP设计。
3 |
4 | MiniSQL:https://github.com/AlanShaw-GitHub/MiniSQL/tree/master/miniSQL
5 |
6 | 图书管理系统:https://github.com/AlanShaw-GitHub/MiniSQL/tree/master/bookmanagement/
7 |
--------------------------------------------------------------------------------
/bookmanagement/README.md:
--------------------------------------------------------------------------------
1 | # 数据库程序设计
2 |
3 | | 姓名 | 学号 | 指导老师 | 日期 |
4 | | ------ | ---- | -------- | --------- |
5 | | 肖振新 | * | 高云君 | 2018.6.10 |
6 |
7 |
8 | ## 一、实验目的
9 |
10 | 1、 设计并实现一个精简的图书管理系统,具有入库、查询、借书、还书、借书证管理 等基本功能。
11 |
12 | 2、 通过本次设计来加深对数据库的了解和使用,同时提高自身的系统编程能力。
13 |
14 | ## 二、实验平台开发工具:
15 |
16 | MySQL、Visual Studio 2017 、UWP、Pyhton、C#、Xaml、telerik UI for ASP.NET
17 |
18 | Jetbrains DataGrip、Debian Linux OS 9(MySQL server)、MySQL Plugin For .NET framework
19 |
20 | ## 三、总体设计
21 |
22 | ### 系统架构概述
23 |
24 | 本程序是在Windows平台下开发的简易图书管理系统,我也不太擅长花很多时间在写长篇大论的报告上,所以本报告也会写的比较简洁,主要以贴图为主。。
25 |
26 |
27 |
28 | 前端采用Windows UWP开发包进行设计,主窗口采用了Windows 2017 fall creator update(Windows秋季创意者更新)中新引入的navigation bar控件进行导航,这也是Windows内置应用(如设置)和众多Windows应用商店所采用的窗口导航方案,总体背景使用了transparent的透明亚克力,使得程序使用更加流畅。安装卸载都如同Windows应用商店的程序那样便捷。数据表的展示使用了telerik UI for ASP.NET,数据展示十分自然完整。
29 |
30 | 本程序使用了大量的异步编程方案,使得在执行存取数据等与后端数据库交互的耗时操作时程序不至于无响应。
31 |
32 | 本程序后端提供了以下三种解决方案:(值得一提的是,不管使用哪一种方案,用户的操作逻辑是完全相同的,只需要在第一次打开本程序时在设置中初始化即可,之后配置就会被保存在Windows系统管理的缓存中,这也符合了前后端逻辑分离的原则)
33 |
34 | 1. 使用本地的MySQL数据库服务器,这需要在本地电脑安装MySQL服务器。
35 | 2. 使用云端的测试服务器,此服务器是作者自行租用的服务器,为了让没有下载MySQL数据库的用户也可以使用,缺点是延迟较高。
36 | 3. 使用MiniSQL数据库引擎,这是为期末大程设计的数据库,正好集成到了此图书管理系统应用中,作为数据库后端引擎,是本程序的一大亮点。该数据库引擎随程序一起打包发布,无需另外下载。
37 |
38 | ### 数据库表设计
39 |
40 | - 图书信息表(books)
41 |
42 | | 字段名 | 类型 | 含义 |
43 | | --------- | -------- | ------------------ |
44 | | BookNo | char(20) | 图书编号,**主码** |
45 | | BookName | char(20) | 图书名 |
46 | | Publisher | char(20) | 出版商 |
47 | | Date | char(20) | 入库时间 |
48 | | Author | char(20) | 作者 |
49 | | Price | char(20) | 价格 |
50 | | Storage | char(20) | 库存量 |
51 |
52 | - 图书借阅信息表(records)
53 |
54 | | 字段名 | 类型 | 含义 |
55 | | ---------------------- | -------- | ------------------------------- |
56 | | UserID | char(20) | 用户名,**来自user表的外键** |
57 | | BookNo | char(20) | 图书编号,**来自books表的外键** |
58 | | LentDate | char(20) | 出借日期 |
59 | | ReturnDate | char(20) | 归还日期 |
60 | | Returned | char(20) | 是否归还 |
61 | | UserID,BookNo,LentDate | - | 主码 |
62 |
63 | - 用户信息表(users)
64 |
65 | | 字段名 | 类型 | 含义 |
66 | | -------- | -------- | ---------------- |
67 | | UserID | char(20) | 用户ID,**主码** |
68 | | Password | char(20) | 密码 |
69 | | Name | char(20) | 用户姓名 |
70 | | Contact | char(20) | 联系方式 |
71 |
72 | ### 所用开发技术
73 |
74 | 本程序的亮点是uwp窗口的设计和多种多样的后端连接方式,本作者的学习路线也是十分的陡峭,从毫无Windows开发经验到两天入门c#、xaml、uwp和win32 api那一套,然后动手写该程序。由于c#远不是我最为拿手的语言,写起来也很不顺手。之前在用傻瓜式的qt做图形界面开发时,并不需要考虑什么异步操作、多线程操作,但是因为图书管理系统在取数据的时候可能很慢(比如大规模的图书数据、或者连接远程服务器时的巨大延迟),为了不使程序在这个过程中无反应(没错就是大家经常看到的此程序“未响应”然后过一会又好了),就必须采用异步编程。最后整个程序使用起来也是十分的流畅。
75 |
76 | navigation bar和透明毛玻璃效果时也是微软对新的UI布局方案一次尝试,笔者注意到,在最新的WWDC 2018苹果全球开发者大会上,新版本的macOS mojave的一些内置程序如“新闻、应用商店”等也采用了类似于Windows uwp的窗口布局方案,这也与本程序的高度一致。如下图所示:
77 |
78 |
79 |
80 | 当然,作为一个数据库作业,最为重要的还是后端数据库逻辑的设计,本实验和数据库理论联系最为密切的应该就是于后端数据库的连接和数据表data schema的设计了。本程序连接后端或者云端的mysql数据库使用的是c#版本的mysql connector,由mysql官方发布,专门为Microsoft .NET framwork框架开发,相应的visual studio集成驱动的下载官网链接为https://dev.mysql.com/downloads/connector/net/,如下图所示:
81 |
82 |
83 |
84 | 数据表的内容在前面也已经列表说明了,主要就是三个数据表,books表存的就是图书的信息,包括图书编号、图书名等等关于本图书的各种信息,在用户登录的情况下可以进行借书操作。users表存的是用户的信息,包括用户账号密码、姓名和联系方式,user表在**管理员账号**下可以进行修改,然后新的用户可以登录借书。连接这两个表的就是借书记录了,借书记录主要包含了借的人的编号,图书编号和出借/归还日期,注意这里的主码是(图书编号+用户编号+出借日期),因为同一本书的库存可能不止一本,用户也可能借很多本这个书(尽管不大可能)。
85 |
86 |
87 |
88 | ### 代码一览
89 |
90 | 代码在这里贴个大概看一下,在报告中附上太多的代码影响美观,就不贴了。
91 |
92 | 前端代码使用xaml设计:
93 |
94 |
95 |
96 | 后端逻辑当然使用微软亲生的c#了:
97 |
98 |
99 |
100 | another:
101 |
102 |
103 |
104 | ## 四、详细设计
105 |
106 | ### 1· 主界面设计
107 |
108 | 主界面的数据表的展示使用了telerik UI for ASP.NET的一个控件,就是下图中的图书显示模块。该空间十分自然的以列表方式展示了数据。
109 |
110 | 右上角有两个按钮,刷新按钮在按下后会重新连接数据库并取回新的数据,用户登录按钮按下后会弹出新的窗口,并提示进行用户的登录。登录后可以进行借书操作。
111 |
112 |
113 |
114 | 点击右上角的用户登录,弹出新的窗口,如果输入账号或者密码错误,则会提示,登录不成功。如下图所示:
115 |
116 |
117 |
118 | ### 2· 图书借阅模块
119 |
120 | 在上方的搜索栏输入对应的信息进行搜索,下面会自动弹出满足条件的图书,选中、点击借阅即可。
121 |
122 |
123 |
124 | 如果没有登录,或者没有选中该图书、该书库存不足,则会报错,借阅不成功:
125 |
126 |
127 |
128 | 如果借阅成功,一条新的记录会被导入到record表中,**并且该书库存减1**.
129 |
130 |
131 |
132 | ### 3· 图书归还模块
133 |
134 | 好了,现在在上一步骤中借到书已经添加到records中了,我们到图书归还模块中去查看(这是归还完成时的界面):
135 |
136 |
137 |
138 | 我们可以点击归还该书:
139 |
140 |
141 |
142 | ### 4· 图书入库/添加用户模块
143 |
144 | 在这里,只有登录了管理员才可以进行操作,如果登录了管理员,**那么左上角会显示管理员字样**
145 |
146 |
147 |
148 | 如果没有登录管理员,则不具有管理员权限,那么在试图操作时会报错:
149 |
150 |
151 |
152 | 我们现在登录管理员账号,导入新图书:
153 |
154 |
155 |
156 | 然后顺便尝试一下添加新用户:
157 |
158 |
159 |
160 | ### 5· 用户管理模块
161 |
162 | 好了,在上一步骤中添加的新用户,可以在用户管理界面进行查看和删除了,同样,如果没有登录管理员,那么跳转到这个界面的时候什么也看不到~
163 |
164 |
165 |
166 | 然后我们选择一个用户,删除掉它:
167 |
168 |
169 |
170 | 这时候,我们可以看到,这个用户已经人间蒸发了:
171 |
172 |
173 |
174 | ### 6·管理员/用户登录模块
175 |
176 | 这里是管理员登录弹窗,注意的是和在主界面的用户登录界面基本相似,但是职能不同。只有在这里登录了,才可以进行上面的图书入库/用户添加删除操作。
177 |
178 |
179 |
180 |
181 |
182 | ### 7· 设置模块
183 |
184 | 设置模块在第一次启动本程序的时候会自动跳转到这里,并且如果没有在这里选择一个数据库源的话,即使点击左侧栏目的其他窗口,也会弹窗报错,提示无法连接数据库。在第一次打开本程序并选择一个方式初始化数据库后,该信息会保存在windows缓存中,接下来打开本程序都不需要再次设置,并且会自动跳转到首页作为默认的界面。
185 |
186 |
187 |
188 | 如果选择本地连接,且之前已经创建好了一个数据库,那么惦记连接到现有的数据库,否则,点击新建一个数据库:
189 |
190 |
191 |
192 | 如果希望连接到一个远程的数据库,那么点击下面的连接到云端数据库(注意:由于网络延迟,服务器在国外(你懂的),所以每次交互需要的时间可能较长,在我的电脑上实际测试,每个操作大概都有一秒左右的延迟):
193 |
194 |
195 |
196 | 同理,也可以连接到程序自带的minisql数据库。
197 |
--------------------------------------------------------------------------------
/bookmanagement/jpg/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/1.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/10.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/11.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/12.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/13.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/14.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/15.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/16.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/17.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/18.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/19.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/2.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/3.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/4.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/5.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/6.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/7.png
--------------------------------------------------------------------------------
/bookmanagement/jpg/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/8.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/9.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9281.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9281.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9359.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9359.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9360.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9360.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9362.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9362.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9363.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9363.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9365.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9365.jpg
--------------------------------------------------------------------------------
/bookmanagement/jpg/IMG_9366.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/bookmanagement/jpg/IMG_9366.jpg
--------------------------------------------------------------------------------
/miniSQL/README.md:
--------------------------------------------------------------------------------
1 | # MiniSQL 设计报告
2 |
3 | | 作者 | 学号 | 课程 | 专业 |
4 | | ------ | ---- | ---------- | ------------------------------------------- |
5 | | 肖振新 | * | 数据库系统 | 竺可桢学院交叉创新平台(计算机+自动化控制) |
6 |
7 | ### 程序运行截图
8 |
9 |
10 |
11 | ## 目录:
12 |
13 | [TOC]
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ## 概论
28 |
29 | 本MiniSQL数据库是仿照MySQL数据库交互界面开发的简易数据库引擎,支持查找、插入、删除等数据库的基本操作。设计过程全部由一人完成,所以没有具体的分工。开发语言采用python 3.7,并进行打包成无需额外依赖的二进制可执行程序,在Windows、macOS和Linux平台下均可运行。
30 |
31 | 本项目的GitHub地址:https://github.com/AlanShaw-GitHub/MiniSQL
32 |
33 | 功能/亮点:
34 |
35 | 1. 程序添加了登录模块,分为root管理员和普通用户两种,root可以**添加删除用户**(通过root权限的sys(username,password)表)。
36 | 2. 程序交互界面高度贴近MySQL和大多数Linux终端交互逻辑,**按键盘的“上”、“下”键可以定位到上、下一条语句**,十分方便。
37 | 3. 程序使用“lasy”的修改方式,即为了减少频繁写入磁盘的用时,所有的更改都暂时保存在内存中,不会写到本地文件,在键入“quit”命令退出本程序时,所有的更改才会被写入文件。任何非正常的退出都会导致此次所有更改的丢失,如果希望立即写入文件,**请敲入“commit”命令**,则所有的更改都会被立即写入文件。
38 | 4. **B+树索引**,在查找时优先检测是否存在在主码上的条件查询,如果没有,再检测是否存在在索引上的条件查询,然后其他。所以大部分查询时间复杂度都在log级别。详细的设计将会在后面一一介绍。
39 | 5. 类型char(N)中的**N没有大小限制**,多大都可以。同样,一个表也可以定义**无数个**属性,没任何限制。
40 | 6. 支持从文件中读取批量命令并执行,命令之间用分号分隔,**支持注释**(以#开头的行会被当作注释而不会被执行)
41 | 7. 提供基本的帮助文档,键入help或者?即可以查看支持的命令,键入“help 【命令】”即可以查看对应命令的帮助文档。
42 | 8. buffer manager采用**嵌套式json文件**存储,b+树有多高就有几层json嵌套。方便而又高效地完整保存了整个B+树的结构,详细设计会在后面介绍。
43 | 9. 较为完善的错误提示机制,使用了大量的检测点以及完整的错误捕捉机制,程序因此不会因为各种异常而退出。大量常见的语法错误都会检测到并且在终端中输出提示,告知用户语法错误的地方。
44 | 10. 简洁而不简单:本程序总代码只有一千多行,但是涵盖了所有数据库所必需的组件,尽可能的精简代码和提高操作效率,是本程序在设计时的首要目标。**每个操作的用时也都会随着结果一起打印出来**,供用户分析。
45 |
46 | ## 第一章 MiniSQL总体框架
47 |
48 | ### 第1.1节 MiniSQL实现功能分析
49 |
50 | 1. 总功能:允许用户通过字符界面输入 SQL 语句实现表的建立/删除;索引的建立/删除以及表记录的插入/删除/查找;
51 | 2. 数据类型:支持三种基本数据类型:INT,CHAR(N),FLOAT,**其中 CHAR(N)中的 N 没有大小限制**,多大都行。
52 | 3. 表定义:一个表最多可以定义**无数个**属性,各属性可以指定是否为 UNIQUE;支持单属性的 主键定义;
53 | 4. 索引的建立和删除:对于表的主属性自动建立B+树索引,对于声明为 UNIQUE 的属性可以 通过 SQL 语句由用户指定建立/删除 B+树索引(因此,所有的 B+树索引都是单属性单值的);
54 | 5. 查找记录:可以通过指定用 AND 连接的多个条件进行查询,支持**等值/不等值**查询和区间查询;
55 | 6. 插入和删除记录:支持每次一条记录的插入操作;支持每次一条或多条记录的删除操作。
56 |
57 | ### 第1.2节 MiniSQL系统体系结构
58 |
59 | 系统体系结构如下图所示,和设计报告模版中的略有不同,我将record manager整合到了catalog和index manager中。
60 |
61 |
62 |
63 | ### 第1.3节 MiniSQL设计语言和运行环境
64 |
65 | 设计语言:python 3.7
66 |
67 | 开发环境:macOS 10.14 Jetbrains PyCharm 2018.1
68 |
69 | ## 第二章 MiniSQL各模块实现的功能/接口
70 |
71 | ### 第2.1节 概论
72 |
73 | ### 第2.2节 Interpreter 实现功能/接口
74 |
75 | ##### Interpreter 模块直接与用户交互,主要实现以下功能:
76 |
77 | 1. 登录模块的基本接口,如果是管理员账户(默认账号root密码123456)登录则具有管理员权限,可以添加删除用户(在 API 层完成)。
78 | 2. 程序流程控制,即“启动并初始化、接收命令、处理命令、显示命令结果、循环、退出”流程。
79 | 3. 接收并解释用户输入的命令,生成命令的内部数据结构表示,调用 API 层提供的函数执行并显示执行结果。(错误语法处理在 API 层实现,Interpreter 模块只负责捕捉错误并提示在窗口。
80 | 4. 帮助信息的实现也在这个部分完成,用户可以在终端键入“help 【命令名】”来查看对应的帮助文档。
81 | 5. 对从文件加载批量命令的支持,其实就是从文件中读取命令,然后调用API层的函数来一句句解析这些语句,以#开头的行会被当作注释而不会被执行。
82 |
83 | ##### Interpreter 模块的接口
84 |
85 | Interpreter 模块主要是调用其他模块的接口,自己没有提供接口给其他模块。
86 |
87 | ### 第2.3节 API 实现功能/接口
88 |
89 | API 模块是整个系统的核心,其主要功能为提供执行 SQL 语句的接口,供 Interpreter 层调用。该接口以 Interpreter 层解释生成的命令内部表示为输入,根据 Catalog Manager 提供的信息确定执行规则,并调用 Record Manager、Index Manager 和 Catalog Manager 提供的相应接口进行执行,最后返回执行结果给 Interpreter 模块。
90 |
91 | API 模块主要是在做语法解析工作,并且也会调用其他模块来进行错误处理。
92 |
93 | ##### 主要实现功能:
94 |
95 | 主要就是对insert、select、delete、create、drop五个指令进行语法解析和错误处理,并调用 Record Manager、Index Manager 和 Catalog Manager 提供的接口实现数据库的更改操作
96 |
97 | ##### 模块接口
98 |
99 | 提供以下五个函数,全部为顶层的Interpreter 模块服务,执行五种不同的操作:
100 |
101 | 1. select:顾名思义。
102 | 2. insert:顾名思义。
103 | 3. delete:顾名思义。
104 | 4. create:顾名思义。
105 | 5. drop:顾名思义。
106 |
107 |
108 |
109 | ### 第2.4节 Catalog Manager 实现功能/接口
110 |
111 | ##### 实现功能
112 |
113 | Catalog Manager 负责管理数据库的所有模式信息,包括:
114 |
115 | 1. 数据库中所有表的定义信息,包括表的名称、表中字段(列)数、主键、定义在该 表上的索引。
116 | 2. 表中每个字段的定义信息,包括字段类型、是否唯一等。
117 | 3. 数据库中所有索引的定义,包括所属表、索引建立在那个字段上等。
118 |
119 | Catalog Manager 还必需提供访问及操作上述信息的接口,供 Interpreter 和 API 模块使用。
120 |
121 | ##### 模块接口
122 |
123 | 1. create_table:创建表的定义。
124 |
125 | 2. check_types_of_table:被Interpreter 模块调用,如果不满足则抛出一个异常。在insert一个记录时,该函数帮助检查values的值是否满足要求,比如values的数量和列数量是否相同、unique的值是否有重复(调用record manager检测)、char的宽度是否满足条件(小于N)。
126 |
127 | 3. exists_table:如果table已经存在,就产生一个异常,被Interpreter 模块在创建一个表的时候预先调用做检查。
128 |
129 | 4. not_exists_table:如果一个表不存在,就产生一个异常,被Interpreter 模块在删除一个表的时候预先调用做检查。
130 |
131 | 5. not_exists_index:如上同理。
132 |
133 | 6. exists_index:如上同理。
134 |
135 | 7. drop_table:删除一个表。
136 |
137 | 8. drop_index:删除一个索引。
138 |
139 | 9. create_index:创建一个索引。
140 |
141 | 10. check_select_statement:检查选择语句的正确性,被Interpreter 模块调用,如果不满足则抛出一个异常。在Interpreter 模块调用select相关操作之前做错误检查,主要检查选择的columns是否在表中有这些列,where语句提到的这些列是否存在、类型是否满足等等。
142 |
143 |
144 | ### 第2.5节 Record/Index Manager 实现功能/接口
145 |
146 | ##### 实现功能
147 |
148 | Index Manager负责 B+树索引的实现,实现B+树的创建和删除(由索引的定义与删除引起)、等值查找、插入键值、删除键值等操作,并对外提供相应的接口。 B+树中节点大小应与缓冲区的块大小相同,B+树的叉数由节点大小与索引键大小计算得到。
149 |
150 | ##### 模块接口
151 |
152 | 1. insert_into_table:将一个列插入b+树。
153 |
154 | 2. delete_from_table:将一个或多个列从b+树中删除,根据where语句。
155 |
156 | 3. select_from_table:从表中选择,并负责格式化输出,根据where语句。
157 |
158 | 4. create_table:创建一个空的b+树。
159 |
160 | 5. delete_table:删除一个表。
161 |
162 | 6. create_index:创建一个索引。
163 |
164 | 7. check_unique:检查该列的某个值是否已经在b+树中,是为Catalog Manager模块的check_types_of_table提供错误检查。
165 |
166 | 8. exist_user:检查sys系统表中是否存在该用户和正确的密码,为顶层interpreter模块服务,做用户登录模块。
167 |
168 |
169 |
170 | ### 第2.6节 Buffer/DB Files Manager 实现功能/接口
171 |
172 | 事实上,在设计时,buffer manager的设计内容都是集成在record和catalog代码中的,record和catalog代码直接实现了 __store__ 和 __load__ 函数,进行数据与磁盘之间的交互。
173 |
174 | ##### 实现功能
175 |
176 | Buffer Manager 负责缓冲区的管理,主要功能有:
177 |
178 | 1. 根据需要,读取指定的数据到系统缓冲区或将缓冲区中的数据写出到文件
179 | 2. 实现缓冲区的替换算法,当缓冲区满时选择合适的页进行替换
180 | 3. 记录缓冲区中各页的状态,如是否被修改过等
181 | 4. 提供缓冲区页的 pin 功能,及锁定缓冲区的页,不允许替换出去为提高磁盘 I/O 操作的效率,缓冲区与文件系统交互的单位是块,块的大小应为文件系统与磁盘交互单位的整数倍,一般可定为 4KB 或 8KB。
182 |
183 | DB Files 指构成数据库的所有数据文件,主要由记录数据文件、索引数据文件和Catalog 数据文件组成。同时还有写回文件和读取文件的功能 。
184 |
185 | ##### 模块接口
186 |
187 | ###### catalog部分存取
188 |
189 | catalog存取表、索引的信息,比如表名、列名、是否unique、主码等等信息,这部分的信息比较容易存取,提供以下函数:
190 |
191 | 1. __store__:存catalog信息。
192 | 2. __load__:从文件中取catalog信息。
193 |
194 | ###### index部分存取
195 |
196 | index存取b+树结构,这部分是比较难存取的,主要是树结构的复杂性和各种指针的问题,使得存取树时要格外小心。
197 |
198 | 1. __store__:存b+树信息。
199 |
200 | 2. __load__:取b+树信息。
201 |
202 |
203 |
204 | ## 第三章 MiniSQL各模块设计详解
205 |
206 | ### 第3.1节 概论
207 |
208 | ### 第3.2节 Interpreter设计详解
209 |
210 | 这个模块主要是调用了python的cmd模块,实现不间断的轮询用户输入并调用底层api进行处理。同时,还提供了用户登录模块、从文件中执行等模块。
211 |
212 | 加载和保存,其实调用底层api,传入当前路径。自己什么也没干:
213 |
214 | ```python
215 | def __initialize__():
216 | CatalogManager.catalog.__initialize__(os.getcwd())
217 | IndexManager.index.__initialize__(os.getcwd())
218 |
219 | def __finalize__():
220 | CatalogManager.catalog.__finalize__()
221 | IndexManager.index.__finalize__()
222 | ```
223 |
224 | 从文件中执行,代码如下,先打开文件,然后用分号分隔命令,去掉以#开头的注释字段,然后一个个调用底层函数进行处理,最后提交修改到底层文件。:
225 |
226 | ```python
227 | def exec_from_file(filename):
228 | f = open(filename)
229 | text = f.read()
230 | f.close()
231 | comands = text.split(';')
232 | comands = [i.strip().replace('\n','') for i in comands]
233 | __initialize__()
234 | for comand in comands:
235 | if comand == '':
236 | continue
237 | if comand[0] == '#':
238 | continue
239 | if comand.split(' ')[0] == 'insert':
240 | try:
241 | APIManager.api.insert(comand[6:])
242 | except Exception as e:
243 | print(str(e))
244 | elif comand.split(' ')[0] == 'select':
245 | try:
246 | APIManager.api.select(comand[6:])
247 | except Exception as e:
248 | print(str(e))
249 | elif comand.split(' ')[0] == 'delete':
250 | try:
251 | APIManager.api.delete(comand[6:])
252 | except Exception as e:
253 | print(str(e))
254 | elif comand.split(' ')[0] == 'drop':
255 | try:
256 | APIManager.api.drop(comand[4:])
257 | except Exception as e:
258 | print(str(e))
259 | elif comand.split(' ')[0] == 'create':
260 | try:
261 | APIManager.api.create(comand[6:])
262 | except Exception as e:
263 | print(str(e))
264 | __finalize__()
265 | ```
266 |
267 | 用户登录模块,除了管理员账号,其他的账号全部都从sys表中读取确认,使用底层的IndexManager.index.exist_user函数,并设置APIManager.api.__root标记表明当前是什么状态:
268 |
269 | ```python
270 | if len(sys.argv) < 5:
271 | print('ERROR : Unsupported syntax, please login.\n',errortext)
272 | sys.exit()
273 | if sys.argv[1] != '-u' or sys.argv[3] != '-p':
274 | print('ERROR : Unsupported syntax, please login.\n',errortext)
275 | sys.exit()
276 | __initialize__()
277 | if sys.argv[2] == 'root' and sys.argv[4] == '123456':
278 | APIManager.api.__root = True
279 | elif IndexManager.index.exist_user(username=sys.argv[2],password=sys.argv[4]):
280 | APIManager.api.__root = False
281 | else:
282 | print('Error : username or password is not correct,please '
283 | 'check and login again.\n',errortext)
284 | sys.exit()
285 | if len(sys.argv) > 5:
286 | if sys.argv[5] != '-execfile':
287 | print('ERROR : Unsupported syntax.\n',errortext)
288 | exec_from_file(sys.argv[6])
289 | sys.exit()
290 | ```
291 |
292 | 继承自cmd模块的类定义,删掉了很多代码,用(...somecode)代替:
293 |
294 | ```python
295 | class miniSQL(cmd.Cmd):
296 | intro = 'Welcome to the MiniSQL database server.\nType help or ? to list commands.\n'
297 | def do_select(self,args):
298 | try:
299 | APIManager.api.select(args.replace(';',''))
300 | except Exception as e:
301 | print(str(e))
302 | ...somecode
303 |
304 | def do_commit(self,args):
305 | time_start = time.time()
306 | __finalize__()
307 | time_end = time.time()
308 | print('Modifications has been commited to local files,',end='')
309 | print(" time elapsed : %fs." % (time_end - time_start))
310 |
311 | def do_quit(self,args):
312 | __finalize__()
313 | print('Goodbye.')
314 | sys.exit()
315 |
316 | def emptyline(self):
317 | pass
318 |
319 | def default(self, line):
320 | print('Unrecognized command.\nNo such symbol : %s' % line)
321 |
322 | def help_commit(self):
323 | print()
324 | text = "To reduce file transfer's time, this SQL server is designed to "+\
325 | "'lasy' write changes to local files, which means it will not store changes "+\
326 | "until you type 'quit' to normally exit the server. if this server exit "+\
327 | "unnormally, all changes will be lost. If you want to write changes to "+\
328 | "local files immediately, please use 'commit' command.\n"
329 | print(text)
330 |
331 | def help_quit(self):
332 | print()
333 | print('Quit the program and write changes to local file.')
334 |
335 | def help_select(self):
336 | print()
337 | print("select * from student;")
338 | print("select num from student where num >= 2 and num < 10 and gender = 'male';")
339 |
340 | ...somecode
341 | ```
342 |
343 |
344 |
345 | ### 第3.3节 API设计详解
346 |
347 | api模块主要是实现了五个函数(select、insert等),调用catalog和index的函数操作,并自己做一些语法上的错误检查,从代码中也可以看到,对于很多的格式错误,我都提供了监测点并抛出错误给顶层模块,抛出错误时,也会在最前面提示是哪个模块抛出的错误。由于篇幅有限,下面只展示两个函数的实现:
348 |
349 | ```python
350 | def insert(args):
351 | time_start = time.time()
352 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
353 | lists = args.split(' ')
354 | if lists[0] != 'into':
355 | raise Exception("API Module : Unrecoginze symbol for command 'insert',it should be 'into'.")
356 | table = lists[1]
357 | if table == 'sys' and __root == False:
358 | raise Exception("ERROR : Can't modify 'sys' table, you are not root.")
359 | if lists[2] != 'values':
360 | raise Exception("API Module : Unrecoginze symbol for command 'insert',it should be 'values'.")
361 | value = args[re.search('\(',args).start()+1:find_last(args,')')]
362 | values = value.split(',')
363 | CatalogManager.catalog.not_exists_table(table)
364 | CatalogManager.catalog.check_types_of_table(table,values)
365 | IndexManager.index.insert_into_table(table,values)
366 | time_end = time.time()
367 | print(" time elapsed : %fs." % (time_end-time_start))
368 |
369 | def delete(args):
370 | time_start = time.time()
371 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
372 | lists = args.split(' ')
373 | if lists[0] != 'from':
374 | raise Exception("API Module : Unrecoginze symbol for command 'delete',it should be 'from'.")
375 | table = lists[1]
376 | if table == 'sys' and __root == False:
377 | raise Exception("ERROR : Can't modify 'sys' table, you are not root.")
378 | CatalogManager.catalog.not_exists_table(table)
379 | if len(lists) == 2:
380 | IndexManager.index.delete_from_table(table,[])
381 | else:
382 | IndexManager.index.delete_from_table(table,lists[3:])
383 | time_end = time.time()
384 | print(" time elapsed : %fs." % (time_end-time_start))
385 | ```
386 |
387 |
388 |
389 | ### 第3.4节 Catalog Manager设计详解
390 |
391 | catalog模块存取表的信息,定义的数据结构为:
392 |
393 | ```python
394 | tables = {}
395 | class table_instance():
396 | def __init__(self,table_name,primary_key = 0):
397 | self.table_name = table_name
398 | self.primary_key = primary_key
399 | columns =[]
400 |
401 | class column():
402 | def __init__(self,column_name,is_unique,type = 'char',length = 16):
403 | self.column_name = column_name
404 | self.is_unique = is_unique
405 | self.type = type
406 | self.length = length
407 | ```
408 |
409 | tables是所有的表的集合,是一个字典,key是表名,value是一个指向class table_instance的指针,代表了一个表实例对象。一个表中存了表名、主键的名字,以及一个列的集合。列又是个class column实例对象,他存了列名、是否unique、类型是什么以及如果是char类型的话,长度是多少。
410 |
411 | 下面简单给出一个表的创建对应的函数,其他琐碎的函数就不给出了。
412 |
413 | ```python
414 | def create_table(table,statement):
415 | global tables
416 | primary_place = re.search('primary key *\(',statement).end()
417 | primary_place_end = re.search('\)',statement[primary_place:]).start()
418 | primary_key = statement[primary_place:][:primary_place_end].strip()
419 | cur_table = table_instance(table,primary_key)
420 | lists = statement.split(',')
421 | columns = []
422 | for cur_column_statement in lists[0:len(lists)-1]:
423 | cur_column_statement = cur_column_statement.strip()
424 | cur_lists = cur_column_statement.split(' ')
425 | is_unique = False
426 | type = 'char'
427 | column_name = cur_lists[0]
428 | if re.search('unique',concat_list(cur_lists[1:])) or column_name == primary_key:
429 | is_unique = True
430 | if re.search('char',concat_list(cur_lists[1:])):
431 | length_start = re.search('\(',concat_list(cur_lists[1:])).start()+1
432 | length_end = re.search('\)', concat_list(cur_lists[1:])).start()
433 | length = int(concat_list(cur_lists[1:])[length_start:length_end])
434 |
435 | elif re.search('int', concat_list(cur_lists[1:])):
436 | length = 0
437 | type = 'int'
438 | elif re.search('float', concat_list(cur_lists[1:])):
439 | length = 0
440 | type = 'float'
441 | else:
442 | raise Exception("Catalog Module : Unsupported type for %d." % column_name)
443 | columns.append(column(column_name,is_unique,type,length))
444 | cur_table.columns = columns
445 | seed = False
446 | for index,__column in enumerate(cur_table.columns):
447 | if __column.column_name == cur_table.primary_key:
448 | cur_table.primary_key = index
449 | seed = True
450 | break
451 | if seed == False:
452 | raise Exception("Catalog Module : primary_key '%s' not exists."
453 | % cur_table.primary_key)
454 |
455 | tables[table] = cur_table
456 | ```
457 |
458 |
459 |
460 | ### 第3.5节 Record/Index Manager设计详解
461 |
462 | 这个模块是整个数据库的关键部分。这个模块主要实现了b+树的增删查找。这部分也是整个程序代码量最大的部分。
463 |
464 | 向b+树中插入的代码:(先调用了find_leaf_place找到要插入的叶节点位置,如果能插下去,那么就调用insert_into_leaf插入,完事。如果不能,就先调用insert_into_leaf插入,然后拆分,然后调用insert_into_parent递归的向父节点更新。
465 |
466 | ```python
467 | def insert_into_table(table,__values):
468 | for index,col in enumerate(CatalogManager.catalog.tables[table].columns):
469 | if col.type == 'int':
470 | __values[index] = int(__values[index])
471 | elif col.type == 'char':
472 | __values[index] = __values[index].strip().replace("'",'')
473 | elif col.type == 'float':
474 | __values[index] = float(__values[index])
475 |
476 | cur_node = tables[table]
477 | __primary_key = CatalogManager.catalog.tables[table].primary_key
478 | # __primary_key = 0
479 | if len(cur_node.keys) == 0:
480 | # new tree
481 | cur_node.keys.append(__values[__primary_key])
482 | cur_node.pointers.append(__values)
483 | cur_node.pointers.append('')
484 | print('Successfully insert into table %s,' % table,end='')
485 | return
486 |
487 | cur_node = find_leaf_place(table,__values[__primary_key])
488 | if len(cur_node.keys) < N - 1:
489 | insert_into_leaf(cur_node,__values[__primary_key],__values)
490 |
491 | else:
492 | insert_into_leaf(cur_node,__values[__primary_key],__values)
493 | new_node = node(True,[],[])
494 | tmp_keys = cur_node.keys
495 | tmp_pointers = cur_node.pointers
496 | cur_node.keys = []
497 | cur_node.pointers = []
498 | for i in range(math.ceil(N/2)):
499 | cur_node.keys.append(tmp_keys.pop(0))
500 | cur_node.pointers.append(tmp_pointers.pop(0))
501 | for i in range(N - math.ceil(N/2)):
502 | new_node.keys.append(tmp_keys.pop(0))
503 | new_node.pointers.append(tmp_pointers.pop(0))
504 | cur_node.pointers.append(new_node)
505 | new_node.pointers.append(tmp_pointers.pop(0))
506 | insert_into_parent(table,cur_node,new_node.keys[0],new_node)
507 |
508 | print('Successfully insert into table %s,' % table,end='')
509 | ```
510 |
511 | 删除的代码:(删除时先拆分用and连接的复杂的条件,这部分和select做的事情相似。然后再看看这些条件里面有没有主码的条件,如果有,那么查找就可以在log时间内完成了(都是调用find_leaf_place_with_condition完成)。然后对于满足的所有条件,调用check_conditions检查所有的条件是否满足,如果满足,就删掉他们,然后调用maintain_B_plus_tree_after_delete来保持删除后的b+树结构)
512 |
513 | ```python
514 | def delete_from_table(table,statement):
515 | # delete rows from table according to the statement's condition
516 | # usage : find_leaf_place_with_condition(table, column, value,condition)
517 | if len(statement) == 0:
518 | tables[table] = node(True,[],[],'')
519 | print("Successfully delete all entrys from table '%s'," % table,end='')
520 | else:
521 | columns = {}
522 | for index,col in enumerate(CatalogManager.catalog.tables[table].columns):
523 | columns[col.column_name] = index
524 | __primary_key = CatalogManager.catalog.tables[table].primary_key
525 | # __primary_key = 0
526 | # columns = {'num':0,'val':1}
527 |
528 | conditions = []
529 | tmp = []
530 | pos = 1
531 | for i in statement:
532 | if i == 'and':
533 | conditions.append(tmp)
534 | tmp = []
535 | pos = 1
536 | continue
537 | if pos == 1:
538 | tmp.append(columns[i])
539 | elif pos == 3:
540 | if CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'char':
541 | tmp.append(i.strip().replace("'", ''))
542 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'int':
543 | tmp.append(int(i))
544 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'float':
545 | tmp.append(float(i))
546 | else:
547 | tmp.append(i)
548 | pos = pos + 1
549 | conditions.append(tmp)
550 | times = 0
551 | while True:
552 | nodes = find_leaf_place_with_condition(table,
553 | conditions[0][0],conditions[0][2],conditions[0][1])
554 | for col in conditions:
555 | if col[0] == __primary_key:
556 | nodes = find_leaf_place_with_condition(table,col[0],col[2],col[1])
557 | break
558 |
559 | if len(nodes) == 0:
560 | break
561 | seed = False
562 | for __node in nodes:
563 | if seed == True:
564 | break
565 | for index,leaf in enumerate(__node.pointers[0:-1]):
566 | if check_conditions(leaf,conditions):
567 | __node.pointers.pop(index)
568 | __node.keys.pop(index)
569 | maintain_B_plus_tree_after_delete(table,__node)
570 | times = times + 1
571 | seed = True
572 | break
573 | if seed == False:
574 | break
575 | print("Successfully delete %d entry(s) from table '%s'," % (times,table),end='')
576 | ```
577 |
578 | 其中maintain_B_plus_tree_after_delete比较有意思,它是一个递归函数,会从叶节点开始递归的保持b+树的结构:
579 |
580 | ```python
581 | def maintain_B_plus_tree_after_delete(table,__node):
582 | global N
583 | if __node.parent == '' and len(__node.pointers) == 1:
584 | tables[table] = __node.pointers[0]
585 | elif ((len(__node.pointers) < math.ceil(N/2) and __node.is_leaf == False) or
586 | (len(__node.keys) < math.ceil((N-1)/2) and __node.is_leaf == True) ) \
587 | and __node.parent != '':
588 | previous = False
589 | other_node = node(True,[],[])
590 | K = ''
591 | __index = 0
592 | for index, i in enumerate(__node.parent.pointers):
593 | if i == __node:
594 | if index == len(__node.parent.pointers) - 1:
595 | other_node = __node.parent.pointers[-2]
596 | previous = True
597 | K = __node.parent.keys[index - 1]
598 | else:
599 | K = __node.parent.keys[index]
600 | other_node = __node.parent.pointers[index + 1]
601 | __index = index + 1
602 |
603 | if (other_node.is_leaf == True and len(other_node.keys)+len(__node.keys) < N) or \
604 | (other_node.is_leaf == False and len(other_node.pointers) +
605 | len(__node.pointers) <= N):
606 | if previous == True:
607 | if other_node.is_leaf == False:
608 | other_node.pointers = other_node.pointers + __node.pointers
609 | other_node.keys = other_node.keys + [K] + __node.keys
610 | for __node__ in __node.pointers:
611 | __node__.parent = other_node
612 | else:
613 | other_node.pointers = other_node.pointers[0:-1]
614 | other_node.pointers = other_node.pointers + __node.pointers
615 | other_node.keys = other_node.keys + __node.keys
616 | __node.parent.pointers = __node.parent.pointers[0:-1]
617 | __node.parent.keys = __node.parent.keys[0:-1]
618 | maintain_B_plus_tree_after_delete(table,__node.parent)
619 | else:
620 | if other_node.is_leaf == False:
621 | __node.pointers = __node.pointers + other_node.pointers
622 | __node.keys = __node.keys + [K] + other_node.keys
623 | for __node__ in other_node.pointers:
624 | __node__.parent = __node
625 | else:
626 | __node.pointers = __node.pointers[0:-1]
627 | __node.pointers = __node.pointers + other_node.pointers
628 | __node.keys = __node.keys + other_node.keys
629 | __node.parent.pointers.pop(__index)
630 | __node.parent.keys.pop(__index-1)
631 | maintain_B_plus_tree_after_delete(table,__node.parent)
632 | else:
633 | if previous == True:
634 | if other_node.is_leaf == True:
635 | __node.keys.insert(0,other_node.keys.pop(-1))
636 | __node.pointers.insert(0,other_node.pointers.pop(-2))
637 | __node.parent.keys[-1] = __node.keys[0]
638 | else:
639 | __tmp = other_node.pointers.pop(-1)
640 | __tmp.parent = __node
641 | __node.pointers.insert(0,__tmp)
642 | __node.keys.insert(0,__node.parent.keys[-1])
643 | __node.parent.keys[-1] = other_node.keys.pop(-1)
644 | else:
645 | if other_node.is_leaf == True:
646 | __node.keys.insert(-1,other_node.keys.pop(0))
647 | __node.pointers.insert(-2,other_node.pointers.pop(0))
648 | __node.parent.keys[__index-1] = other_node.keys[0]
649 | else:
650 | __tmp = other_node.pointers.pop(0)
651 | __tmp.parent = __node
652 | __node.pointers.insert(-1,__tmp)
653 | __node.keys.insert(-1,__node.parent.keys[__index-1])
654 | __node.parent.keys[__index-1] = other_node.keys.pop(0)
655 |
656 | ```
657 |
658 | 然后就是select函数了,这部分的代码比较多,主要是要先看一下查询条件where子句有没有,如果没有就说默认返回所有的记录。如果有的话,那么就像上面的delete做的那样对where子句进行解析、查找(当然也要看一下有没有主码属性,有的话就优先查找主码属性),然后把这些记录保存在缓冲区,然后再看一要输出哪些column,如果是*就是默认输出所有的column,如果出现不存在的column会报错。然后代码的最后是优雅地格式化输出所有的列。
659 |
660 | ```python
661 | def select_from_table(table,__conditions = '',__columns = ''):
662 | results = []
663 | columns = {}
664 | for index,col in enumerate(CatalogManager.catalog.tables[table].columns):
665 | columns[col.column_name] = index
666 | __primary_key = CatalogManager.catalog.tables[table].primary_key
667 | if len(tables[table].keys) == 0:
668 | pass
669 | else:
670 | if __conditions != '':
671 | conditions = []
672 | statement = __conditions.split(' ')
673 | tmp = []
674 | pos = 1
675 | for i in statement:
676 | if i == 'and':
677 | conditions.append(tmp)
678 | tmp = []
679 | pos = 1
680 | continue
681 | if pos == 1:
682 | tmp.append(columns[i])
683 | elif pos == 3:
684 | if CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'char':
685 | tmp.append(i.strip().replace("'",''))
686 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'int':
687 | tmp.append(int(i))
688 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'float':
689 | tmp.append(float(i))
690 | else:
691 | tmp.append(i)
692 | pos = pos + 1
693 | conditions.append(tmp)
694 | nodes = find_leaf_place_with_condition(table,
695 | conditions[0][0], conditions[0][2], conditions[0][1])
696 | for col in conditions:
697 | if col[0] == __primary_key:
698 | nodes = find_leaf_place_with_condition(table, col[0], col[2], col[1])
699 | break
700 | for __node in nodes:
701 | for pointer in __node.pointers[0:-1]:
702 | if check_conditions(pointer,conditions):
703 | results.append(pointer)
704 | else:
705 | first_leaf_node = tables[table]
706 | while first_leaf_node.is_leaf != True:
707 | first_leaf_node = first_leaf_node.pointers[0]
708 | while True:
709 | for i in first_leaf_node.pointers[0:-1]:
710 | results.append(i)
711 | if first_leaf_node.pointers[-1] != '':
712 | first_leaf_node = first_leaf_node.pointers[-1]
713 | else:
714 | break
715 |
716 | if __columns == '*':
717 | __columns_list = list(columns.keys())
718 | __columns_list_num = list(columns.values())
719 | else:
720 | __columns_list_num = [columns[i.strip()] for i in __columns.split(',')]
721 | __columns_list = [i.strip() for i in __columns.split(',')]
722 |
723 | print('-' * (17 * len(__columns_list_num) + 1))
724 | for i in __columns_list:
725 | if len(str(i)) > 14:
726 | output = str(i)[0:14]
727 | else:
728 | output = str(i)
729 | print('|',output.center(15),end='')
730 | print('|')
731 | print('-' * (17 * len(__columns_list_num) + 1))
732 | for i in results:
733 | for j in __columns_list_num:
734 | if len(str(i[j])) > 14:
735 | output = str(i[j])[0:14]
736 | else:
737 | output = str(i[j])
738 | print('|',output.center(15) ,end='')
739 | print('|')
740 | print('-' * (17 * len(__columns_list_num) + 1))
741 | print("Returned %d entrys," % len(results),end='')
742 | ```
743 |
744 |
745 |
746 | ### 第3.6节 Buffer/DB Files Manager设计详解
747 |
748 | index模块的b+树格式的存取方法是本程序创新的一个地方,使用嵌套式json文件格式递归地存取b+树结构,**无损完美优雅**的保存了b+树的所有信息:
749 |
750 | ```python
751 | __last_leaf_pointer = ''
752 | def __load__():
753 | global __last_leaf_pointer
754 | f = open(os.path.join(path,'dbfiles/index_files/tables_B-plus_tree.msql'))
755 | json_tables = json.loads(f.read())
756 | f.close()
757 | for table in json_tables.items():
758 | if len(table[1]['keys']) == 0:
759 | tables[table[0]] = node(True,[],[])
760 | continue
761 | tables[table[0]] = node(table[1]['is_leaf'],table[1]['keys'],table[1]['pointers'],'')
762 | if not tables[table[0]].is_leaf:
763 | tables[table[0]].pointers = recursive_load_node(table[1]['pointers'],tables[table[0]])
764 |
765 | def recursive_load_node(pointer_list,parent):
766 | global __last_leaf_pointer
767 | lists = []
768 | for pointer in pointer_list:
769 | new_node = node(pointer['is_leaf'],pointer['keys'],pointer['pointers'],parent)
770 | lists.append(new_node)
771 | if not lists[-1].is_leaf:
772 | new_node.pointers = recursive_load_node(pointer['pointers'],lists[-1])
773 | else:
774 | if __last_leaf_pointer == '':
775 | __last_leaf_pointer = new_node
776 | else:
777 | __last_leaf_pointer.pointers.append(new_node)
778 | __last_leaf_pointer = new_node
779 | return lists
780 |
781 | def __store__():
782 | global path
783 | __tables = {}
784 | for table in tables.items():
785 | __tables[table[0]] = recursive_store_node(table[1])
786 | f = open(os.path.join(path,'dbfiles/index_files/tables_B-plus_tree.msql'),'w')
787 | json_tables = json.dumps(__tables)
788 | f.write(json_tables)
789 | f.close()
790 |
791 | def recursive_store_node(node):
792 | cur_node = {}
793 | cur_node['is_leaf'] = node.is_leaf
794 | cur_node['keys'] = node.keys
795 | if node.is_leaf == True and node.pointers[-1] != '':
796 | cur_node['pointers'] = node.pointers[0:-1]
797 | elif node.is_leaf == True and node.pointers[-1] == '':
798 | cur_node['pointers'] = node.pointers
799 | else:
800 | cur_node['pointers'] = []
801 | for __node in node.pointers:
802 | cur_node['pointers'].append(recursive_store_node(__node))
803 | return cur_node
804 | ```
805 |
806 | 文件最终存储效果,多层嵌套,**b+树有多高就有几层嵌套**:
807 |
808 | ```json
809 | {"sys": {"is_leaf": true, "keys": ["alan"], "pointers": [["alan", "123456"], ""]}, "student": {"is_leaf": false, "keys": [7], "pointers": [{"is_leaf": false, "keys": [3, 5], "pointers": [{"is_leaf": true, "keys": [1, 2], "pointers": [[1, "Alan", "male", "2017.9.1"], [2, "rose", "female", "2016.9.1"]]}, {"is_leaf": true, "keys": [3, 4], "pointers": [[3, "Robert", "male", "2016.9.1"], [4, "jack", "male", "2015.9.1"]]}, {"is_leaf": true, "keys": [5, 6], "pointers": [[5, "jason", "male", "2015.9.1"], [6, "Hans", "female", "2015.9.1"]]}]}, {"is_leaf": false, "keys": [9], "pointers": [{"is_leaf": true, "keys": [7, 8], "pointers": [[7, "rosa", "male", "2014.9.1"], [8, "messi", "female", "2013.9.1"]]}, {"is_leaf": true, "keys": [9, 10, 11], "pointers": [[9, "Neymar", "male", "2013.9.1"], [10, "Christ", "male", "2011.9.1"], [11, "shaw", "female", "2010.9.1"], ""]}]}]}}
810 | ```
811 |
812 | 至于catalog模块的表信息存取就比较容易了,简单操作,也是使用json格式存储,其中每个column有三个字段,分别为(是否unique、类型、如果是char类型那么最大宽度是多少):
813 |
814 | ```json
815 | {"sys": {"primary_key": 0, "columns": {"username": [true, "char", 16], "password": [false, "char", 16]}}, "student": {"primary_key": 0, "columns": {"ID": [true, "int", 0], "name": [false, "char", 10], "gender": [false, "char", 10], "enroll_date": [false, "char", 10]}}}
816 | ```
817 |
818 | 相应的代码:
819 |
820 | ```python
821 | def __load__():
822 | f = open(os.path.join(path,'dbfiles/catalog_files/tables_catalog.msql'))
823 | json_tables = json.loads(f.read())
824 | for table in json_tables.items():
825 | __table = table_instance(table[0],table[1]['primary_key'])
826 | columns = []
827 | for __column in table[1]['columns'].items():
828 | columns.append(column(__column[0],
829 | __column[1][0],__column[1][1],__column[1][2]))
830 | __table.columns = columns
831 | tables[table[0]] = __table
832 | f.close()
833 | f = open(os.path.join(path, 'dbfiles/catalog_files/indexs_catalog.msql'))
834 | json_indexs = f.read()
835 | json_indexs = json.loads(json_indexs)
836 | for index in json_indexs.items():
837 | indexs[index[0]] = index[1]
838 | f.close()
839 |
840 | def __store__():
841 | __tables = {}
842 | for items in tables.items():
843 | definition = {}
844 | definition['primary_key'] = items[1].primary_key
845 | __columns = {}
846 | for i in items[1].columns:
847 | __columns[i.column_name] = [i.is_unique,i.type,i.length]
848 | definition['columns'] = __columns
849 | __tables[items[0]] = definition
850 | json_tables = json.dumps(__tables)
851 | f = open(os.path.join(path,'dbfiles/catalog_files/tables_catalog.msql'),'w')
852 | f.write(json_tables)
853 | f.close()
854 | f = open(os.path.join(path, 'dbfiles/catalog_files/indexs_catalog.msql'), 'w')
855 | f.write(json.dumps(indexs))
856 | f.close()
857 | ```
858 |
859 |
860 |
861 | ## 第四章 MiniSQL系统测试
862 |
863 | 首先,尝试登录,故意输错格式或者账号密码,会提示错误是什么,并且提示正确的格式是什么样子的:
864 |
865 |
866 |
867 | 然后,根据提示的正确格式,我们可以进行正确的登录了,初始账号密码为root和123456(这是管理员账号),我们先从文件中读取批量执行指令,文件中的命令如下所示:
868 |
869 | ```mysql
870 | create table student (ID int, name char(10),gender char(10),enroll_date char(10),primary key(ID));
871 | insert into student values ( 00001,'Alan','male','2017.9.1');
872 | insert into student values ( 00002,'rose','female','2016.9.1');
873 | insert into student values ( 00003,'Robert','male','2016.9.1');
874 | insert into student values ( 00004,'jack','male','2015.9.1');
875 | insert into student values ( 00005,'jason','male','2015.9.1');
876 | insert into student values ( 00006,'Hans','female','2015.9.1');
877 | insert into student values ( 00007,'rosa','male','2014.9.1');
878 | insert into student values ( 00008,'messi','female','2013.9.1');
879 | insert into student values ( 00009,'Neymar','male','2013.9.1');
880 | insert into student values ( 00010,'Christ','male','2011.9.1');
881 | insert into student values ( 00011,'shaw','female','2010.9.1');
882 | ```
883 |
884 | 执行该文件的内容,输出如下所示:
885 |
886 |
887 |
888 | 好了,我们现在直接登录来操作,输入正确的账号密码,登录成功,提示一些信息:
889 |
890 |
891 |
892 | 我们来管理用户,管理方法是在用户表sys中插入删除用户,然后这些用户可以以普通模式登录数据库,这些用户不具有写(但有读)sys表的权限。一些操作如下图所示(注意,即使是管理员也不具有删除sys表的权限,因为这是系统必须表,不能被删除):
893 |
894 |
895 |
896 | 现在我们可以使用新添加的“shaw”用户进行登录了,当然,他没有写sys表的权限:
897 |
898 |
899 |
900 | 好了,用户管理部分就到这里,下面我们进行select操作,不带任何参数就是选择所有的,也进行复杂的select条件查询,连接多个and表达式(中途有个小插曲,输错了列的名字,可以看到程序提供的较为完整的错误检查也准确的定位到了错误并输出):
901 |
902 |
903 |
904 | 然后,我们进行delete操作,不提供where语句就是默认删除所有的entry:
905 |
906 |
907 |
908 | 当然,我们也可以进行复杂的where表达式来批量删除,下面把性别为female的都删除(注意,此处提供的where表达式解析和select的基本一致,同样支持复杂的and连接多个表达式):
909 |
910 |
911 |
912 | 创建一个新的表然后添加一些entry:
913 |
914 |
915 |
916 | 删除一个表,这时候再尝试使用该表就会报错,因为他已经不存在了,当然,这时候想要后悔可以强制退出程序,之前说过的,这样所有的更改不会被写入磁盘文件:
917 |
918 |
919 |
920 | 这时候,如果想要提交修改,使用commit命令,可以看到,用时是其他在内存上直接操作的10-100倍,已经达到了毫秒级,所以本程序使用lasy store来提高效率是十分有意义的:
921 |
922 |
923 |
924 | 创建索引也很简单,可以看到错误提示机制也很完善,输入格式错误是会提示的:
925 |
926 |
927 |
928 | 删除索引也很简单,如果尝试删除不存在的索引,也会报错。
929 |
930 |
931 |
932 | 最后,敲入quit退出程序,同样,修改会被写进内存:
933 |
934 |
935 |
936 |
--------------------------------------------------------------------------------
/miniSQL/jpg/1 2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/1 2.jpg
--------------------------------------------------------------------------------
/miniSQL/jpg/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/1.jpg
--------------------------------------------------------------------------------
/miniSQL/jpg/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/1.png
--------------------------------------------------------------------------------
/miniSQL/jpg/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/10.png
--------------------------------------------------------------------------------
/miniSQL/jpg/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/11.png
--------------------------------------------------------------------------------
/miniSQL/jpg/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/12.png
--------------------------------------------------------------------------------
/miniSQL/jpg/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/13.png
--------------------------------------------------------------------------------
/miniSQL/jpg/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/14.png
--------------------------------------------------------------------------------
/miniSQL/jpg/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/15.png
--------------------------------------------------------------------------------
/miniSQL/jpg/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/2.png
--------------------------------------------------------------------------------
/miniSQL/jpg/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/3.png
--------------------------------------------------------------------------------
/miniSQL/jpg/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/4.png
--------------------------------------------------------------------------------
/miniSQL/jpg/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/5.png
--------------------------------------------------------------------------------
/miniSQL/jpg/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/6.png
--------------------------------------------------------------------------------
/miniSQL/jpg/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/7.png
--------------------------------------------------------------------------------
/miniSQL/jpg/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/8.png
--------------------------------------------------------------------------------
/miniSQL/jpg/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlanShaw-GitHub/MiniSQL/101a660c38c7fe5c96b504b40de982cd27f9807c/miniSQL/jpg/9.png
--------------------------------------------------------------------------------
/miniSQL/miniSQL/APIManager/api.py:
--------------------------------------------------------------------------------
1 | import re
2 | import CatalogManager.catalog
3 | import IndexManager.index
4 | import time
5 |
6 | __root = True
7 | def select(args):
8 | time_start = time.time()
9 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
10 | lists = args.split(' ')
11 | start_from = re.search('from', args).start()
12 | end_from = re.search('from', args).end()
13 | columns = args[0:start_from].strip()
14 | if re.search('where', args):
15 | start_where = re.search('where', args).start()
16 | end_where = re.search('where', args).end()
17 | table = args[end_from+1:start_where].strip()
18 | conditions = args[end_where+1:].strip()
19 | else:
20 | table = args[end_from+1:].strip()
21 | conditions = ''
22 | CatalogManager.catalog.not_exists_table(table)
23 | CatalogManager.catalog.check_select_statement(table,conditions,columns)
24 | IndexManager.index.select_from_table(table,conditions,columns)
25 | time_end = time.time()
26 | print(" time elapsed : %fs." % (time_end-time_start))
27 |
28 |
29 | def create(args):
30 | time_start = time.time()
31 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
32 | lists = args.split(' ')
33 | if lists[0] == 'table':
34 | start_on = re.search('table', args).end()
35 | start = re.search('\(', args).start()
36 | end = find_last(args,')')
37 | table = args[start_on:start].strip()
38 | statement = args[start + 1:end].strip()
39 | CatalogManager.catalog.exists_table(table)
40 | IndexManager.index.create_table(table,statement)
41 | CatalogManager.catalog.create_table(table,statement)
42 |
43 | elif lists[0] == 'index':
44 | index_name = lists[1]
45 | if lists[2] != 'on':
46 | raise Exception("API Module : Unrecoginze symbol for command 'create index',it should be 'on'.")
47 | start_on = re.search('on',args).start()
48 | start = re.search('\(',args).start()
49 | end = find_last(args, ')')
50 | table = args[start_on:start].strip()
51 | column = args[start+1:end].strip()
52 | CatalogManager.catalog.exists_index(index_name)
53 | CatalogManager.catalog.create_index(index_name,table,column)
54 | IndexManager.index.create_index(index_name,table,column)
55 |
56 | else:
57 | raise Exception("API Module : Unrecoginze symbol for command 'create',it should be 'table' or 'index'.")
58 | time_end = time.time()
59 | print("Successfully create table '%s', time elapsed : %fs."
60 | % (table,time_end - time_start))
61 |
62 | def drop(args):
63 | time_start = time.time()
64 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
65 | if args[0:5] == 'table':
66 | table = args[6:].strip()
67 | if table == 'sys':
68 | raise Exception("ERROR : Can't delete 'sys' table, it is necessary for MiniSQL server to run.")
69 | CatalogManager.catalog.not_exists_table(table)
70 | CatalogManager.catalog.drop_table(table)
71 | IndexManager.index.delete_table(table)
72 | time_end = time.time()
73 | print("Successfully delete table '%s', time elapsed : %fs." % (table,time_end - time_start))
74 |
75 | elif args[0:5] == 'index':
76 | index = args[6:].strip()
77 | CatalogManager.catalog.not_exists_index(index)
78 | CatalogManager.catalog.drop_index(index)
79 |
80 | else:
81 | raise Exception("API Module : Unrecoginze symbol for command 'drop',it should be 'table' or 'index'.")
82 |
83 |
84 | def insert(args):
85 | time_start = time.time()
86 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
87 | lists = args.split(' ')
88 | if lists[0] != 'into':
89 | raise Exception("API Module : Unrecoginze symbol for command 'insert',it should be 'into'.")
90 | table = lists[1]
91 | if table == 'sys' and __root == False:
92 | raise Exception("ERROR : Can't modify 'sys' table, you are not root.")
93 | if lists[2] != 'values':
94 | raise Exception("API Module : Unrecoginze symbol for command 'insert',it should be 'values'.")
95 | value = args[re.search('\(',args).start()+1:find_last(args,')')]
96 | values = value.split(',')
97 | CatalogManager.catalog.not_exists_table(table)
98 | CatalogManager.catalog.check_types_of_table(table,values)
99 | IndexManager.index.insert_into_table(table,values)
100 | time_end = time.time()
101 | print(" time elapsed : %fs." % (time_end-time_start))
102 |
103 | def delete(args):
104 | time_start = time.time()
105 | args = re.sub(r' +', ' ', args).strip().replace('\u200b','')
106 | lists = args.split(' ')
107 | if lists[0] != 'from':
108 | raise Exception("API Module : Unrecoginze symbol for command 'delete',it should be 'from'.")
109 | table = lists[1]
110 | if table == 'sys' and __root == False:
111 | raise Exception("ERROR : Can't modify 'sys' table, you are not root.")
112 | CatalogManager.catalog.not_exists_table(table)
113 | if len(lists) == 2:
114 | IndexManager.index.delete_from_table(table,[])
115 | else:
116 | IndexManager.index.delete_from_table(table,lists[3:])
117 | time_end = time.time()
118 | print(" time elapsed : %fs." % (time_end-time_start))
119 |
120 | def find_last(string,str):
121 | last_position=-1
122 | while True:
123 | position=string.find(str,last_position+1)
124 | if position==-1:
125 | return last_position
126 | last_position=position
--------------------------------------------------------------------------------
/miniSQL/miniSQL/CatalogManager/catalog.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import re
4 | import IndexManager.index
5 |
6 | tables = {}
7 | path = ''
8 | indexs = {}
9 |
10 | class table_instance():
11 | def __init__(self,table_name,primary_key = 0):
12 | self.table_name = table_name
13 | self.primary_key = primary_key
14 |
15 | columns =[]
16 |
17 | class column():
18 | def __init__(self,column_name,is_unique,type = 'char',length = 16):
19 | self.column_name = column_name
20 | self.is_unique = is_unique
21 | self.type = type
22 | self.length = length
23 |
24 | def __initialize__(__path):
25 | global path
26 | path = __path
27 | if not os.path.exists(os.path.join(path,'dbfiles/catalog_files')):
28 | os.makedirs(os.path.join(path,'dbfiles/catalog_files'))
29 | f = open(os.path.join(path,'dbfiles/catalog_files/tables_catalog.msql'),'w')
30 | f.close()
31 | f = open(os.path.join(path,'dbfiles/catalog_files/indexs_catalog.msql'),'w')
32 | f.close()
33 | tables['sys'] = table_instance('sys',0)
34 | indexs['sys_default_index'] = {'table':'sys','column':'username'}
35 | columns = []
36 | columns.append(column('username',True))
37 | columns.append(column('password', False))
38 | tables['sys'].columns = columns
39 | __store__()
40 | __load__()
41 |
42 | def __finalize__():
43 | __store__()
44 |
45 | def __load__():
46 | f = open(os.path.join(path,'dbfiles/catalog_files/tables_catalog.msql'))
47 | json_tables = json.loads(f.read())
48 | for table in json_tables.items():
49 | __table = table_instance(table[0],table[1]['primary_key'])
50 | columns = []
51 | for __column in table[1]['columns'].items():
52 | columns.append(column(__column[0],
53 | __column[1][0],__column[1][1],__column[1][2]))
54 | __table.columns = columns
55 | tables[table[0]] = __table
56 | f.close()
57 | f = open(os.path.join(path, 'dbfiles/catalog_files/indexs_catalog.msql'))
58 | json_indexs = f.read()
59 | json_indexs = json.loads(json_indexs)
60 | for index in json_indexs.items():
61 | indexs[index[0]] = index[1]
62 | f.close()
63 |
64 | def __store__():
65 | __tables = {}
66 | for items in tables.items():
67 | definition = {}
68 | definition['primary_key'] = items[1].primary_key
69 | __columns = {}
70 | for i in items[1].columns:
71 | __columns[i.column_name] = [i.is_unique,i.type,i.length]
72 | definition['columns'] = __columns
73 | __tables[items[0]] = definition
74 | json_tables = json.dumps(__tables)
75 | f = open(os.path.join(path,'dbfiles/catalog_files/tables_catalog.msql'),'w')
76 | f.write(json_tables)
77 | f.close()
78 | f = open(os.path.join(path, 'dbfiles/catalog_files/indexs_catalog.msql'), 'w')
79 | f.write(json.dumps(indexs))
80 | f.close()
81 |
82 | def check_types_of_table(table,values):
83 | cur_table = tables[table]
84 | if len(cur_table.columns) != len(values):
85 | raise Exception("Catalog Module : table '%s' "
86 | "has %d columns." % (table,len(cur_table.columns)))
87 | for index,i in enumerate(cur_table.columns):
88 | if i.type == 'int':
89 | value = int(values[index])
90 | elif i.type == 'float':
91 | value = float(values[index])
92 | else:
93 | value = values[index]
94 | if len(value) > i.length:
95 | raise Exception("Catalog Module : table '%s' : column '%s' 's length"
96 | " can't be longer than %d." % (table, i.column_name,i.length))
97 |
98 | if i.is_unique:
99 | IndexManager.index.check_unique(table,index,value)
100 |
101 | def exists_table(table):
102 | for key in tables.keys():
103 | if key == table:
104 | raise Exception("Catalog Module : table already exists.")
105 |
106 | def not_exists_table(table):
107 | for key in tables.keys():
108 | if key == table:
109 | return
110 | raise Exception("Catalog Module : table not exists.")
111 |
112 | def not_exists_index(index):
113 | for key in indexs.keys():
114 | if key == index:
115 | return
116 | raise Exception("Catalog Module : index not exists.")
117 |
118 | def exists_index(index):
119 | for key in indexs.keys():
120 | if key == index:
121 | raise Exception("Catalog Module : index already exists.")
122 |
123 | def create_table(table,statement):
124 | global tables
125 | primary_place = re.search('primary key *\(',statement).end()
126 | primary_place_end = re.search('\)',statement[primary_place:]).start()
127 | primary_key = statement[primary_place:][:primary_place_end].strip()
128 | cur_table = table_instance(table,primary_key)
129 | lists = statement.split(',')
130 | columns = []
131 | for cur_column_statement in lists[0:len(lists)-1]:
132 | cur_column_statement = cur_column_statement.strip()
133 | cur_lists = cur_column_statement.split(' ')
134 | is_unique = False
135 | type = 'char'
136 | column_name = cur_lists[0]
137 | if re.search('unique',concat_list(cur_lists[1:])) or column_name == primary_key:
138 | is_unique = True
139 | if re.search('char',concat_list(cur_lists[1:])):
140 | length_start = re.search('\(',concat_list(cur_lists[1:])).start()+1
141 | length_end = re.search('\)', concat_list(cur_lists[1:])).start()
142 | length = int(concat_list(cur_lists[1:])[length_start:length_end])
143 |
144 | elif re.search('int', concat_list(cur_lists[1:])):
145 | length = 0
146 | type = 'int'
147 | elif re.search('float', concat_list(cur_lists[1:])):
148 | length = 0
149 | type = 'float'
150 | else:
151 | raise Exception("Catalog Module : Unsupported type for %d." % column_name)
152 | columns.append(column(column_name,is_unique,type,length))
153 | cur_table.columns = columns
154 | seed = False
155 | for index,__column in enumerate(cur_table.columns):
156 | if __column.column_name == cur_table.primary_key:
157 | cur_table.primary_key = index
158 | seed = True
159 | break
160 | if seed == False:
161 | raise Exception("Catalog Module : primary_key '%s' not exists."
162 | % cur_table.primary_key)
163 |
164 | tables[table] = cur_table
165 |
166 |
167 | def drop_table(table):
168 | tables.pop(table)
169 |
170 | def drop_index(index):
171 | indexs.pop(index)
172 | print("Successfully delete index '%s'." % index)
173 |
174 | def create_index(index_name,table,column):
175 | indexs[index_name] = {'table':table,'column':column}
176 |
177 | def check_select_statement(table,conditions,__columns):
178 | # raise an exception if something is wrong
179 | columns = []
180 | for i in tables[table].columns:
181 | columns.append(i.column_name)
182 | if conditions != '':
183 | conditions = re.sub('and|or',',',conditions)
184 | conditions_lists = conditions.split(',')
185 | for i in conditions_lists:
186 | if i.strip().split(' ')[0] not in columns:
187 | raise Exception("Catalog Module : no such column"
188 | " name '%s'." % i.strip().split(' ')[0])
189 | if __columns == '*':
190 | return
191 |
192 | __columns_list = __columns.split(',')
193 | for i in __columns_list:
194 | if i.strip() not in columns:
195 | raise Exception("Catalog Module : no such column name '%s'." % i.strip())
196 |
197 |
198 | def concat_list(lists):
199 | statement = ''
200 | for i in lists:
201 | statement = statement + i
202 | return statement
203 |
204 | if __name__ == '__main__':
205 | # new_table = table_instance('my_table','yes')
206 | # new_table.columns.append(column('yes',True))
207 | # new_table.columns.append(column('no',False))
208 | # tables['my_table'] = new_table
209 | __initialize__('/Users/alan/Desktop/CodingLife/Python/miniSQL')
210 | ##__store__()
211 |
212 |
--------------------------------------------------------------------------------
/miniSQL/miniSQL/IndexManager/index.py:
--------------------------------------------------------------------------------
1 | import CatalogManager.catalog
2 | import math
3 | import json
4 | import os
5 |
6 | N = 4
7 |
8 | tables = {}
9 | path = ''
10 |
11 | def __initialize__(__path):
12 | global path
13 | path = __path
14 | if not os.path.exists(os.path.join(path,'dbfiles/index_files')):
15 | os.makedirs(os.path.join(path,'dbfiles/index_files'))
16 | tables['sys'] = node(True, ['alan'], [['alan','123456'],''])
17 | __store__()
18 | __load__()
19 |
20 | def __finalize__():
21 | __store__()
22 |
23 | class node():
24 | def __init__(self,is_leaf,keys,pointers,parent = ''):
25 | self.is_leaf = is_leaf
26 | self.keys = keys
27 | self.pointers = pointers
28 | self.parent = parent
29 |
30 | __last_leaf_pointer = ''
31 | def __load__():
32 | global __last_leaf_pointer
33 | f = open(os.path.join(path,'dbfiles/index_files/tables_B-plus_tree.msql'))
34 | json_tables = json.loads(f.read())
35 | f.close()
36 | for table in json_tables.items():
37 | if len(table[1]['keys']) == 0:
38 | tables[table[0]] = node(True,[],[])
39 | continue
40 | tables[table[0]] = \
41 | node(table[1]['is_leaf'],table[1]['keys'],table[1]['pointers'],'')
42 | if not tables[table[0]].is_leaf:
43 | tables[table[0]].pointers = \
44 | recursive_load_node(table[1]['pointers'],tables[table[0]])
45 |
46 | def recursive_load_node(pointer_list,parent):
47 | global __last_leaf_pointer
48 | lists = []
49 | for pointer in pointer_list:
50 | new_node = node(pointer['is_leaf'],pointer['keys'],pointer['pointers'],parent)
51 | lists.append(new_node)
52 | if not lists[-1].is_leaf:
53 | new_node.pointers = recursive_load_node(pointer['pointers'],lists[-1])
54 | else:
55 | if __last_leaf_pointer == '':
56 | __last_leaf_pointer = new_node
57 | else:
58 | __last_leaf_pointer.pointers.append(new_node)
59 | __last_leaf_pointer = new_node
60 | return lists
61 |
62 | def __store__():
63 | global path
64 | __tables = {}
65 | for table in tables.items():
66 | __tables[table[0]] = recursive_store_node(table[1])
67 | f = open(os.path.join(path,'dbfiles/index_files/tables_B-plus_tree.msql'),'w')
68 | json_tables = json.dumps(__tables)
69 | f.write(json_tables)
70 | f.close()
71 |
72 | def recursive_store_node(node):
73 | cur_node = {}
74 | cur_node['is_leaf'] = node.is_leaf
75 | cur_node['keys'] = node.keys
76 | if node.is_leaf == True and node.pointers[-1] != '':
77 | cur_node['pointers'] = node.pointers[0:-1]
78 | elif node.is_leaf == True and node.pointers[-1] == '':
79 | cur_node['pointers'] = node.pointers
80 | else:
81 | cur_node['pointers'] = []
82 | for __node in node.pointers:
83 | cur_node['pointers'].append(recursive_store_node(__node))
84 | return cur_node
85 |
86 | def __prints(table):
87 | node = tables[table]
88 | __do_print(node)
89 |
90 | def __do_print(node):
91 | if node.is_leaf == True:
92 | print('leaf')
93 | print(node.keys)
94 | print(node.pointers)
95 | if node.parent != '':
96 | print('parent:',node.parent.keys)
97 | else:
98 | print('node')
99 | print(node.keys)
100 | if node.parent != '':
101 | print('parent:',node.parent.keys)
102 | for i in node.pointers:
103 | __do_print(i)
104 |
105 |
106 | def insert_into_table(table,__values):
107 | for index,col in enumerate(CatalogManager.catalog.tables[table].columns):
108 | if col.type == 'int':
109 | __values[index] = int(__values[index])
110 | elif col.type == 'char':
111 | __values[index] = __values[index].strip().replace("'",'')
112 | elif col.type == 'float':
113 | __values[index] = float(__values[index])
114 |
115 | cur_node = tables[table]
116 | __primary_key = CatalogManager.catalog.tables[table].primary_key
117 | # __primary_key = 0
118 | if len(cur_node.keys) == 0:
119 | # new tree
120 | cur_node.keys.append(__values[__primary_key])
121 | cur_node.pointers.append(__values)
122 | cur_node.pointers.append('')
123 | print('Successfully insert into table %s,' % table,end='')
124 | return
125 |
126 | cur_node = find_leaf_place(table,__values[__primary_key])
127 | if len(cur_node.keys) < N - 1:
128 | insert_into_leaf(cur_node,__values[__primary_key],__values)
129 |
130 | else:
131 | insert_into_leaf(cur_node,__values[__primary_key],__values)
132 | new_node = node(True,[],[])
133 | tmp_keys = cur_node.keys
134 | tmp_pointers = cur_node.pointers
135 | cur_node.keys = []
136 | cur_node.pointers = []
137 | for i in range(math.ceil(N/2)):
138 | cur_node.keys.append(tmp_keys.pop(0))
139 | cur_node.pointers.append(tmp_pointers.pop(0))
140 | for i in range(N - math.ceil(N/2)):
141 | new_node.keys.append(tmp_keys.pop(0))
142 | new_node.pointers.append(tmp_pointers.pop(0))
143 | cur_node.pointers.append(new_node)
144 | new_node.pointers.append(tmp_pointers.pop(0))
145 | insert_into_parent(table,cur_node,new_node.keys[0],new_node)
146 |
147 | print('Successfully insert into table %s,' % table,end='')
148 |
149 | def create_table(table,statement):
150 | tables[table] = node(True,[],[])
151 |
152 | def delete_table(table):
153 | # delete table instance
154 | tables.pop(table)
155 |
156 | def delete_from_table(table,statement):
157 | # delete rows from table according to the statement's condition
158 | # usage : find_leaf_place_with_condition(table, column, value,condition)
159 | if len(statement) == 0:
160 | tables[table] = node(True,[],[],'')
161 | print("Successfully delete all entrys from table '%s'," % table,end='')
162 | else:
163 | columns = {}
164 | for index,col in enumerate(CatalogManager.catalog.tables[table].columns):
165 | columns[col.column_name] = index
166 | __primary_key = CatalogManager.catalog.tables[table].primary_key
167 | # __primary_key = 0
168 | # columns = {'num':0,'val':1}
169 |
170 | conditions = []
171 | tmp = []
172 | pos = 1
173 | for i in statement:
174 | if i == 'and':
175 | conditions.append(tmp)
176 | tmp = []
177 | pos = 1
178 | continue
179 | if pos == 1:
180 | tmp.append(columns[i])
181 | elif pos == 3:
182 | if CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'char':
183 | tmp.append(i.strip().replace("'", ''))
184 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'int':
185 | tmp.append(int(i))
186 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'float':
187 | tmp.append(float(i))
188 | else:
189 | tmp.append(i)
190 | pos = pos + 1
191 | conditions.append(tmp)
192 | times = 0
193 | while True:
194 | nodes = find_leaf_place_with_condition(table,
195 | conditions[0][0],conditions[0][2],conditions[0][1])
196 | for col in conditions:
197 | if col[0] == __primary_key:
198 | nodes = find_leaf_place_with_condition(table,col[0],col[2],col[1])
199 | break
200 |
201 | if len(nodes) == 0:
202 | break
203 | seed = False
204 | for __node in nodes:
205 | if seed == True:
206 | break
207 | for index,leaf in enumerate(__node.pointers[0:-1]):
208 | if check_conditions(leaf,conditions):
209 | __node.pointers.pop(index)
210 | __node.keys.pop(index)
211 | maintain_B_plus_tree_after_delete(table,__node)
212 | times = times + 1
213 | seed = True
214 | break
215 | if seed == False:
216 | break
217 | print("Successfully delete %d entry(s) from table '%s'," % (times,table),end='')
218 |
219 | def check_conditions(leaf,conditions):
220 | for cond in conditions:
221 | # cond <-> column op value
222 | __value = leaf[cond[0]]
223 | if cond[1] == '<':
224 | if not (__value < cond[2]):
225 | return False
226 | elif cond[1] == '<=':
227 | if not (__value <= cond[2]):
228 | return False
229 | elif cond[1] == '>':
230 | if not (__value > cond[2]):
231 | return False
232 | elif cond[1] == '>=':
233 | if not (__value >= cond[2]):
234 | return False
235 | elif cond[1] == '<>':
236 | if not (__value != cond[2]):
237 | return False
238 | elif cond[1] == '=':
239 | if not (__value == cond[2]):
240 | return False
241 | else:
242 | raise Exception("Index Module : unsupported op.")
243 | return True
244 |
245 | def maintain_B_plus_tree_after_delete(table,__node):
246 | global N
247 | if __node.parent == '' and len(__node.pointers) == 1:
248 | tables[table] = __node.pointers[0]
249 | elif ((len(__node.pointers) < math.ceil(N/2) and __node.is_leaf == False) or
250 | (len(__node.keys) < math.ceil((N-1)/2) and __node.is_leaf == True) ) \
251 | and __node.parent != '':
252 | previous = False
253 | other_node = node(True,[],[])
254 | K = ''
255 | __index = 0
256 | for index, i in enumerate(__node.parent.pointers):
257 | if i == __node:
258 | if index == len(__node.parent.pointers) - 1:
259 | other_node = __node.parent.pointers[-2]
260 | previous = True
261 | K = __node.parent.keys[index - 1]
262 | else:
263 | K = __node.parent.keys[index]
264 | other_node = __node.parent.pointers[index + 1]
265 | __index = index + 1
266 |
267 | if (other_node.is_leaf == True and len(other_node.keys)+len(__node.keys) < N) or \
268 | (other_node.is_leaf == False and len(other_node.pointers) +
269 | len(__node.pointers) <= N):
270 | if previous == True:
271 | if other_node.is_leaf == False:
272 | other_node.pointers = other_node.pointers + __node.pointers
273 | other_node.keys = other_node.keys + [K] + __node.keys
274 | for __node__ in __node.pointers:
275 | __node__.parent = other_node
276 | else:
277 | other_node.pointers = other_node.pointers[0:-1]
278 | other_node.pointers = other_node.pointers + __node.pointers
279 | other_node.keys = other_node.keys + __node.keys
280 | __node.parent.pointers = __node.parent.pointers[0:-1]
281 | __node.parent.keys = __node.parent.keys[0:-1]
282 | maintain_B_plus_tree_after_delete(table,__node.parent)
283 | else:
284 | if other_node.is_leaf == False:
285 | __node.pointers = __node.pointers + other_node.pointers
286 | __node.keys = __node.keys + [K] + other_node.keys
287 | for __node__ in other_node.pointers:
288 | __node__.parent = __node
289 | else:
290 | __node.pointers = __node.pointers[0:-1]
291 | __node.pointers = __node.pointers + other_node.pointers
292 | __node.keys = __node.keys + other_node.keys
293 | __node.parent.pointers.pop(__index)
294 | __node.parent.keys.pop(__index-1)
295 | maintain_B_plus_tree_after_delete(table,__node.parent)
296 | else:
297 | if previous == True:
298 | if other_node.is_leaf == True:
299 | __node.keys.insert(0,other_node.keys.pop(-1))
300 | __node.pointers.insert(0,other_node.pointers.pop(-2))
301 | __node.parent.keys[-1] = __node.keys[0]
302 | else:
303 | __tmp = other_node.pointers.pop(-1)
304 | __tmp.parent = __node
305 | __node.pointers.insert(0,__tmp)
306 | __node.keys.insert(0,__node.parent.keys[-1])
307 | __node.parent.keys[-1] = other_node.keys.pop(-1)
308 | else:
309 | if other_node.is_leaf == True:
310 | __node.keys.insert(-1,other_node.keys.pop(0))
311 | __node.pointers.insert(-2,other_node.pointers.pop(0))
312 | __node.parent.keys[__index-1] = other_node.keys[0]
313 | else:
314 | __tmp = other_node.pointers.pop(0)
315 | __tmp.parent = __node
316 | __node.pointers.insert(-1,__tmp)
317 | __node.keys.insert(-1,__node.parent.keys[__index-1])
318 | __node.parent.keys[__index-1] = other_node.keys.pop(0)
319 |
320 | def create_index(index_name,table,column):
321 | pass
322 |
323 | def select_from_table(table,__conditions = '',__columns = ''):
324 | results = []
325 | columns = {}
326 | for index,col in enumerate(CatalogManager.catalog.tables[table].columns):
327 | columns[col.column_name] = index
328 | __primary_key = CatalogManager.catalog.tables[table].primary_key
329 | # __primary_key = 0
330 | # columns = {'num': 0, 'val': 1}
331 |
332 | if len(tables[table].keys) == 0:
333 | pass
334 | else:
335 | if __conditions != '':
336 | conditions = []
337 | statement = __conditions.split(' ')
338 | tmp = []
339 | pos = 1
340 | for i in statement:
341 | if i == 'and':
342 | conditions.append(tmp)
343 | tmp = []
344 | pos = 1
345 | continue
346 | if pos == 1:
347 | tmp.append(columns[i])
348 | elif pos == 3:
349 | if CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'char':
350 | tmp.append(i.strip().replace("'",''))
351 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'int':
352 | tmp.append(int(i))
353 | elif CatalogManager.catalog.tables[table].columns[tmp[0]].type == 'float':
354 | tmp.append(float(i))
355 | else:
356 | tmp.append(i)
357 | pos = pos + 1
358 | conditions.append(tmp)
359 | nodes = find_leaf_place_with_condition(table,
360 | conditions[0][0], conditions[0][2], conditions[0][1])
361 | for col in conditions:
362 | if col[0] == __primary_key:
363 | nodes = find_leaf_place_with_condition(table, col[0], col[2], col[1])
364 | break
365 | for __node in nodes:
366 | for pointer in __node.pointers[0:-1]:
367 | if check_conditions(pointer,conditions):
368 | results.append(pointer)
369 | else:
370 | first_leaf_node = tables[table]
371 | while first_leaf_node.is_leaf != True:
372 | first_leaf_node = first_leaf_node.pointers[0]
373 | while True:
374 | for i in first_leaf_node.pointers[0:-1]:
375 | results.append(i)
376 | if first_leaf_node.pointers[-1] != '':
377 | first_leaf_node = first_leaf_node.pointers[-1]
378 | else:
379 | break
380 |
381 | if __columns == '*':
382 | __columns_list = list(columns.keys())
383 | __columns_list_num = list(columns.values())
384 | else:
385 | __columns_list_num = [columns[i.strip()] for i in __columns.split(',')]
386 | __columns_list = [i.strip() for i in __columns.split(',')]
387 |
388 | print('-' * (17 * len(__columns_list_num) + 1))
389 | for i in __columns_list:
390 | if len(str(i)) > 14:
391 | output = str(i)[0:14]
392 | else:
393 | output = str(i)
394 | print('|',output.center(15),end='')
395 | print('|')
396 | print('-' * (17 * len(__columns_list_num) + 1))
397 | for i in results:
398 | for j in __columns_list_num:
399 | if len(str(i[j])) > 14:
400 | output = str(i[j])[0:14]
401 | else:
402 | output = str(i[j])
403 | print('|',output.center(15) ,end='')
404 | print('|')
405 | print('-' * (17 * len(__columns_list_num) + 1))
406 | print("Returned %d entrys," % len(results),end='')
407 |
408 | def check_unique(table,column,value):
409 | columns = []
410 | for col in CatalogManager.catalog.tables[table].columns:
411 | columns.append(col)
412 | if len(find_leaf_place_with_condition(table,column,value,'=')):
413 | raise Exception("Index Module : column '%s' does not satisfy "
414 | "unique constrains." % columns[column])
415 |
416 | def find_leaf_place(table,value):
417 | # search on primary key
418 | cur_node = tables[table]
419 | while not cur_node.is_leaf:
420 | seed = False
421 | for index,key in enumerate(cur_node.keys):
422 | if key > value:
423 | cur_node = cur_node.pointers[index]
424 | seed = True
425 | break
426 | if seed == False:
427 | cur_node = cur_node.pointers[-1]
428 | return cur_node
429 |
430 | def find_leaf_place_with_condition(table,column,value,condition):
431 | # __primary_key = CatalogManager.catalog.tables[table].primary_key
432 | __primary_key = 0
433 | head_node = tables[table]
434 | first_leaf_node = head_node
435 | while first_leaf_node.is_leaf != True:
436 | first_leaf_node = first_leaf_node.pointers[0]
437 | lists = []
438 |
439 | if __primary_key == column and condition != '<>':
440 | while not head_node.is_leaf:
441 | seed = False
442 | for index, key in enumerate(head_node.keys):
443 | if key > value:
444 | head_node = head_node.pointers[index]
445 | seed = True
446 | break
447 | if seed == False:
448 | head_node = head_node.pointers[-1]
449 | if condition == '=':
450 | for pointer in head_node.pointers[0:-1]:
451 | if pointer[column] == value:
452 | lists.append(head_node)
453 | elif condition == '<=':
454 | cur_node = first_leaf_node
455 | while True:
456 | if cur_node != head_node:
457 | lists.append(cur_node)
458 | cur_node = cur_node.pointers[-1]
459 | else:
460 | break
461 | for pointer in head_node.pointers[0:-1]:
462 | if pointer[column] <= value:
463 | lists.append(head_node)
464 | break
465 | elif condition == '<':
466 | cur_node = first_leaf_node
467 | while True:
468 | if cur_node != head_node:
469 | lists.append(cur_node)
470 | cur_node = cur_node.pointers[-1]
471 | else:
472 | break
473 | for pointer in head_node.pointers[0:-1]:
474 | if pointer[column] < value:
475 | lists.append(head_node)
476 | break
477 | elif condition == '>':
478 | for pointer in head_node.pointers[0:-1]:
479 | if pointer[column] > value:
480 | lists.append(head_node)
481 | break
482 | while True:
483 | head_node = head_node.pointers[-1]
484 | if head_node != '':
485 | lists.append(head_node)
486 | else:
487 | break
488 | elif condition == '>=':
489 | for pointer in head_node.pointers[0:-1]:
490 | if pointer[column] >= value:
491 | lists.append(head_node)
492 | break
493 | while True:
494 | head_node = head_node.pointers[-1]
495 | if head_node != '':
496 | lists.append(head_node)
497 | else:
498 | break
499 | else:
500 | raise Exception("Index Module : unsupported op.")
501 |
502 | else:
503 | while True:
504 | for pointer in first_leaf_node.pointers[0:-1]:
505 | if condition == '=':
506 | if pointer[column] == value:
507 | lists.append(first_leaf_node)
508 | break
509 | elif condition == '<':
510 | if pointer[column] < value:
511 | lists.append(first_leaf_node)
512 | break
513 | elif condition == '<=':
514 | if pointer[column] <= value:
515 | lists.append(first_leaf_node)
516 | break
517 | elif condition == '>':
518 | if pointer[column] > value:
519 | lists.append(first_leaf_node)
520 | break
521 | elif condition == '>=':
522 | if pointer[column] >= value:
523 | lists.append(first_leaf_node)
524 | break
525 | elif condition == '<>':
526 | if pointer[column] != value:
527 | lists.append(first_leaf_node)
528 | break
529 | else:
530 | raise Exception("Index Module : unsupported op.")
531 | if first_leaf_node.pointers[-1] == '':
532 | break
533 | first_leaf_node = first_leaf_node.pointers[-1]
534 | return lists
535 |
536 | def insert_into_leaf(cur_node,value,pointer):
537 | for index,key in enumerate(cur_node.keys):
538 | if key == value:
539 | raise Exception("Index Module : primary_key already exists.")
540 | if key > value:
541 | cur_node.pointers.insert(index,pointer)
542 | cur_node.keys.insert(index,value)
543 | return
544 | cur_node.pointers.insert(len(cur_node.keys), pointer)
545 | cur_node.keys.insert(len(cur_node.keys), value)
546 |
547 | def insert_into_parent(table,__node,__key,new_node):
548 | if __node.parent == '':
549 | cur_node = node(False,[],[],'')
550 | cur_node.pointers.append(__node)
551 | cur_node.pointers.append(new_node)
552 | cur_node.keys.append(__key)
553 | __node.parent = cur_node
554 | new_node.parent = cur_node
555 | tables[table] = cur_node
556 | else:
557 | p = __node.parent
558 | if len(p.pointers) < N:
559 | seed = False
560 | for index,key in enumerate(p.keys):
561 | if __key < key:
562 | p.keys.insert(index,__key)
563 | p.pointers.insert(index+1,new_node)
564 | seed = True
565 | break
566 | if seed == False:
567 | p.keys.append(__key)
568 | p.pointers.append(new_node)
569 | new_node.parent = p
570 | else:
571 | seed = False
572 | for index,key in enumerate(p.keys):
573 | if __key < key:
574 | p.keys.insert(index,__key)
575 | p.pointers.insert(index+1,new_node)
576 | seed = True
577 | break
578 | if seed == False:
579 | p.keys.append(__key)
580 | p.pointers.append(new_node)
581 | __new_node = node(False,[],[])
582 | tmp_keys = p.keys
583 | tmp_pointers = p.pointers
584 | p.keys = []
585 | p.pointers = []
586 | for i in range(math.ceil(N / 2)):
587 | p.keys.append(tmp_keys.pop(0))
588 | p.pointers.append(tmp_pointers.pop(0))
589 | p.pointers.append(tmp_pointers.pop(0))
590 | k__ = tmp_keys.pop(0)
591 | for i in range(N - math.ceil(N / 2) - 1):
592 | __new_node.keys.append(tmp_keys.pop(0))
593 | __tmp = tmp_pointers.pop(0)
594 | __tmp.parent = __new_node
595 | __new_node.pointers.append(__tmp)
596 | __tmp = tmp_pointers.pop(0)
597 | __tmp.parent = __new_node
598 | __new_node.pointers.append(__tmp)
599 | new_node.parent = __new_node
600 | insert_into_parent(table,p,k__,__new_node)
601 |
602 | def exist_user(username,password):
603 | nodes = find_leaf_place_with_condition('sys', 0, username, '=')
604 | for __node in nodes:
605 | for ptr in __node.pointers[0:-1]:
606 | if ptr[0] == username and ptr[1] == password:
607 | return True
608 | return False
609 |
610 |
611 | if __name__ == '__main__':
612 | __initialize__('/Users/alan/Desktop/CodingLife/Python/miniSQL/')
613 | tables['student'] = node(True, [], [])
614 | insert_into_table('student',[2,'we'])
615 | insert_into_table('student', [3, 'ke'])
616 | insert_into_table('student', [5, 'ww'])
617 | insert_into_table('student', [7, 'ww'])
618 | insert_into_table('student', [11, 'wl'])
619 | insert_into_table('student', [17, 'wl'])
620 | insert_into_table('student', [19, 'wl'])
621 | insert_into_table('student', [23, 'wl'])
622 | insert_into_table('student', [29, 'wl'])
623 | insert_into_table('student', [31, 'wl'])
624 | insert_into_table('student', [9, 'wl'])
625 | insert_into_table('student', [10, 'wl'])
626 | insert_into_table('student', [8, 'wl'])
627 |
628 |
629 | # delete_from_table('student',['num','=',23])
630 | # delete_from_table('student', ['num', '=', 19])
631 | # __prints('student')
632 | # select_from_table('student','num > 0','*')
633 | __store__()
634 | pass
635 |
--------------------------------------------------------------------------------
/miniSQL/miniSQL/MiniSQL.py:
--------------------------------------------------------------------------------
1 | import cmd
2 | import APIManager.api
3 | import CatalogManager.catalog
4 | import IndexManager.index
5 | import sys
6 | import time
7 | import os
8 |
9 | class miniSQL(cmd.Cmd):
10 | intro = 'Welcome to the MiniSQL database server.\nType help or ? to list commands.\n'
11 | def do_select(self,args):
12 | # APIManager.api.select(args.replace(';', ''))
13 | try:
14 | APIManager.api.select(args.replace(';',''))
15 | except Exception as e:
16 | print(str(e))
17 |
18 | def do_create(self,args):
19 | try:
20 | APIManager.api.create(args.replace(';',''))
21 | except Exception as e:
22 | print(str(e))
23 |
24 | def do_drop(self,args):
25 | try:
26 | APIManager.api.drop(args.replace(';',''))
27 | except Exception as e:
28 | print(str(e))
29 |
30 | def do_insert(self,args):
31 | try:
32 | APIManager.api.insert(args.replace(';',''))
33 | except Exception as e:
34 | print(str(e))
35 |
36 | def do_delete(self,args):
37 | try:
38 | APIManager.api.delete(args.replace(';',''))
39 | except Exception as e:
40 | print(str(e))
41 |
42 | def do_commit(self,args):
43 | time_start = time.time()
44 | __finalize__()
45 | time_end = time.time()
46 | print('Modifications has been commited to local files,',end='')
47 | print(" time elapsed : %fs." % (time_end - time_start))
48 |
49 | def do_quit(self,args):
50 | __finalize__()
51 | print('Goodbye.')
52 | sys.exit()
53 |
54 | def emptyline(self):
55 | pass
56 |
57 | def default(self, line):
58 | print('Unrecognized command.\nNo such symbol : %s' % line)
59 |
60 | def help_commit(self):
61 | print()
62 | text = "To reduce file transfer's time, this SQL server is designed to "+\
63 | "'lasy' write changes to local files, which means it will not store changes "+\
64 | "until you type 'quit' to normally exit the server. if this server exit "+\
65 | "unnormally, all changes will be lost. If you want to write changes to "+\
66 | "local files immediately, please use 'commit' command.\n"
67 | print(text)
68 |
69 | def help_quit(self):
70 | print()
71 | print('Quit the program and write changes to local file.')
72 |
73 | def help_select(self):
74 | print()
75 | print("select * from student;")
76 | print("select num from student where num >= 2 and num < 10 and gender = 'male';")
77 |
78 | def help_create(self):
79 | print()
80 | print("create table student (ID int, name char(10),gender char(10)"
81 | ",enroll_date char(10),primary key(ID));")
82 |
83 | def help_drop(self):
84 | print()
85 | print("drop table student;")
86 |
87 | def help_insert(self):
88 | print('''
89 | insert into student values ( 1,'Alan','male','2017.9.1');
90 | insert into student values ( 2,'rose','female','2016.9.1');
91 | insert into student values ( 3,'Robert','male','2016.9.1');
92 | insert into student values ( 4,'jack','male','2015.9.1');
93 | insert into student values ( 5,'jason','male','2015.9.1');
94 | insert into student values ( 6,'Hans','female','2015.9.1');
95 | insert into student values ( 7,'rosa','male','2014.9.1');
96 | insert into student values ( 8,'messi','female','2013.9.1');
97 | insert into student values ( 9,'Neymar','male','2013.9.1');
98 | insert into student values ( 10,'Christ','male','2011.9.1');
99 | insert into student values ( 11,'shaw','female','2010.9.1');
100 | ''')
101 |
102 | def help_delete(self):
103 | print()
104 | print("delete from students")
105 | print("delete from student where sno = '88888888';")
106 |
107 | def exec_from_file(filename):
108 | f = open(filename)
109 | text = f.read()
110 | f.close()
111 | comands = text.split(';')
112 | comands = [i.strip().replace('\n','') for i in comands]
113 | __initialize__()
114 | for comand in comands:
115 | if comand == '':
116 | continue
117 | if comand[0] == '#':
118 | continue
119 | if comand.split(' ')[0] == 'insert':
120 | try:
121 | APIManager.api.insert(comand[6:])
122 | except Exception as e:
123 | print(str(e))
124 | elif comand.split(' ')[0] == 'select':
125 | try:
126 | APIManager.api.select(comand[6:])
127 | except Exception as e:
128 | print(str(e))
129 | elif comand.split(' ')[0] == 'delete':
130 | try:
131 | APIManager.api.delete(comand[6:])
132 | except Exception as e:
133 | print(str(e))
134 | elif comand.split(' ')[0] == 'drop':
135 | try:
136 | APIManager.api.drop(comand[4:])
137 | except Exception as e:
138 | print(str(e))
139 | elif comand.split(' ')[0] == 'create':
140 | try:
141 | APIManager.api.create(comand[6:])
142 | except Exception as e:
143 | print(str(e))
144 | __finalize__()
145 |
146 |
147 | def __initialize__():
148 | CatalogManager.catalog.__initialize__(os.getcwd())
149 | IndexManager.index.__initialize__(os.getcwd())
150 |
151 | def __finalize__():
152 | CatalogManager.catalog.__finalize__()
153 | IndexManager.index.__finalize__()
154 |
155 | if __name__ == '__main__':
156 | errortext = '''
157 | MiniSQL -u [username] -p [password] (optional)-execfile [filename]
158 | \tLogin operators :
159 | \t\t-u username\tusername for MiniSQL.
160 | \t\t-p password\tpassword for MiniSQL.\n
161 | \tExecute SQL file operators :
162 | \t\t-execfile filename\tSQL filename to be executed for MiniSQL.
163 | '''
164 | if len(sys.argv) < 5:
165 | print('ERROR : Unsupported syntax, please login.\n',errortext)
166 | sys.exit()
167 | if sys.argv[1] != '-u' or sys.argv[3] != '-p':
168 | print('ERROR : Unsupported syntax, please login.\n',errortext)
169 | sys.exit()
170 | __initialize__()
171 | if sys.argv[2] == 'root' and sys.argv[4] == '123456':
172 | APIManager.api.__root = True
173 | elif IndexManager.index.exist_user(username=sys.argv[2],password=sys.argv[4]):
174 | APIManager.api.__root = False
175 | else:
176 | print('Error : username or password is not correct,please '
177 | 'check and login again.\n',errortext)
178 | sys.exit()
179 | if len(sys.argv) > 5:
180 | if sys.argv[5] != '-execfile':
181 | print('ERROR : Unsupported syntax.\n',errortext)
182 | exec_from_file(sys.argv[6])
183 | sys.exit()
184 | print("MiniSQL database server, version 1.0.0-release, (x86_64-apple-darwin)\n"
185 | "Copyright 2018 @ Alan Shaw from ZJU. Course final project for DBS.\n"
186 | "These shell commands are defined internally. Type `help' to see this list.\n"
187 | "Type `help name' to find out more about the function `name'.\n")
188 |
189 | miniSQL.prompt = '(%s)' % sys.argv[2] + 'MiniSQL > '
190 | miniSQL().cmdloop()
191 |
--------------------------------------------------------------------------------
/miniSQL/miniSQL/dbfiles/catalog_files/indexs_catalog.msql:
--------------------------------------------------------------------------------
1 | {"sys_default_index": {"table": "sys", "column": "username"}}
--------------------------------------------------------------------------------
/miniSQL/miniSQL/dbfiles/catalog_files/tables_catalog.msql:
--------------------------------------------------------------------------------
1 | {"sys": {"primary_key": 0, "columns": {"username": [true, "char", 16], "password": [false, "char", 16]}}, "student": {"primary_key": 0, "columns": {"num": [true, "int", 0], "val": [false, "char", 10]}}}
--------------------------------------------------------------------------------
/miniSQL/miniSQL/dbfiles/index_files/tables_B-plus_tree.msql:
--------------------------------------------------------------------------------
1 | {"sys": {"is_leaf": true, "keys": ["alan"], "pointers": [["alan", "123456"], ""]}, "student": {"is_leaf": false, "keys": [7], "pointers": [{"is_leaf": false, "keys": [3, 5], "pointers": [{"is_leaf": true, "keys": [1, 2], "pointers": [[1, "a"], [2, "b"]]}, {"is_leaf": true, "keys": [3, 4], "pointers": [[3, "c"], [4, "d"]]}, {"is_leaf": true, "keys": [5, 6], "pointers": [[5, "e"], [6, "f"]]}]}, {"is_leaf": false, "keys": [9], "pointers": [{"is_leaf": true, "keys": [7, 8], "pointers": [[7, "g"], [8, "h"]]}, {"is_leaf": true, "keys": [9, 10, 11], "pointers": [[9, "I"], [10, "j"], [11, "k"], ""]}]}]}}
--------------------------------------------------------------------------------