pax_global_header00006660000000000000000000000064145600747770014533gustar00rootroot0000000000000052 comment=d672551c7d45e09dd1cab20db97e9d11fadbcb76 mariadb-connector-python-1.1.10/000077500000000000000000000000001456007477700165215ustar00rootroot00000000000000mariadb-connector-python-1.1.10/.gitattributes000066400000000000000000000000461456007477700214140ustar00rootroot00000000000000# Normalise line endings: * text=auto mariadb-connector-python-1.1.10/.gitignore000066400000000000000000000007041456007477700205120ustar00rootroot00000000000000# Keep empty directories: # Keep empty directories: >> .gitignore/.git* # Compiled Static libraries *.lib *.a *.la *.lai *.lo # Compiled Dynamic libraries *.so *.so.* *.dylib *.dll # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex *.dgcov .*.swp .gdb_history # Build/Dist build/ dist/ # Cache __pycache__ #VS files/directories *.vcxproj *.filters *.user ipch *.sln *.suo *.sdf Win32 x64 *.dir Debug Release RelWithDebInfo #vim backups *.*~ mariadb-connector-python-1.1.10/.gitmodules000066400000000000000000000002061456007477700206740ustar00rootroot00000000000000[submodule "mariadb-connector-c"] path = mariadb-connector-c url = https://github.com/MariaDB/mariadb-connector-c.git branch = 3.0 mariadb-connector-python-1.1.10/.pre-commit-config.yaml000066400000000000000000000002051456007477700227770ustar00rootroot00000000000000repos: - repo: https://github.com/pycqa/flake8 rev: '5.0.4' # pick a git hash / tag to point to hooks: - id: flake8 mariadb-connector-python-1.1.10/.readthedocs.yml000066400000000000000000000011031456007477700216020ustar00rootroot00000000000000# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/source/conf.py # Build documentation with MkDocs #mkdocs: # configuration: mkdocs.yml # Optionally build your docs in additional formats such as PDF and ePub formats: all # Optionally set the version of Python and requirements required to build your docs #python: # version: 3.7 # install: # - requirements: docs/requirements.txt mariadb-connector-python-1.1.10/.travis.yml000066400000000000000000000067221456007477700206410ustar00rootroot00000000000000sudo: true language: c before_install: - export MAIN_PATH=`pwd` # install pyenv to test multiple python version - git clone https://github.com/pyenv/pyenv.git ~/.pyenv - export PYENV_ROOT="$HOME/.pyenv" - eval "$(pyenv init -)" - export PATH="$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH" # install c dependency - |- if [ "$TRAVIS_OS_NAME" == "linux" ] ; then sudo apt-get install software-properties-common sudo apt-get install -f libssl-dev libssl1.1 sudo apt-get install -f fi - git clone https://github.com/mariadb-corporation/mariadb-connector-c.git ~/.cc_3 - cd ~/.cc_3 - mkdir bld - cd bld - |- case $TRAVIS_OS_NAME in windows) cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr cmake --build . --config RelWithDebInfo ;; osx) cmake .. -DCMAKE_BUILD_TYPE=Release -DWITH_EXTERNAL_ZLIB:BOOL=ON -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 make -j4 ;; linux) cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr make -j4 ;; esac - sudo make install - export MARIADB_PLUGIN_DIR==`mariadb_config --plugindir` - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/mariadb # install "install-latest" to retrieve latest python version corresponding to major.minor version - git clone https://github.com/momo-lab/pyenv-install-latest.git "$(pyenv root)"/plugins/pyenv-install-latest - export REAL_PYTHON_VERSION=$(pyenv install-latest --print $PYTHON_VER) - echo $REAL_PYTHON_VERSION - pyenv install $REAL_PYTHON_VERSION - export PYENV_VERSION=$REAL_PYTHON_VERSION - pyenv versions # install server - cd $MAIN_PATH env: global: PYTHON_VER="3.10" HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1 DB=testp CLEAR_TEXT=0 import: mariadb-corporation/connector-test-machine:common-build.yml@master jobs: include: - stage: Language env: srv=mariadb v=10.11 local=1 PYTHON_VER="3.8" name: "Python 3.8" - env: srv=mariadb v=10.11 local=1 PYTHON_VER="3.9" name: "Python 3.9" - env: srv=mariadb v=10.11 local=1 PYTHON_VER="3.10" name: "Python 3.10" - env: srv=mariadb v=10.11 local=1 PYTHON_VER="3.11" name: "Python 3.11" - env: srv=mariadb v=10.11 local=1 PYTHON_VER="3.12" name: "Python 3.12" script: - python --version - python -m pip install . # - python setup.py build # - python setup.py install - cd testing - |- if [ -z "$BENCH" ] ; then python -m unittest discover -v else pip install mysql-connector-python pymysql pyperf export TEST_MODULE=mariadb python bench_init.py --inherit-environ=TEST_MODULE,TEST_DB_USER,TEST_DB_HOST,TEST_DB_DATABASE,TEST_DB_PORT,TEST_REQUIRE_TLS,TEST_DB_PASSWORD --copy-env python bench.py -o mariadb_bench.json --inherit-environ=TEST_MODULE,TEST_DB_USER,TEST_DB_HOST,TEST_DB_DATABASE,TEST_DB_PORT,TEST_REQUIRE_TLS,TEST_DB_PASSWORD --copy-env export TEST_MODULE=mysql.connector python bench.py -o mysql-connector-python_bench.json --inherit-environ=TEST_MODULE,TEST_DB_USER,TEST_DB_HOST,TEST_DB_DATABASE,TEST_DB_PORT,TEST_REQUIRE_TLS,TEST_DB_PASSWORD --copy-env export TEST_MODULE=pymysql python bench.py -o pymysql_bench.json --inherit-environ=TEST_MODULE,TEST_DB_USER,TEST_DB_HOST,TEST_DB_DATABASE,TEST_DB_PORT,TEST_REQUIRE_TLS,TEST_DB_PASSWORD --copy-env python -m pyperf compare_to pymysql_bench.json mysql-connector-python_bench.json mariadb_bench.json --table fi mariadb-connector-python-1.1.10/LICENSE000066400000000000000000000636361456007477700175440ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mariadb-connector-python-1.1.10/MANIFEST.in000066400000000000000000000003061456007477700202560ustar00rootroot00000000000000recursive-include test *.py recursive-include include *.h include LICENSE include mariadb_windows.py include mariadb_posix.py include MANIFEST.in include MANIFEST include README.md include site.cfg mariadb-connector-python-1.1.10/README.md000066400000000000000000000031251456007477700200010ustar00rootroot00000000000000

# MariaDB Connector/Python [![License (LGPL version 2.1)][licence-image]](LICENSE) [![Python 3.7][python-image]][python-url] [![Build Status](https://travis-ci.com/mariadb-corporation/mariadb-connector-python.svg?branch=1.1)](https://app.travis-ci.com/mariadb-corporation/mariadb-connector-python) Coverity Scan Build Status MariaDB Connector/Python enables python programs to access MariaDB and MySQL databases, using an API which is compliant with the Python DB API 2.0 (PEP-249). It is written in C and uses MariaDB Connector/C client library for client server communication. ## License MariaDB Connector/Python is licensed under the LGPL 2.1 or later (LGPL-2.1-or-later) ## Source code MariaDB Connector/Python source code is hosted on [Github](https://github.com/mariadb-corporation/mariadb-connector-python) ## Documentation MariaDB Connector/Python documentation can be found on [Github Pages](https://mariadb-corporation.github.io/mariadb-connector-python/) ## Bugs Bugs and feature requests should be filed in the [MariaDB bug ticket system](https://jira.mariadb.org/) [licence-image]:https://img.shields.io/badge/license-GNU%20LGPL%20version%202.1-green.svg?style=flat-square [python-image]:https://img.shields.io/badge/python-3.7-blue.svg [python-url]:https://www.python.org/downloads/release/python-370/ mariadb-connector-python-1.1.10/_config.yml000066400000000000000000000000331456007477700206440ustar00rootroot00000000000000theme: jekyll-theme-minimalmariadb-connector-python-1.1.10/helper/000077500000000000000000000000001456007477700200005ustar00rootroot00000000000000mariadb-connector-python-1.1.10/helper/create_errconst.py000066400000000000000000000021461456007477700235370ustar00rootroot00000000000000# # script for creating error constants # import requests ignore_definitions = ["ERR_ERROR_FIRST", "ER_ERROR_LAST", "CR_MIN_ERROR", "CR_MAX_ERROR", "CLIENT_ERRMAP", "CER_MIN_ERROR", "CER_MAX_ERROR", "CR_MYSQL_LAST_ERROR", "CR_MARIADB_LAST_ERROR"] files = ["https://raw.githubusercontent.com/mariadb-corporation/" "mariadb-connector-c/3.3/include/mysqld_error.h", "https://raw.githubusercontent.com/mariadb-corporation/" "mariadb-connector-c/3.3/include/errmsg.h"] error_definitions = [] for i in range(0, len(files)): errors = requests.get(files[i], allow_redirects=True) error_definitions += errors.content.decode("utf8").split("\n") print("# Autogenerated file. Please do not edit!\n\n") for i in range(0, len(error_definitions)): x = error_definitions[i].split() if (len(x) >= 3 and x[0] == "#define" and x[1] not in ignore_definitions and x[1][:9] != "ER_UNUSED"): try: if int(x[2]) > 0: print("%s = %s" % (x[1], x[2])) except Exception: pass mariadb-connector-python-1.1.10/include/000077500000000000000000000000001456007477700201445ustar00rootroot00000000000000mariadb-connector-python-1.1.10/include/docs/000077500000000000000000000000001456007477700210745ustar00rootroot00000000000000mariadb-connector-python-1.1.10/include/docs/common.h000066400000000000000000000066061456007477700225450ustar00rootroot00000000000000#define __connect__doc__ \ "connect(*args, **kwargs)\n"\ "--\n"\ "\n"\ "Establishes a connection to a database server and returns a connection\n"\ "object.\n\n"\ "Connection parameters are provided as a set of keyword arguments:\n"\ "----------------------\n"\ "host: string\n"\ " The host name or IP address of the database server\n\n"\ "user: string\n"\ "username: string\n"\ " The username used to authenticate with the database server\n\n"\ "password: string\n"\ "passwd: string\n"\ " The password of the given user\n\n"\ "database: string\n"\ "db: string\n"\ " database (schema) name to use when connecting with the database\n"\ " server\n\n"\ "unix_socket: string\n"\ " The location of the unix socket file to use instead of using an IP port\n"\ " to connect. If socket authentication is enabled, this can also be used\n"\ " in place of a password.\n\n"\ "port: integer\n"\ " port number of the database server. If not specified the default\n"\ " value of 3306 will be used.\n\n"\ "connect_timeout: integer\n"\ " connect timeout in seconds\n\n"\ "read_timeout: integer\n"\ " read timeout in seconds\n\n"\ "write_timeout: integer\n"\ " write timeout in seconds\n\n"\ "local_infile: boolean\n"\ " Enables or disables the use of LOAD DATA LOCAL INFILE statements.\n\n"\ "compress: boolean\n"\ " Uses the compressed protocol for client server communication. If the\n"\ " server doesn't support compressed protocol, the default protocol will\n"\ " be used\n\n"\ "init_command: string\n"\ " Command(s) which will be executed when connecting and reconnecting to\n"\ " the database server\n\n"\ "default_file: string\n"\ " Read options from the specified option file. If the file is an empty\n"\ " string, default configuration file(s) will be used\n\n"\ "default_group: string\n"\ " Read options from the specified group\n\n"\ "ssl_key: string\n"\ " Defines a path to a private key file to use for TLS. This option\n"\ " requires that you use the absolute path, not a relative path. The\n"\ " specified key must be in PEM format\n\n"\ "ssl_cert: string\n"\ " Defines a path to the X509 certificate file to use for TLS.\n"\ " This option requires that you use the absolute path, not a relative\n"\ " path. The X609 certificate must be in PEM format.\n\n"\ "ssl_ca: string\n"\ " Defines a path to a PEM file that should contain one or more X509\n"\ " certificates for trusted Certificate Authorities (CAs) to use for TLS.\n"\ " This option requires that you use the absolute path, not a relative\n"\ " path.\n\n"\ "ssl_capath: string\n"\ " Defines a path to a directory that contains one or more PEM files that\n"\ " contains one X509 certificate for a trusted Certificate Authority (CA)\n\n"\ "ssl_cipher: string\n"\ " Defines a list of permitted cipher suites to use for TLS\n\n"\ "ssl_crlpath: string\n"\ " Defines a path to a PEM file that should contain one or more revoked\n"\ " X509 certificates to use for TLS. This option requires that you use\n"\ " the absolute path, not a relative path.\n\n"\ "ssl_verify_cert: boolean\n"\ " Enables server certificate verification.\n\n"\ "ssl: Boolean\n"\ " The connection must use TLS security or it will fail.\n\n"\ "autocommit: Boolean or None\n"\ " Specifies the autocommit settings: None will use the server default,"\ " True will enable autocommit, False will disable it (default).\n\n" mariadb-connector-python-1.1.10/include/docs/connection.h000066400000000000000000000103731456007477700234100ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2019 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ PyDoc_STRVAR( connection_connect__doc__, __connect__doc__ ); PyDoc_STRVAR( connection__doc__, "The Connection class is used to open and manage a connection to a\n" "MariaDB or compatible database server" ); PyDoc_STRVAR( connection_dump_debug_info__doc__, "dump_debug_info()\n" "--\n" "\n" "This function is designed to be executed by an user with the SUPER privilege\n" "and is used to dump server status information into the log for the MariaDB\n" "Server relating to the connection." ); PyDoc_STRVAR( connection_close__doc__, "close()\n" "--\n" "\n" "Close the connection now (rather than whenever .__del__() is called).\n\n" "The connection will be unusable from this point forward; an Error\n" "(or subclass) exception will be raised if any operation is attempted\n" "with the connection. The same applies to all cursor objects trying to\n" "use the connection.\n\n" "Note that closing a connection without committing the changes first\n" "will cause an implicit rollback to be performed." ); PyDoc_STRVAR( connection_change_user__doc__, "change_user(user: str, password: str, database: str)\n" "--\n" "\n" "Changes the user and default database of the current connection\n\n" "Parameters:\n" " - user: user name\n" " - password: password\n" " - database: name of default database\n\n" "In order to successfully change users a valid username and password\n" "parameters must be provided and that user must have sufficient\n" "permissions to access the desired database. If for any reason\n" "authorization fails an exception will be raised and the current user\n" "authentication will remain." ); PyDoc_STRVAR( connection_reconnect__doc__, "reconnect()\n" "--\n" "\n" "tries to reconnect to a server in case the connection died due to timeout\n" "or other errors. It uses the same credentials which were specified in\n" "connect() method." ); PyDoc_STRVAR( connection_reset__doc__, "reset()\n" "--\n" "\n" "Resets the current connection and clears session state and pending\n" "results. Open cursors will become invalid and cannot be used anymore." ); PyDoc_STRVAR( connection_escape_string__doc__, "escape_string(statement)\n" "--\n" "\n" "Parameters:\n" "statement: string\n\n" "This function is used to create a legal SQL string that you can use in\n" "an SQL statement. The given string is encoded to an escaped SQL string." ); /* ok */ PyDoc_STRVAR( connection_ping__doc__, "ping()\n" "--\n" "\n" "Checks if the connection to the database server is still available.\n\n" "If auto reconnect was set to true, an attempt will be made to reconnect\n" "to the database server in case the connection\n" "was lost\n\n" "If the connection is not available an InterfaceError will be raised." ); PyDoc_STRVAR( connection_auto_reconnect__doc__, "(read/write)\n\n" "Enable or disable automatic reconnection to the server if the connection\n" "is found to have been lost.\n\n" "When enabled, client tries to reconnect to a database server in case\n" "the connection to a database server died due to timeout or other errors." ); PyDoc_STRVAR( connection_warnings__doc__, "Returns the number of warnings from the last executed statement, or zero\n" "if there are no warnings." ); mariadb-connector-python-1.1.10/include/docs/cursor.h000066400000000000000000000106471456007477700225720ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2019 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ PyDoc_STRVAR( cursor_description__doc__, "This read-only attribute is a sequence of 11-item sequences\n" "Each of these sequences contains information describing one result column:\n\n" "- name\n" "- type_code\n" "- display_size\n" "- internal_size\n" "- precision\n" "- scale\n" "- null_ok\n" "- field_flags\n" "- table_name\n" "- original_column_name\n" "- original_table_name\n\n" "This attribute will be None for operations that do not return rows or if the cursor has\n" "not had an operation invoked via the .execute*() method yet.\n\n" ); PyDoc_STRVAR( cursor_metadata__doc__, "Similiar to description property, this property returns a dictionary with complete metadata.\n\n" "The dictionary contains the following keys:\n\n" "- catalog: catalog (always 'def')\n" "- schema: current schema\n" "- field: alias column name or if no alias was specified column name\n" "- org_field: original column name\n" "- table: alias table name or if no alias was specified table name\n" "- org_table: original table name\n" "- type: column type\n" "- charset: character set (utf8mb4 or binary)\n" "- length: The length of the column\n" "- max length: The maximum length of the column\n" "- decimals: The numer of decimals\n" "- flags: Flags (flags are defined in constants.FIELD_FLAG)\n" "- ext_type: Extended data type (types are defined in constants.EXT_FIELD_TYPE)\n" ); PyDoc_STRVAR( cursor_warnings__doc__, "Returns the number of warnings from the last executed statement, or zero\n" "if there are no warnings.\n\n" ); PyDoc_STRVAR( cursor_closed__doc__, "Indicates if the cursor is closed and can't be reused" ); PyDoc_STRVAR( cursor_buffered__doc__, "When True all result sets are immediately transferred and the connection\n" "between client and server is no longer blocked. Since version 1.1.0 default\n" "is True, for prior versions default was False." ); PyDoc_STRVAR( cursor_close__doc__, "close()\n" "--\n" "\n" "Closes the cursor. If the cursor has pending or unread results, .close()\n" "will cancel them so that further operations using the same connection\n" "can be executed.\n\n" "The cursor will be unusable from this point forward; an Error (or subclass)\n" "exception will be raised if any operation is attempted with the cursor." ); PyDoc_STRVAR( cursor_fetchone__doc__, "fetchone()\n" "--\n" "\n" "Fetches next row of a pending result set and returns a tuple.\n" ); PyDoc_STRVAR( cursor_field_count__doc__, "field_count()\n" "--\n" "\n" "Returns the number of fields (columns) of a result set." ); PyDoc_STRVAR( cursor_nextset__doc__, "nextset()\n" "--\n" "\n" "Will make the cursor skip to the next available result set,\n" "discarding any remaining rows from the current set." ); PyDoc_STRVAR( cursor_next__doc__, "next()\n" "--\n" "\n" "Return the next row from the currently executed SQL statement\n" "using the same semantics as .fetchone()." ); PyDoc_STRVAR( cursor_statement__doc__, "(read only)\n\n" "The last executed statement" ); PyDoc_STRVAR( cursor_rownumber__doc__, "(read only)\n\n" "Current row number in result set" ); PyDoc_STRVAR( cursor_arraysize__doc__, "(read/write)\n\n" "the number of rows to fetch" ); PyDoc_STRVAR( cursor_paramcount__doc__, "(read)\n\n" "Returns the number of parameter markers present in the executed statement." ); mariadb-connector-python-1.1.10/include/docs/exception.h000066400000000000000000000050511456007477700232440ustar00rootroot00000000000000/***************************************************************************** Copyright (C) 2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ******************************************************************************/ PyDoc_STRVAR( exception_interface__doc__, "Exception raised for errors that are related to the database interface "\ "rather than the database itself" ); PyDoc_STRVAR( exception_warning__doc__, "Exception raised for important warnings like data truncations "\ "while inserting, etc" ); PyDoc_STRVAR( exception_database__doc__, "Exception raised for errors that are related to the database" ); PyDoc_STRVAR( exception_data__doc__, "Exception raised for errors that are due to problems with the "\ "processed data like division by zero, numeric value out of range, etc." ); PyDoc_STRVAR( exception_pool__doc__, "Exception raised for errors related to ConnectionPool class." ); PyDoc_STRVAR( exception_operational__doc__, "Exception raised for errors that are related to the database's "\ "operation and not necessarily under the control of the programmer." ); PyDoc_STRVAR( exception_integrity__doc__, "Exception raised when the relational integrity of the database "\ "is affected, e.g. a foreign key check fails" ); PyDoc_STRVAR( exception_internal__doc__, "Exception raised when the database encounters an internal error, "\ "e.g. the cursor is not valid anymore"; ); PyDoc_STRVAR( exception_programming__doc__, "Exception raised for programming errors, e.g. table not found or "\ "already exists, syntax error in the SQL statement" ); PyDoc_STRVAR( exception_notsupported__doc__, "Exception raised in case a method or database API was used which is "\ "not supported by the database" ); mariadb-connector-python-1.1.10/include/docs/module.h000066400000000000000000000020531456007477700225320ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2019 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ PyDoc_STRVAR( module_connect__doc__, __connect__doc__ ); mariadb-connector-python-1.1.10/include/mariadb_python.h000077500000000000000000000621021456007477700233210ustar00rootroot00000000000000/****************************************************************************** Copyright (C) 2018-2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ******************************************************************************/ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "bytesobject.h" #include "structmember.h" #include "structseq.h" #include #include #include #include #include #include #include #include #define CHECK_TYPE(obj, type) \ (Py_TYPE((obj)) == type || PyType_IsSubtype(Py_TYPE((obj)), type)) #define CHECK_TYPE_NO_NONE(obj, type) \ (obj != Py_None && (Py_TYPE((obj)) == type || PyType_IsSubtype(Py_TYPE((obj)), type))) #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { ob->ob_type = type; } #define Py_SET_TYPE(ob, type) _Py_SET_TYPE((PyObject*)(ob), type) #endif #if defined(_WIN32) #include #include #ifdef _MSC_VER typedef SSIZE_T ssize_t; #endif typedef CRITICAL_SECTION pthread_mutex_t; #define pthread_mutex_init(A,B) InitializeCriticalSection(A) #define pthread_mutex_lock(A) (EnterCriticalSection(A),0) #define pthread_mutex_unlock(A) LeaveCriticalSection(A) #define pthread_mutex_destroy(A) DeleteCriticalSection(A) #define pthread_self() GetCurrentThreadId() #include #else #include #include #endif /* defined(_WIN32) */ #ifndef MIN #define MIN(a,b) (a) < (b) ? (a) : (b) #endif #ifndef MAX #define MAX(a,b) (a) > (b) ? (a) : (b) #endif #if !defined(__GNUC__) && !defined(__clang__) #define __attribute__(A) #endif #ifdef _WIN32 int clock_gettime(int dummy, struct timespec *ct); #define CLOCK_MONOTONIC_RAW 1 #endif #define REQUIRED_CC_VERSION 30103 #if MARIADB_PACKAGE_VERSION_ID < REQUIRED_CC_VERSION #error Minimum required version of MariaDB Connector/C is 3.1.3 #endif #if defined(_WIN32) && defined(_MSVC) #ifndef L64 #define L64(x) x##i64 #endif #else #ifndef L64 #define L64(x) x##LL #endif /* L64 */ #endif /* _WIN32 */ #define STRINGIFY(n) #n #define TOSTRING(n) STRINGIFY(n) #define PY_MARIADB_VERSION TOSTRING(PY_MARIADB_MAJOR_VERSION) "." \ TOSTRING(PY_MARIADB_MINOR_VERSION) "." TOSTRING(PY_MARIADB_PATCH_VERSION) #define MAX_TPC_XID_SIZE 64 #define POOL_DEFAULT_SIZE 5 /* Placeholder for missing documentation */ #define MISSING_DOC NULL /* Magic constant for checking dynamic columns */ #define PYTHON_DYNCOL_VALUE 0xA378BD8E typedef struct st_lex_str { char *str; size_t length; } MrdbString; enum enum_binary_command { SQL_NONE= 0, SQL_INSERT, SQL_UPDATE, SQL_REPLACE, SQL_DELETE, SQL_CALL, SQL_DO, SQL_SELECT, SQL_OTHER=255 }; enum enum_extended_field_type { EXT_TYPE_NONE=0, EXT_TYPE_JSON, EXT_TYPE_UUID, EXT_TYPE_INET4, EXT_TYPE_INET6, EXT_TYPE_POINT, EXT_TYPE_MULTIPOINT, EXT_TYPE_LINESTRING, EXT_TYPE_MULTILINESTRING, EXT_TYPE_POLYGON, EXT_TYPE_MULTIPOLYGON, EXT_TYPE_GEOMETRYCOLLECTION }; enum enum_result_format { RESULT_TUPLE= 0, RESULT_NAMED_TUPLE, RESULT_DICTIONARY }; enum enum_dyncol_type { DYNCOL_LIST= 1, DYNCOL_TUPLE, DYNCOL_SET, DYNCOL_DICT, DYNCOL_ODICT, DYNCOL_LAST }; enum enum_tpc_state { TPC_STATE_NONE= 0, TPC_STATE_XID, TPC_STATE_PREPARE }; enum enum_paramstyle { NONE= 0, QMARK= 1, FORMAT= 2, PYFORMAT= 3 }; typedef struct st_ext_field_type { enum enum_extended_field_type ext_type; MARIADB_CONST_STRING str; } Mrdb_ExtFieldType; typedef struct st_parser { MrdbString statement; MrdbString *keys; uint8_t in_literal[3]; uint8_t in_comment; uint8_t in_values; uint8_t is_insert; uint8_t comment_eol; uint32_t param_count; uint32_t key_count; char* value_ofs; PyObject *param_list; enum enum_paramstyle paramstyle; enum enum_binary_command command; MYSQL *mysql; } MrdbParser; /* PEP-249: Connection object */ typedef struct { PyObject_HEAD PyThreadState *thread_state; MYSQL *mysql; int open; uint8_t is_buffered; uint8_t is_closed; enum enum_tpc_state tpc_state; char xid[150]; /* large enough, to hold 2 * MAX_TPC_XID size + integer value */ PyObject *dsn; /* always null */ const char *host; /* const char *tls_cipher; const char *tls_version; const char *unix_socket; int port; const char *charset; const char *collation; */ uint8_t inuse; uint8_t status; uint8_t asynchronous; struct timespec last_used; char *server_info; uint8_t closed; #if MARIADB_PACKAGE_VERSION_ID > 30301 PyObject *status_callback; #endif PyObject *last_executed_stmt; PyObject *converter; } MrdbConnection; typedef struct { enum enum_field_types type; PyObject *Value; uint8_t indicator; } Mariadb_Value; /* Parameter info for cursor.executemany() operations */ typedef struct { enum enum_field_types type; size_t bits; /* for PyLong Object */ PyTypeObject *ob_type; uint8_t has_indicator; } MrdbParamInfo; typedef struct { PyObject *value; char indicator; enum enum_field_types type; size_t length; uint8_t free_me; void *buffer; unsigned char num[8]; MYSQL_TIME tm; } MrdbParamValue; typedef struct { char *statement; Py_ssize_t statement_len; enum enum_paramstyle paramstyle; enum enum_binary_command command; uint32_t paramcount; uint8_t is_text; PyObject *paramlist; PyObject *keys; } MrdbParseInfo; /* PEP-249: Cursor object */ typedef struct { PyObject_HEAD MrdbConnection *connection; MYSQL_STMT *stmt; MYSQL_RES *result; PyObject *data; uint32_t array_size; uint32_t row_array_size; /* for fetch many */ MrdbParamInfo *paraminfo; MrdbParamValue *value; MYSQL_BIND *params; MYSQL_BIND *bind; MYSQL_FIELD *fields; char *statement; size_t statement_len; PyObject **values; PyStructSequence_Field *sequence_fields; PyTypeObject *sequence_type; MrdbParseInfo parseinfo; unsigned long prefetch_rows; unsigned long cursor_type; int64_t affected_rows; uint32_t field_count; int64_t row_count; uint64_t lastrow_id; unsigned long row_number; enum enum_result_format result_format; uint8_t is_prepared; char is_buffered; uint8_t fetched; uint8_t closed; uint8_t reprepare; enum enum_paramstyle paramstyle; } MrdbCursor; typedef struct { PyObject_HEAD } Mariadb_Fieldinfo; typedef struct { ps_field_fetch_func func; int pack_len; unsigned long max_len; } Mariadb_Conversion; extern char *dsn_keys[]; /* Exceptions */ extern PyObject *Mariadb_InterfaceError; extern PyObject *Mariadb_Error; extern PyObject *Mariadb_DatabaseError; extern PyObject *Mariadb_DataError; extern PyObject *Mariadb_PoolError; extern PyObject *Mariadb_OperationalError; extern PyObject *Mariadb_IntegrityError; extern PyObject *Mariadb_InternalError; extern PyObject *Mariadb_ProgrammingError; extern PyObject *Mariadb_NotSupportedError; extern PyObject *Mariadb_Warning; extern PyObject *decimal_module, *decimal_type; /* Object types */ extern PyTypeObject MrdbPool_Type; extern PyTypeObject Mariadb_Fieldinfo_Type; extern PyTypeObject MrdbConnection_Type; extern PyTypeObject MrdbCursor_Type; PyObject *ListOrTuple_GetItem(PyObject *obj, Py_ssize_t index); int Mariadb_traverse(PyObject *self, visitproc visit, void *arg); /* Function prototypes */ void mariadb_throw_exception(void *handle, PyObject *execption_type, int8_t is_statement, const char *message, ...); Mrdb_ExtFieldType *mariadb_extended_field_type(const MYSQL_FIELD *field); PyObject * MrdbConnection_ping(MrdbConnection *self); PyObject * MrdbConnection_kill(MrdbConnection *self, PyObject *args); PyObject * MrdbConnection_reconnect(MrdbConnection *self); PyObject * MrdbConnection_reset(MrdbConnection *self); PyObject * MrdbConnection_autocommit(MrdbConnection *self, PyObject *args); PyObject * MrdbConnection_change_user(MrdbConnection *self, PyObject *args); PyObject *MrdbConnection_rollback(MrdbConnection *self); PyObject * MrdbConnection_commit(MrdbConnection *self); PyObject * MrdbConnection_close(MrdbConnection *self); PyObject * MrdbConnection_connect( PyObject *self,PyObject *args, PyObject *kwargs); void MrdbConnection_SetAttributes(MrdbConnection *self); /* TPC methods */ PyObject * MrdbConnection_xid(MrdbConnection *self, PyObject *args); PyObject * MrdbConnection_tpc_begin(MrdbConnection *self, PyObject *args); PyObject * MrdbConnection_tpc_commit(MrdbConnection *self, PyObject *args); PyObject * MrdbConnection_tpc_rollback(MrdbConnection *self, PyObject *args); PyObject * MrdbConnection_tpc_prepare(MrdbConnection *self); PyObject * MrdbConnection_tpc_recover(MrdbConnection *self); /* codecs prototypes */ uint8_t mariadb_check_bulk_parameters(MrdbCursor *self, PyObject *data); uint8_t mariadb_check_execute_parameters(MrdbCursor *self, PyObject *data); uint8_t mariadb_param_update(void *data, MYSQL_BIND *bind, uint32_t row_nr); /* parser prototypes */ MrdbParser * MrdbParser_init(MYSQL *mysql, const char *statement, size_t length); void MrdbParser_end(MrdbParser *p); uint8_t MrdbParser_parse(MrdbParser *p, uint8_t is_batch, char *errmsg, size_t errmsg_len); /* Global defines */ #define MARIADB_PY_APILEVEL "2.0" #define MARIADB_PY_PARAMSTYLE "qmark" #define MARIADB_PY_THREADSAFETY 1 #define MAX_POOL_SIZE 64 #define TIMEDIFF(a,b)\ ((a).tv_sec * (uint64_t)1E09 + (a).tv_nsec) -\ ((b).tv_sec * (uint64_t)1E09 + (b).tv_nsec) /* Helper macros */ #define MrdbIndicator_Check(a)\ (PyObject_HasAttrString(a, "indicator")) #define MARIADB_CHECK_CONNECTION(connection, ret)\ if (!(connection) || !(connection)->mysql)\ {\ mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0, \ "Invalid connection or not connected");\ return (ret);\ } #define MARIADB_CHECK_TPC(connection)\ if (connection->tpc_state == TPC_STATE_NONE)\ {\ mariadb_throw_exception(connection->mysql, Mariadb_ProgrammingError, 0,\ "Transaction not started");\ return NULL;\ } #define MARIADB_FREE_MEM(a)\ if (a)\ {\ PyMem_RawFree((a));\ (a)= NULL;\ } #define MARIADB_CHECK_STMT(cursor)\ if (!cursor->connection->mysql || cursor->closed)\ {\ (cursor)->closed= 1;\ mariadb_throw_exception(cursor->stmt, Mariadb_ProgrammingError, 1,\ "Invalid cursor or not connected");\ } #define MARIADB_CHECK_STMT_FETCH(cursor)\ if (cursor->closed || (!cursor->connection->mysql && !self->is_buffered))\ {\ (cursor)->closed= 1;\ mariadb_throw_exception(cursor->stmt, Mariadb_ProgrammingError, 1,\ "Invalid cursor or not connected");\ } // #define pooling_keywords "pool_name", "pool_size", "reset_session", "idle_timeout", "acquire_timeout" #define connection_keywords "dsn", "host", "user", "password", "database", "port", "socket",\ "connect_timeout", "read_timeout", "write_timeout",\ "local_infile", "compress", "init_command",\ "default_file", "default_group",\ "ssl_key", "ssl_ca", "ssl_cert", "ssl_crl",\ "ssl_cipher", "ssl_capath", "ssl_crlpath",\ "ssl_verify_cert", "ssl",\ "client_flags", "charset" /* MariaDB protocol macros */ #define int1store(T,A) *((int8_t*) (T)) = (A) #define uint1korr(A) (*(((uint8_t*)(A)))) #if defined(__i386__) || defined(_WIN32) #define sint2korr(A) (*((int16_t *) (A))) #define sint3korr(A) ((int32_t) ((((unsigned char) (A)[2]) & 128) ? \ (((uint32_t) 255L << 24) | \ (((uint32_t) (unsigned char) (A)[2]) << 16) |\ (((uint32_t) (unsigned char) (A)[1]) << 8) | \ ((uint32_t) (unsigned char) (A)[0])) : \ (((uint32_t) (unsigned char) (A)[2]) << 16) |\ (((uint32_t) (unsigned char) (A)[1]) << 8) | \ ((uint32_t) (unsigned char) (A)[0]))) #define sint4korr(A) (*((long *) (A))) #define uint2korr(A) (*((uint16_t *) (A))) #if defined(HAVE_purify) && !defined(_WIN32) #define uint3korr(A) (uint32_t) (((uint32_t) ((unsigned char) (A)[0])) +\ (((uint32_t) ((unsigned char) (A)[1])) << 8) +\ (((uint32_t) ((unsigned char) (A)[2])) << 16)) #else /* ATTENTION ! Please, note, uint3korr reads 4 bytes (not 3) ! It means, that you have to provide enough allocated space ! */ #define uint3korr(A) (long) (*((unsigned int *) (A)) & 0xFFFFFF) #endif /* HAVE_purify && !_WIN32 */ #define uint4korr(A) (*((uint32_t *) (A))) #define uint5korr(A) ((unsigned long long)(((uint32_t) ((unsigned char) (A)[0])) +\ (((uint32_t) ((unsigned char) (A)[1])) << 8) +\ (((uint32_t) ((unsigned char) (A)[2])) << 16) +\ (((uint32_t) ((unsigned char) (A)[3])) << 24)) +\ (((unsigned long long) ((unsigned char) (A)[4])) << 32)) #define uint6korr(A) ((unsigned long long)(((uint32_t) ((unsigned char) (A)[0])) + \ (((uint32_t) ((unsigned char) (A)[1])) << 8) + \ (((uint32_t) ((unsigned char) (A)[2])) << 16) + \ (((uint32_t) ((unsigned char) (A)[3])) << 24)) + \ (((unsigned long long) ((unsigned char) (A)[4])) << 32) + \ (((unsigned long long) ((unsigned char) (A)[5])) << 40)) #define uint8_tkorr(A) (*((unsigned long long *) (A))) #define sint8korr(A) (*((long long *) (A))) #define int2store(T,A) *((uint16_t*) (T))= (uint16_t) (A) #define int3store(T,A) do { *(T)= (unsigned char) ((A));\ *(T+1)=(unsigned char) (((uint) (A) >> 8));\ *(T+2)=(unsigned char) (((A) >> 16)); } while (0) #define int4store(T,A) *((long *) (T))= (long) (A) #define int5store(T,A) do { *(T)= (unsigned char)((A));\ *((T)+1)=(unsigned char) (((A) >> 8));\ *((T)+2)=(unsigned char) (((A) >> 16));\ *((T)+3)=(unsigned char) (((A) >> 24)); \ *((T)+4)=(unsigned char) (((A) >> 32)); } while(0) #define int6store(T,A) do { *(T)= (unsigned char)((A)); \ *((T)+1)=(unsigned char) (((A) >> 8)); \ *((T)+2)=(unsigned char) (((A) >> 16)); \ *((T)+3)=(unsigned char) (((A) >> 24)); \ *((T)+4)=(unsigned char) (((A) >> 32)); \ *((T)+5)=(unsigned char) (((A) >> 40)); } while(0) #define int8store(T,A) *((unsigned long long *) (T))= (unsigned long long) (A) typedef union { double v; long m[2]; } doubleget_union; #define doubleget(V,M) \ do { doubleget_union _tmp; \ _tmp.m[0] = *((long*)(M)); \ _tmp.m[1] = *(((long*) (M))+1); \ (V) = _tmp.v; } while(0) #define doublestore(T,V) do { *((long *) T) = ((doubleget_union *)&V)->m[0]; \ *(((long *) T)+1) = ((doubleget_union *)&V)->m[1]; \ } while (0) #define float4get(V,M) do { *((float *) &(V)) = *((float*) (M)); } while(0) #define float8get(V,M) doubleget((V),(M)) #define float4store(V,M) memcpy((unsigned char*) V,(unsigned char*) (&M),sizeof(float)) #define floatstore(T,V) memcpy((unsigned char*)(T), (unsigned char*)(&V),sizeof(float)) #define floatget(V,M) memcpy((unsigned char*) &V,(unsigned char*) (M),sizeof(float)) #define float8store(V,M) doublestore((V),(M)) #else /* We're here if it's not a IA-32 architecture (Win32 and UNIX IA-32 defines were done before) */ #define sint2korr(A) (int16_t) (((int16_t) ((unsigned char) (A)[0])) +\ ((int16_t) ((int16_t) (A)[1]) << 8)) #define sint3korr(A) ((int32_t) ((((unsigned char) (A)[2]) & 128) ? \ (((uint32_t) 255L << 24) | \ (((uint32_t) (unsigned char) (A)[2]) << 16) |\ (((uint32_t) (unsigned char) (A)[1]) << 8) | \ ((uint32_t) (unsigned char) (A)[0])) : \ (((uint32_t) (unsigned char) (A)[2]) << 16) |\ (((uint32_t) (unsigned char) (A)[1]) << 8) | \ ((uint32_t) (unsigned char) (A)[0]))) #define sint4korr(A) (int32_t) (((int32_t) ((unsigned char) (A)[0])) +\ (((int32_t) ((unsigned char) (A)[1]) << 8)) +\ (((int32_t) ((unsigned char) (A)[2]) << 16)) +\ (((int32_t) ((int16_t) (A)[3]) << 24))) #define sint8korr(A) (long long) uint8korr(A) #define uint2korr(A) (uint16_t) (((uint16_t) ((unsigned char) (A)[0])) +\ ((uint16_t) ((unsigned char) (A)[1]) << 8)) #define uint3korr(A) (uint32_t) (((uint32_t) ((unsigned char) (A)[0])) +\ (((uint32_t) ((unsigned char) (A)[1])) << 8) +\ (((uint32_t) ((unsigned char) (A)[2])) << 16)) #define uint4korr(A) (uint32_t) (((uint32_t) ((unsigned char) (A)[0])) +\ (((uint32_t) ((unsigned char) (A)[1])) << 8) +\ (((uint32_t) ((unsigned char) (A)[2])) << 16) +\ (((uint32_t) ((unsigned char) (A)[3])) << 24)) #define uint5korr(A) ((unsigned long long)(((uint32_t) ((unsigned char) (A)[0])) +\ (((uint32_t) ((unsigned char) (A)[1])) << 8) +\ (((uint32_t) ((unsigned char) (A)[2])) << 16) +\ (((uint32_t) ((unsigned char) (A)[3])) << 24)) +\ (((unsigned long long) ((unsigned char) (A)[4])) << 32)) #define uint6korr(A) ((unsigned long long)(((uint32_t) ((unsigned char) (A)[0])) + \ (((uint32_t) ((unsigned char) (A)[1])) << 8) + \ (((uint32_t) ((unsigned char) (A)[2])) << 16) + \ (((uint32_t) ((unsigned char) (A)[3])) << 24)) + \ (((unsigned long long) ((unsigned char) (A)[4])) << 32) + \ (((unsigned long long) ((unsigned char) (A)[5])) << 40)) #define uint8korr(A) ((unsigned long long)(((uint32_t) ((unsigned char) (A)[0])) +\ (((uint32_t) ((unsigned char) (A)[1])) << 8) +\ (((uint32_t) ((unsigned char) (A)[2])) << 16) +\ (((uint32_t) ((unsigned char) (A)[3])) << 24)) +\ (((unsigned long long) (((uint32_t) ((unsigned char) (A)[4])) +\ (((uint32_t) ((unsigned char) (A)[5])) << 8) +\ (((uint32_t) ((unsigned char) (A)[6])) << 16) +\ (((uint32_t) ((unsigned char) (A)[7])) << 24))) <<\ 32)) #define int2store(T,A) do { uint def_temp= (uint) (A) ;\ *((unsigned char*) (T))= (unsigned char)(def_temp); \ *((unsigned char*) (T)+1)=(unsigned char)((def_temp >> 8)); \ } while(0) #define int3store(T,A) do { /*lint -save -e734 */\ *((unsigned char*)(T))=(unsigned char) ((A));\ *((unsigned char*) (T)+1)=(unsigned char) (((A) >> 8));\ *((unsigned char*)(T)+2)=(unsigned char) (((A) >> 16)); \ /*lint -restore */} while(0) #define int4store(T,A) do { *((char *)(T))=(char) ((A));\ *(((char *)(T))+1)=(char) (((A) >> 8));\ *(((char *)(T))+2)=(char) (((A) >> 16));\ *(((char *)(T))+3)=(char) (((A) >> 24)); } while(0) #define int5store(T,A) do { *((char *)(T))= (char)((A)); \ *(((char *)(T))+1)= (char)(((A) >> 8)); \ *(((char *)(T))+2)= (char)(((A) >> 16)); \ *(((char *)(T))+3)= (char)(((A) >> 24)); \ *(((char *)(T))+4)= (char)(((A) >> 32)); \ } while(0) #define int6store(T,A) do { *((char *)(T))= (char)((A)); \ *(((char *)(T))+1)= (char)(((A) >> 8)); \ *(((char *)(T))+2)= (char)(((A) >> 16)); \ *(((char *)(T))+3)= (char)(((A) >> 24)); \ *(((char *)(T))+4)= (char)(((A) >> 32)); \ *(((char *)(T))+5)= (char)(((A) >> 40)); \ } while(0) #define int8store(T,A) do { uint def_temp= (uint) (A), def_temp2= (uint) ((A) >> 32); \ int4store((T),def_temp); \ int4store((T+4),def_temp2); } while(0) #ifdef WORDS_BIGENDIAN #define float4store(T,A) do { *(T)= ((unsigned char *) &A)[3];\ *((T)+1)=(char) ((unsigned char *) &A)[2];\ *((T)+2)=(char) ((unsigned char *) &A)[1];\ *((T)+3)=(char) ((unsigned char *) &A)[0]; } while(0) #define float4get(V,M) do { float def_temp;\ ((unsigned char*) &def_temp)[0]=(M)[3];\ ((unsigned char*) &def_temp)[1]=(M)[2];\ ((unsigned char*) &def_temp)[2]=(M)[1];\ ((unsigned char*) &def_temp)[3]=(M)[0];\ (V)=def_temp; } while(0) #define float8store(T,V) do { *(T)= ((unsigned char *) &V)[7];\ *((T)+1)=(char) ((unsigned char *) &V)[6];\ *((T)+2)=(char) ((unsigned char *) &V)[5];\ *((T)+3)=(char) ((unsigned char *) &V)[4];\ *((T)+4)=(char) ((unsigned char *) &V)[3];\ *((T)+5)=(char) ((unsigned char *) &V)[2];\ *((T)+6)=(char) ((unsigned char *) &V)[1];\ *((T)+7)=(char) ((unsigned char *) &V)[0]; } while(0) #define float8get(V,M) do { double def_temp;\ ((unsigned char*) &def_temp)[0]=(M)[7];\ ((unsigned char*) &def_temp)[1]=(M)[6];\ ((unsigned char*) &def_temp)[2]=(M)[5];\ ((unsigned char*) &def_temp)[3]=(M)[4];\ ((unsigned char*) &def_temp)[4]=(M)[3];\ ((unsigned char*) &def_temp)[5]=(M)[2];\ ((unsigned char*) &def_temp)[6]=(M)[1];\ ((unsigned char*) &def_temp)[7]=(M)[0];\ (V) = def_temp; } while(0) #else #define float4get(V,M) memcpy(&V, (M), sizeof(float)) #define float4store(V,M) memcpy(V, (&M), sizeof(float)) #if defined(__FLOAT_WORD_ORDER) && (__FLOAT_WORD_ORDER == __BIG_ENDIAN) #define doublestore(T,V) do { *(((char*)T)+0)=(char) ((unsigned char *) &V)[4];\ *(((char*)T)+1)=(char) ((unsigned char *) &V)[5];\ *(((char*)T)+2)=(char) ((unsigned char *) &V)[6];\ *(((char*)T)+3)=(char) ((unsigned char *) &V)[7];\ *(((char*)T)+4)=(char) ((unsigned char *) &V)[0];\ *(((char*)T)+5)=(char) ((unsigned char *) &V)[1];\ *(((char*)T)+6)=(char) ((unsigned char *) &V)[2];\ *(((char*)T)+7)=(char) ((unsigned char *) &V)[3]; }\ while(0) #define doubleget(V,M) do { double def_temp;\ ((unsigned char*) &def_temp)[0]=(M)[4];\ ((unsigned char*) &def_temp)[1]=(M)[5];\ ((unsigned char*) &def_temp)[2]=(M)[6];\ ((unsigned char*) &def_temp)[3]=(M)[7];\ ((unsigned char*) &def_temp)[4]=(M)[0];\ ((unsigned char*) &def_temp)[5]=(M)[1];\ ((unsigned char*) &def_temp)[6]=(M)[2];\ ((unsigned char*) &def_temp)[7]=(M)[3];\ (V) = def_temp; } while(0) #endif /* __FLOAT_WORD_ORDER */ #define float8get(V,M) doubleget((V),(M)) #define float8store(V,M) doublestore((V),(M)) #endif /* WORDS_BIGENDIAN */ #ifdef HAVE_BIGENDIAN #define ushortget(V,M) do { V = (uint16_t) (((uint16_t) ((unsigned char) (M)[1]))+\ ((uint16_t) ((uint16_t) (M)[0]) << 8)); } while(0) #define shortget(V,M) do { V = (short) (((short) ((unsigned char) (M)[1]))+\ ((short) ((short) (M)[0]) << 8)); } while(0) #define longget(V,M) do { int32 def_temp;\ ((unsigned char*) &def_temp)[0]=(M)[0];\ ((unsigned char*) &def_temp)[1]=(M)[1];\ ((unsigned char*) &def_temp)[2]=(M)[2];\ ((unsigned char*) &def_temp)[3]=(M)[3];\ (V)=def_temp; } while(0) #define ulongget(V,M) do { uint32 def_temp;\ ((unsigned char*) &def_temp)[0]=(M)[0];\ ((unsigned char*) &def_temp)[1]=(M)[1];\ ((unsigned char*) &def_temp)[2]=(M)[2];\ ((unsigned char*) &def_temp)[3]=(M)[3];\ (V)=def_temp; } while(0) #define shortstore(T,A) do { uint def_temp=(uint) (A) ;\ *(((char*)T)+1)=(char)(def_temp); \ *(((char*)T)+0)=(char)(def_temp >> 8); } while(0) #define longstore(T,A) do { *(((char*)T)+3)=((A));\ *(((char*)T)+2)=(((A) >> 8));\ *(((char*)T)+1)=(((A) >> 16));\ *(((char*)T)+0)=(((A) >> 24)); } while(0) #define floatget(V,M) memcpy(&V, (M), sizeof(float)) #define floatstore(T,V) memcpy((T), (void*) (&V), sizeof(float)) #define doubleget(V,M) memcpy(&V, (M), sizeof(double)) #define doublestore(T,V) memcpy((T), (void *) &V, sizeof(double)) #define longlongget(V,M) memcpy(&V, (M), sizeof(unsigned long long)) #define longlongstore(T,V) memcpy((T), &V, sizeof(unsigned long long)) #else #define ushortget(V,M) do { V = uint2korr(M); } while(0) #define shortget(V,M) do { V = sint2korr(M); } while(0) #define longget(V,M) do { V = sint4korr(M); } while(0) #define ulongget(V,M) do { V = uint4korr(M); } while(0) #define shortstore(T,V) int2store(T,V) #define longstore(T,V) int4store(T,V) #ifndef floatstore #define floatstore(T,V) memcpy((T), (void *) (&V), sizeof(float)) #define floatget(V,M) memcpy(&V, (M), sizeof(float)) #endif #ifndef doubleget #define doubleget(V,M) memcpy(&V, (M), sizeof(double)) #define doublestore(T,V) memcpy((T), (void *) &V, sizeof(double)) #endif /* doubleget */ #define longlongget(V,M) memcpy(&V, (M), sizeof(unsigned long long)) #define longlongstore(T,V) memcpy((T), &V, sizeof(unsigned long long)) #endif /* WORDS_BIGENDIAN */ #endif /* __i386__ OR _WIN32 */ /* Due to callback functions we cannot use PY_BEGIN/END_ALLOW_THREADS */ #define MARIADB_BEGIN_ALLOW_THREADS(obj)\ {\ (obj)->thread_state= PyEval_SaveThread();\ } #define MARIADB_END_ALLOW_THREADS(obj)\ if ((obj)->thread_state)\ {\ PyEval_RestoreThread((obj)->thread_state);\ (obj)->thread_state= NULL;\ } #define MARIADB_UNBLOCK_THREADS(obj)\ {\ if ((obj)->thread_state)\ {\ _save= (obj)->thread_state;\ PyEval_RestoreThread(_save);\ (obj)->thread_state= NULL;\ }\ } #define MARIADB_BLOCK_THREADS(obj)\ if (_save)\ {\ (obj)->thread_state= PyEval_SaveThread();\ _save= NULL;\ } mariadb-connector-python-1.1.10/mariadb/000077500000000000000000000000001456007477700201205ustar00rootroot00000000000000mariadb-connector-python-1.1.10/mariadb/__init__.py000066400000000000000000000145601456007477700222370ustar00rootroot00000000000000''' MariaDB Connector/Python module enables python programs to access MariaDB and MySQL databases, using an API which is compliant with the Python DB API 2.0 (PEP-249). ''' import mariadb from ._mariadb import ( DataError, DatabaseError, Error, IntegrityError, InterfaceError, InternalError, NotSupportedError, OperationalError, PoolError, ProgrammingError, Warning, mariadbapi_version, ) from .field import fieldinfo from mariadb.dbapi20 import * # noqa: F401,F403 from mariadb.connectionpool import * # noqa: F401,F403 from mariadb.cursors import Cursor from mariadb.release_info import __version__ as __version__ from mariadb.release_info import __version_info__ as __version_info__ from mariadb.release_info import __author__ as __author__ from mariadb.connections import Connection # disable for now, until tests are in place # from mariadb.pooling import * _POOLS = _CONNECTION_POOLS = {} __all__ = ["DataError", "DatabaseError", "Error", "IntegrityError", "InterfaceError", "InternalError", "NotSupportedError", "OperationalError", "PoolError", "ProgrammingError", "Warning", "Connection", "__version__", "__version_info__", "__author__", "Cursor", "fieldinfo"] def connect(*args, connectionclass=mariadb.connections.Connection, **kwargs): """ Creates a MariaDB Connection object. By default the standard connectionclass mariadb.connections.Connection will be created. Parameter connectionclass specifies a subclass of mariadb.Connection object. If not specified default will be used. This optional parameter was added in version 1.1.0. Connection parameters are provided as a set of keyword arguments: - host: The host name or IP address of the database server. If MariaDB Connector/Python was built with MariaDB Connector/C 3.3 it is also possible to provide a comma separated list of hosts for simple fail over in case of one or more hosts are not available. - user, username: The username used to authenticate with the database server - password, passwd: The password of the given user - database, db: database (schema) name to use when connecting with the database server - unix_socket: The location of the unix socket file to use instead of using an IP port to connect. If socket authentication is enabled, this can also be used in place of a password. - port: port number of the database server. If not specified the default value of 3306 will be used. - connect_timeout: connect timeout in seconds - read_timeout: read timeout in seconds - write_timeout: write timeout in seconds - local_infile: Enables or disables the use of LOAD DATA LOCAL INFILE statements. - compress= False: Uses the compressed protocol for client server communication. If the server doesn't support compressed protocol, the default protocol will be used. - init_command: Command(s) which will be executed when connecting and reconnecting to the database server - default_file: Read options from the specified option file. If the file is an empty string, default configuration file(s) will be used - default_group: Read options from the specified group - plugin_dir: Directory which contains MariaDB client plugins. - reconnect: Enables or disables automatic reconnect. Available since version 1.1.4 - ssl_key: Defines a path to a private key file to use for TLS. This option requires that you use the absolute path, not a relative path. The specified key must be in PEM format - ssl_cert: Defines a path to the X509 certificate file to use for TLS. This option requires that you use the absolute path, not a relative path. The X609 certificate must be in PEM format. - ssl_ca: Defines a path to a PEM file that should contain one or more X509 certificates for trusted Certificate Authorities (CAs) to use for TLS. This option requires that you use the absolute path, not a relative path. - ssl_capath: Defines a path to a directory that contains one or more PEM files that contains one X509 certificate for a trusted Certificate Authority (CA) - ssl_cipher: Defines a list of permitted cipher suites to use for TLS - ssl_crlpath: Defines a path to a PEM file that should contain one or more revoked X509 certificates to use for TLS. This option requires that you use the absolute path, not a relative path. - ssl_verify_cert: Enables server certificate verification. - ssl: The connection must use TLS security or it will fail. - tls_version: A comma-separated list (without whitespaces) of TLS versions. Valid versions are TLSv1.0, TLSv1.1,TLSv1.2 and TLSv1.3. Added in version 1.1.7. - autocommit=False: Specifies the autocommit settings. True will enable autocommit, False will disable it (default). - converter: Specifies a conversion dictionary, where keys are FIELD_TYPE values and values are conversion functions """ if kwargs: if "pool_name" in kwargs: if not kwargs["pool_name"] in mariadb._CONNECTION_POOLS: pool = mariadb.ConnectionPool(**kwargs) else: pool = mariadb._CONNECTION_POOLS[kwargs["pool_name"]] c = pool.get_connection() return c connection = connectionclass(*args, **kwargs) if not isinstance(connection, mariadb.connections.Connection): raise mariadb.ProgrammingError("%s is not an instance of " "mariadb.Connection" % connection) return connection client_version_info = tuple(int(x, 10) for x in mariadbapi_version.split('.')) client_version = client_version_info[0] * 10000 +\ client_version_info[1] * 1000 + client_version_info[2] mariadb-connector-python-1.1.10/mariadb/connectionpool.py000066400000000000000000000262501456007477700235300ustar00rootroot00000000000000# # Copyright (C) 2020-2021 Georg Richter and MariaDB Corporation AB # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # import mariadb import _thread import time from mariadb.constants import STATUS MAX_POOL_SIZE = 64 class ConnectionPool(object): """ Class defining a pool of database connections MariaDB Connector/Python supports simple connection pooling. A connection pool holds a number of open connections and handles thread safety when providing connections to threads. The size of a connection pool is configurable at creation time, but cannot be changed afterwards. The maximum size of a connection pool is limited to 64 connections. Keyword Arguments: * pool_name (str) -- Name of connection pool * pool_size (int)=5 -- Size of pool. If not specified default value of 5 will be used. Maximum allowed number is 64. * pool_reset_connection (bool)=True -- Will reset the connection before returning it to the pool. Default value is True. * pool_validation_interval (int)=500 -- Specifies the validation interval in milliseconds after which the status of a connection requested from the pool is checked. The default values is 500 milliseconds, a value of 0 means that the status will always be checked. (Added in version 1.1.6) """ def __init__(self, *args, **kwargs): """ Creates a connection pool class :param str pool_name: Name of connection pool :param int pool_size: Size of pool. If not specified default value of 5 will be used. Maximum allowed number is 64. :param bool pool_reset_connection: Will reset the connection before returning it to the pool. Default value is True. """ self._connections_free = [] self._connections_used = [] self._pool_args = {} self._conn_args = {} self._lock_pool = _thread.RLock() self.__closed = 0 key_words = ["pool_name", "pool_size", "pool_reset_connection", "pool_validation_interval"] # check if pool_name was provided if kwargs and "pool_name" in kwargs: # check if pool_name already exists if kwargs["pool_name"] in mariadb._CONNECTION_POOLS: raise mariadb.ProgrammingError("Pool '%s' already exists" % kwargs["pool_name"]) else: raise mariadb.ProgrammingError("No pool name specified") # save pool keyword arguments self._pool_args["name"] = kwargs.get("pool_name") self._pool_args["size"] = int(kwargs.get("pool_size", 5)) self._pool_args["reset_connection"] = \ bool(kwargs.get("pool_reset_connection", True)) self._pool_args["validation_interval"] = \ int(kwargs.get("pool_validation_interval", 500)) # validate pool size (must be in range between 1 and MAX_POOL_SIZE) if not (0 < self._pool_args["size"] <= MAX_POOL_SIZE): raise mariadb.ProgrammingError("Pool size must be in range of " "1 and %s" % MAX_POOL_SIZE) # store pool and connection arguments self._conn_args = kwargs.copy() for key in key_words: if key in self._conn_args: del self._conn_args[key] if len(self._conn_args) > 0: with self._lock_pool: # fill connection pool for i in range(0, self._pool_args["size"]): try: connection = mariadb.Connection(**self._conn_args) except mariadb.Error: # if an error occurred, close all connections # and raise exception for j in range(0, len(self._connections_free)): try: self._connections_free[j].close() except mariadb.Error: # connect failed, so we are not # interested in errors # from close() method pass del self._connections_free[j] raise self.add_connection(connection) # store connection pool in _CONNECTION_POOLS mariadb._CONNECTION_POOLS[self._pool_args["name"]] = self def _replace_connection(self, connection): """ Removes the given connection and adds a new connection. """ if connection: if connection in self._connections_free: x = self._connections_free.index(connection) del self._connections_free[x] elif connection in self._connections_used: x = self._connections_used.index(connection) del self._connections_used[x] connection._Connection__pool = None connection.close() return self.add_connection() def __repr__(self): if (self.__closed): return "" % (hex(id(self)),) else: return "" % (self.pool_name, hex(id(self))) def add_connection(self, connection=None): """ Adds a connection object to the connection pool. In case that the pool doesn’t have a free slot or is not configured a PoolError exception will be raised. """ if not self._conn_args: raise mariadb.PoolError("Couldn't get configuration for pool %s" % self._pool_args["name"]) if (connection is not None and not isinstance(connection, mariadb.connections.Connection)): raise mariadb.ProgrammingError("Passed parameter is not a " "connection object") if connection is None and len(self._conn_args) == 0: raise mariadb.PoolError("Can't get configuration for pool %s" % self._pool_args["name"]) total = len(self._connections_free + self._connections_used) if total >= self._pool_args["size"]: raise mariadb.PoolError("Can't add connection to pool %s: " "No free slot available (%s)." % (self._pool_args["name"], total)) with self._lock_pool: if connection is None: connection = mariadb.Connection(**self._conn_args) connection._Connection__pool = self connection.__last_used = time.perf_counter_ns() self._connections_free.append(connection) return connection def get_connection(self): """ Returns a connection from the connection pool or raises a PoolError exception if a connection is not available. """ conn = None with self._lock_pool: for i in range(0, len(self._connections_free)): conn = self._connections_free[i] dt = (time.perf_counter_ns() - conn.__last_used) / 1000000 if dt > self._pool_args["validation_interval"]: try: conn.ping() except mariadb.Error: conn = self._replace_connection(conn) if not conn: continue conn._used += 1 self._connections_used.append(conn) idx = self._connections_free.index(conn) del self._connections_free[idx] return conn raise mariadb.PoolError("No connection available") def _close_connection(self, connection): """ Returns connection to the pool. Internally used by connection object. """ with self._lock_pool: try: if self._pool_args["reset_connection"]: connection.reset() elif connection.server_status & STATUS.IN_TRANS: connection.rollback() except mariadb.Error: self._replace_connection(connection) if connection: if connection in self._connections_used: x = self._connections_used.index(connection) del self._connections_used[x] connection.__last_used = time.perf_counter_ns() self._connections_free.append(connection) def set_config(self, **kwargs): """ Sets the connection configuration for the connection pool. For valid connection arguments check the mariadb.connect() method. Note: This method doesn't create connections in the pool. To fill the pool one has to use add_connection() ḿethod. """ self._conn_args = kwargs def close(self): """Closes connection pool and all connections.""" try: for c in (self._connections_free + self._connections_used): c._Connection__pool = None c.close() finally: self._connections_free = None self._connections_used = None del mariadb._CONNECTION_POOLS[self._pool_args["name"]] @property def pool_name(self): """Returns the name of the connection pool.""" return self._pool_args["name"] @property def pool_size(self): """Returns the size of the connection pool.""" return self._pool_args["size"] @property def max_size(self): "Returns the maximum size for connection pools.""" return MAX_POOL_SIZE @property def connection_count(self): "Returns the number of connections in connection pool.""" try: return len(self._connections_free + self._connections_used) except Exception: return 0 @property def pool_reset_connection(self): """ If set to true, the connection will be reset on both client and server side after .close() method was called """ return self._pool_args["reset_connection"] @pool_reset_connection.setter def pool_reset_connection(self, reset): self._pool_args["reset_connection"] = reset mariadb-connector-python-1.1.10/mariadb/connections.py000066400000000000000000000544371456007477700230310ustar00rootroot00000000000000# # Copyright (C) 2020-2021 Georg Richter and MariaDB Corporation AB # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # import mariadb import socket import mariadb.cursors from mariadb.constants import STATUS, TPC_STATE, INFO from packaging import version _DEFAULT_CHARSET = "utf8mb4" _DEFAULT_COLLATION = "utf8mb4_general_ci" _MAX_TPC_XID_SIZE = 64 class Connection(mariadb._mariadb.connection): """ MariaDB Connector/Python Connection Object Handles the connection to a MariaDB or MySQL database server. It encapsulates a database session. Connections are created using the method mariadb.connect() """ def _check_closed(self): if self._closed: raise mariadb.ProgrammingError("Invalid connection or " "not connected") def __init__(self, *args, **kwargs): """ Establishes a connection to a database server and returns a connection object. """ self._socket = None self._used = 0 self._last_executed_statement = None self._socket = None self.__pool = None self.__last_used = 0 self.tpc_state = TPC_STATE.NONE self._xid = None autocommit = kwargs.pop("autocommit", False) reconnect = kwargs.pop("reconnect", False) self._converter = kwargs.pop("converter", None) # if host contains a connection string or multiple hosts, # we need to check if it's supported by Connector/C if "host" in kwargs: host = kwargs.get("host") if version.Version(mariadb.mariadbapi_version) <\ version.Version('3.3.0') and ',' in host: raise mariadb.ProgrammingError("Host failover list requires " "MariaDB Connector/C 3.3.0 " "or newer") # compatibility feature: if SSL is provided as a dictionary, # we will map it's content if "ssl" in kwargs and not isinstance(kwargs["ssl"], bool): ssl = kwargs.pop("ssl", None) for key in ["ca", "cert", "capath", "key", "cipher"]: if key in ssl: kwargs["ssl_%s" % key] = ssl[key] kwargs["ssl"] = True super().__init__(*args, **kwargs) self.autocommit = autocommit self.auto_reconnect = reconnect def cursor(self, cursorclass=mariadb.cursors.Cursor, **kwargs): """ Returns a new cursor object for the current connection. If no cursorclass was specified, a cursor with default mariadb.Cursor class will be created. Optional keyword parameters: - buffered = True If set to False the result will be unbuffered, which means before executing another statement with the same connection the entire result set must be fetched. Please note that the default was False for MariaDB Connector/Python versions < 1.1.0. - dictionary = False Return fetch values as dictionary. - named_tuple = False Return fetch values as named tuple. This feature exists for compatibility reasons and should be avoided due to possible inconsistency. - cursor_type = CURSOR.NONE If cursor_type is set to CURSOR.READ_ONLY, a cursor is opened for the statement invoked with cursors execute() method. - prepared = False When set to True cursor will remain in prepared state after the first execute() method was called. Further calls to execute() method will ignore the sql statement. - binary = False Always execute statement in MariaDB client/server binary protocol. In versions prior to 1.1.0 results were unbuffered by default, which means before executing another statement with the same connection the entire result set must be fetched. fetch* methods of the cursor class by default return result set values as a tuple, unless named_tuple or dictionary was specified. The latter one exists for compatibility reasons and should be avoided due to possible inconsistency in case two or more fields in a result set have the same name. If cursor_type is set to CURSOR.READ_ONLY, a cursor is opened for the statement invoked with cursors execute() method. """ self._check_closed() cursor = cursorclass(self, **kwargs) if not isinstance(cursor, mariadb._mariadb.cursor): raise mariadb.ProgrammingError("%s is not an instance of " "mariadb.cursor" % cursor) return cursor def close(self): self._check_closed() if self._Connection__pool: self._Connection__pool._close_connection(self) else: super().close() def __enter__(self): self._check_closed() "Returns a copy of the connection." return self def __exit__(self, exc_type, exc_val, exc_tb): self._check_closed() "Closes connection." self.close() def commit(self): """ Commit any pending transaction to the database. """ self._check_closed() if self.tpc_state > TPC_STATE.NONE: raise mariadb.ProgrammingError("commit() is not allowed if " "a TPC transaction is active") self._execute_command("COMMIT") self._read_response() def rollback(self): """ Causes the database to roll back to the start of any pending transaction Closing a connection without committing the changes first will cause an implicit rollback to be performed. Note that rollback() will not work as expected if autocommit mode was set to True or the storage engine does not support transactions." """ self._check_closed() if self.tpc_state > TPC_STATE.NONE: raise mariadb.ProgrammingError("rollback() is not allowed if a " "TPC transaction is active") self._execute_command("ROLLBACK") self._read_response() def kill(self, id: int): """ This function is used to ask the server to kill a database connection specified by the processid parameter. The connection id can be be retrieved by SHOW PROCESSLIST sql command. """ self._check_closed() if not isinstance(id, int): raise mariadb.ProgrammingError("id must be of type int.") stmt = "KILL %s" % id self._execute_command(stmt) self._read_response() def begin(self): """ Start a new transaction which can be committed by .commit() method, or cancelled by .rollback() method. """ self._check_closed() self._execute_command("BEGIN") self._read_response() def select_db(self, new_db: str): """ Gets the default database for the current connection. The default database can also be obtained or changed by database attribute. """ self._check_closed() self.database = new_db def get_server_version(self): """ Returns a tuple representing the version of the connected server in the following format: (MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION) """ return self.server_version_info def show_warnings(self): """ Shows error, warning and note messages from last executed command. """ self._check_closed() if (not self.warnings): return None cursor = self.cursor() cursor.execute("SHOW WARNINGS") ret = cursor.fetchall() del cursor return ret class xid(tuple): """ xid(format_id: int, global_transaction_id: str, branch_qualifier: str) Creates a transaction ID object suitable for passing to the .tpc_*() methods of this connection. Parameters: - format_id: Format id. If not set default value `0` will be used. - global_transaction_id: Global transaction qualifier, which must be unique. The maximum length of the global transaction id is limited to 64 characters. - branch_qualifier: Branch qualifier which represents a local transaction identifier. The maximum length of the branch qualifier is limited to 64 characters. """ def __new__(self, format_id, transaction_id, branch_qualifier): if not isinstance(format_id, int): raise mariadb.ProgrammingError("argument 1 must be int, " "not %s", type(format_id).__name__) if not isinstance(transaction_id, str): raise mariadb.ProgrammingError("argument 2 must be str, " "not %s", type(transaction_id).__mane__) if not isinstance(branch_qualifier, str): raise mariadb.ProgrammingError("argument 3 must be str, " "not %s", type(transaction_id).__name__) if len(transaction_id) > _MAX_TPC_XID_SIZE: raise mariadb.ProgrammingError("Maximum length of " "transaction_id exceeded.") if len(branch_qualifier) > _MAX_TPC_XID_SIZE: raise mariadb.ProgrammingError("Maximum length of " "branch_qualifier exceeded.") if format_id == 0: format_id = 1 return super().__new__(self, (format_id, transaction_id, branch_qualifier)) def tpc_begin(self, xid): """ Parameter: xid: xid object which was created by .xid() method of connection class Begins a TPC transaction with the given transaction ID xid. This method should be called outside of a transaction (i.e. nothing may have executed since the last .commit() or .rollback()). Furthermore, it is an error to call .commit() or .rollback() within the TPC transaction. A ProgrammingError is raised, if the application calls .commit() or .rollback() during an active TPC transaction. """ self._check_closed() if type(xid).__name__ != "xid": raise mariadb.ProgrammingError("argument 1 must be xid " "not %s", type(xid).__name__) stmt = "XA BEGIN '%s','%s',%s" % (xid[1], xid[2], xid[0]) try: self._execute_command(stmt) self._read_response() except mariadb.Error: raise self.tpc_state = TPC_STATE.XID self._xid = xid def tpc_commit(self, xid=None): """ Optional parameter:" xid: xid object which was created by .xid() method of connection class. When called with no arguments, .tpc_commit() commits a TPC transaction previously prepared with .tpc_prepare(). If .tpc_commit() is called prior to .tpc_prepare(), a single phase commit is performed. A transaction manager may choose to do this if only a single resource is participating in the global transaction. When called with a transaction ID xid, the database commits the given transaction. If an invalid transaction ID is provided, a ProgrammingError will be raised. This form should be called outside of a transaction, and is intended for use in recovery." """ self._check_closed() if not xid: xid = self._xid if self.tpc_state == TPC_STATE.NONE: raise mariadb.ProgrammingError("Transaction not started.") if xid is None and self.tpc_state != TPC_STATE.PREPARE: raise mariadb.ProgrammingError("Transaction is not prepared.") if xid and type(xid).__name__ != "xid": raise mariadb.ProgrammingError("argument 1 must be xid " "not %s" % type(xid).__name__) if self.tpc_state < TPC_STATE.PREPARE: stmt = "XA END '%s','%s',%s" % (xid[1], xid[2], xid[0]) self._execute_command(stmt) try: self._read_response() except mariadb.Error: self._xid = None self.tpc_state = TPC_STATE.NONE raise stmt = "XA COMMIT '%s','%s',%s" % (xid[1], xid[2], xid[0]) if self.tpc_state < TPC_STATE.PREPARE: stmt = stmt + " ONE PHASE" try: self._execute_command(stmt) self._read_response() except mariadb.Error: self._xid = None self.tpc_state = TPC_STATE.NONE raise # cleanup self._xid = None self.tpc_state = TPC_STATE.NONE def tpc_prepare(self): """ Performs the first phase of a transaction started with .tpc_begin(). A ProgrammingError will be raised if this method was called outside of a TPC transaction. After calling .tpc_prepare(), no statements can be executed until .tpc_commit() or .tpc_rollback() have been called. """ self._check_closed() if self.tpc_state == TPC_STATE.NONE: raise mariadb.ProgrammingError("Transaction not started.") if self.tpc_state == TPC_STATE.PREPARE: raise mariadb.ProgrammingError("Transaction is already in " "prepared state.") xid = self._xid stmt = "XA END '%s','%s',%s" % (xid[1], xid[2], xid[0]) try: self._execute_command(stmt) self._read_response() except mariadb.Error: self._xid = None self.tpc_state = TPC_STATE.NONE raise stmt = "XA PREPARE '%s','%s',%s" % (xid[1], xid[2], xid[0]) try: self._execute_command(stmt) self._read_response() except mariadb.Error: self._xid = None self.tpc_state = TPC_STATE.NONE raise self.tpc_state = TPC_STATE.PREPARE def tpc_rollback(self, xid=None): """ Parameter: xid: xid object which was created by .xid() method of connection class Performs the first phase of a transaction started with .tpc_begin(). A ProgrammingError will be raised if this method outside of a TPC transaction. After calling .tpc_prepare(), no statements can be executed until .tpc_commit() or .tpc_rollback() have been called. """ self._check_closed() if self.tpc_state == TPC_STATE.NONE: raise mariadb.ProgrammingError("Transaction not started.") if xid and type(xid).__name__ != "xid": raise mariadb.ProgrammingError("argument 1 must be xid " "not %s" % type(xid).__name__) if not xid: xid = self._xid if self.tpc_state < TPC_STATE.PREPARE: stmt = "XA END '%s','%s',%s" % (xid[1], xid[2], xid[0]) self._execute_command(stmt) try: self._read_response() except mariadb.Error: self._xid = None self.tpc_state = TPC_STATE.NONE raise stmt = "XA ROLLBACK '%s','%s',%s" % (xid[1], xid[2], xid[0]) try: self._execute_command(stmt) self._read_response() except mariadb.Error: self._xid = None self.tpc_state = TPC_STATE.NONE raise self.tpc_state = TPC_STATE.PREPARE def tpc_recover(self): """ Returns a list of pending transaction IDs suitable for use with tpc_commit(xid) or .tpc_rollback(xid). """ self._check_closed() cursor = self.cursor() cursor.execute("XA RECOVER") result = cursor.fetchall() del cursor return result @property def database(self): """Get default database for connection.""" self._check_closed() return self._mariadb_get_info(INFO.SCHEMA) @database.setter def database(self, schema): """Set default database.""" self._check_closed() try: self._execute_command("USE %s" % str(schema)) self._read_response() except mariadb.Error: raise @property def user(self): """ Returns the user name for the current connection or empty string if it can't be determined, e.g. when using socket authentication. """ self._check_closed() return self._mariadb_get_info(INFO.USER) @property def character_set(self): """ Client character set. For MariaDB Connector/Python it is always utf8mb4. """ return _DEFAULT_CHARSET @property def client_capabilities(self): """Client capability flags.""" self._check_closed() return self._mariadb_get_info(INFO.CLIENT_CAPABILITIES) @property def server_capabilities(self): """Server capability flags.""" self._check_closed() return self._mariadb_get_info(INFO.SERVER_CAPABILITIES) @property def extended_server_capabilities(self): """ Extended server capability flags (only for MariaDB database servers). """ self._check_closed() return self._mariadb_get_info(INFO.EXTENDED_SERVER_CAPABILITIES) @property def server_port(self): """ Database server TCP/IP port. This value will be 0 in case of a unix socket connection. """ self._check_closed() return self._mariadb_get_info(INFO.PORT) @property def unix_socket(self): """Unix socket name.""" self._check_closed() return self._mariadb_get_info(INFO.UNIX_SOCKET) @property def server_name(self): """Name or IP address of database server.""" self._check_closed() return self._mariadb_get_info(INFO.HOST) @property def collation(self): """Client character set collation""" return _DEFAULT_COLLATION @property def server_info(self): """Server version in alphanumerical format (str)""" self._check_closed() return self._mariadb_get_info(INFO.SERVER_VERSION) @property def tls_cipher(self): """TLS cipher suite if a secure connection is used.""" self._check_closed() return self._mariadb_get_info(INFO.SSL_CIPHER) @property def tls_version(self): """TLS protocol version if a secure connection is used.""" self._check_closed() return self._mariadb_get_info(INFO.TLS_VERSION) @property def server_status(self): """ Return server status flags """ self._check_closed() return self._mariadb_get_info(INFO.SERVER_STATUS) @property def server_version(self): """ Server version in numerical format. The form of the version number is VERSION_MAJOR * 10000 + VERSION_MINOR * 100 + VERSION_PATCH """ self._check_closed() return self._mariadb_get_info(INFO.SERVER_VERSION_ID) @property def server_version_info(self): """ Returns numeric version of connected database server in tuple format. """ self._check_closed() version = self.server_version return (int(version / 10000), int((version % 10000) / 100), version % 100) @property def autocommit(self): """ Toggles autocommit mode on or off for the current database connection. Autocommit mode only affects operations on transactional table types. Be aware that rollback() will not work, if autocommit mode was switched on. By default autocommit mode is set to False." """ self._check_closed() return bool(self.server_status & STATUS.AUTOCOMMIT) @autocommit.setter def autocommit(self, mode): self._check_closed() if bool(mode) == self.autocommit: return try: self._execute_command("SET AUTOCOMMIT=%s" % int(mode)) self._read_response() except mariadb.Error: raise @property def socket(self): """Returns the socket used for database connection""" fno = self._get_socket() if not self._socket: self._socket = socket.socket(fileno=fno) # in case of a possible reconnect, file descriptor has changed elif fno != self._socket.fileno(): self._socket = socket.socket(fileno=fno) return self._socket @property def open(self): """ Returns true if the connection is alive. A ping command will be send to the server for this purpose, which means this function might fail if there are still non processed pending result sets. """ self._check_closed() try: self.ping() except mariadb.Error: return False return True # Aliases character_set_name = character_set @property def thread_id(self): """ Alias for connection_id """ self._check_closed() return self.connection_id mariadb-connector-python-1.1.10/mariadb/constants/000077500000000000000000000000001456007477700221345ustar00rootroot00000000000000mariadb-connector-python-1.1.10/mariadb/constants/CAPABILITY.py000066400000000000000000000014771456007477700241400ustar00rootroot00000000000000''' MariaDB capability flags. These flags are used to check the capabilities both of a MariaDB server or the client applicaion. Capability flags are defined in module *mariadb.constants.CAPABILIY* ''' MYSQL = 1 # MariaDB LONG_PASSWORD = 1 # MySQL FOUND_ROWS = 2 LONG_FLAG = 4 CONNECT_WITH_DB = 8 NO_SCHEMA = 16 COMPRESS = 32 LOCAL_FILES = 128 IGNORE_SPACE = 256 INTERACTIVE = 1024 SSL = 2048 TRANSACTIONS = 8192 SECURE_CONNECTION = 32768 MULTI_STATEMENTS = 1 << 16 MULTI_RESULTS = 1 << 17 PS_MULTI_RESULTS = 1 << 18 PLUGIN_AUTH = 1 << 19 CONNECT_ATTRS = 1 << 20 CAN_HANDLE_EXPIRED_PASSWORDS = 1 < 22 SESSION_TRACKING = 1 << 23 SSL_VERIFY_SERVER_CERT = 1 << 30 REMEMBER_OPTIONS = 1 << 31 # MariaDB specific capabilities PROGRESS = 1 << 32 BULK_OPERATIONS = 1 << 34 EXTENDED_METADATA = 1 << 35 CACHE_METDATA = 1 << 36 mariadb-connector-python-1.1.10/mariadb/constants/CLIENT.py000066400000000000000000000014741456007477700234720ustar00rootroot00000000000000''' MariaDB capability flags. These flags are used to check the capabilities both of a MariaDB server or the client applicaion. Capability flags are defined in module *mariadb.constants.CLIENT* ''' MYSQL = 1 # MariaDB LONG_PASSWORD = 1 # MySQL FOUND_ROWS = 2 LONG_FLAG = 4 CONNECT_WITH_DB = 8 NO_SCHEMA = 16 COMPRESS = 32 LOCAL_FILES = 128 IGNORE_SPACE = 256 INTERACTIVE = 1024 SSL = 2048 TRANSACTIONS = 8192 SECURE_CONNECTION = 32768 MULTI_STATEMENTS = 1 << 16 MULTI_RESULTS = 1 << 17 PS_MULTI_RESULTS = 1 << 18 PLUGIN_AUTH = 1 << 19 CONNECT_ATTRS = 1 << 20 CAN_HANDLE_EXPIRED_PASSWORDS = 1 < 22 SESSION_TRACKING = 1 << 23 SSL_VERIFY_SERVER_CERT = 1 << 30 REMEMBER_OPTIONS = 1 << 31 # MariaDB specific capabilities PROGRESS = 1 << 32 BULK_OPERATIONS = 1 << 34 EXTENDED_METADATA = 1 << 35 CACHE_METDATA = 1 << 36 mariadb-connector-python-1.1.10/mariadb/constants/CURSOR.py000066400000000000000000000003211456007477700235170ustar00rootroot00000000000000""" Cursor constants are used for server side cursors. Currently only read only cursor is supported. Cursor constants are defined in module *mariadb.constants.CURSOR*. """ NONE = 0 READ_ONLY = 1 mariadb-connector-python-1.1.10/mariadb/constants/ERR.py000066400000000000000000001214631456007477700231450ustar00rootroot00000000000000# Autogenerated file. Please do not edit! ER_ERROR_FIRST = 1000 ER_HASHCHK = 1000 ER_NISAMCHK = 1001 ER_NO = 1002 ER_YES = 1003 ER_CANT_CREATE_FILE = 1004 ER_CANT_CREATE_TABLE = 1005 ER_CANT_CREATE_DB = 1006 ER_DB_CREATE_EXISTS = 1007 ER_DB_DROP_EXISTS = 1008 ER_DB_DROP_DELETE = 1009 ER_DB_DROP_RMDIR = 1010 ER_CANT_DELETE_FILE = 1011 ER_CANT_FIND_SYSTEM_REC = 1012 ER_CANT_GET_STAT = 1013 ER_CANT_GET_WD = 1014 ER_CANT_LOCK = 1015 ER_CANT_OPEN_FILE = 1016 ER_FILE_NOT_FOUND = 1017 ER_CANT_READ_DIR = 1018 ER_CANT_SET_WD = 1019 ER_CHECKREAD = 1020 ER_DISK_FULL = 1021 ER_DUP_KEY = 1022 ER_ERROR_ON_CLOSE = 1023 ER_ERROR_ON_READ = 1024 ER_ERROR_ON_RENAME = 1025 ER_ERROR_ON_WRITE = 1026 ER_FILE_USED = 1027 ER_FILSORT_ABORT = 1028 ER_FORM_NOT_FOUND = 1029 ER_GET_ERRNO = 1030 ER_ILLEGAL_HA = 1031 ER_KEY_NOT_FOUND = 1032 ER_NOT_FORM_FILE = 1033 ER_NOT_KEYFILE = 1034 ER_OLD_KEYFILE = 1035 ER_OPEN_AS_READONLY = 1036 ER_OUTOFMEMORY = 1037 ER_OUT_OF_SORTMEMORY = 1038 ER_UNEXPECTED_EOF = 1039 ER_CON_COUNT_ERROR = 1040 ER_OUT_OF_RESOURCES = 1041 ER_BAD_HOST_ERROR = 1042 ER_HANDSHAKE_ERROR = 1043 ER_DBACCESS_DENIED_ERROR = 1044 ER_ACCESS_DENIED_ERROR = 1045 ER_NO_DB_ERROR = 1046 ER_UNKNOWN_COM_ERROR = 1047 ER_BAD_NULL_ERROR = 1048 ER_BAD_DB_ERROR = 1049 ER_TABLE_EXISTS_ERROR = 1050 ER_BAD_TABLE_ERROR = 1051 ER_NON_UNIQ_ERROR = 1052 ER_SERVER_SHUTDOWN = 1053 ER_BAD_FIELD_ERROR = 1054 ER_WRONG_FIELD_WITH_GROUP = 1055 ER_WRONG_GROUP_FIELD = 1056 ER_WRONG_SUM_SELECT = 1057 ER_WRONG_VALUE_COUNT = 1058 ER_TOO_LONG_IDENT = 1059 ER_DUP_FIELDNAME = 1060 ER_DUP_KEYNAME = 1061 ER_DUP_ENTRY = 1062 ER_WRONG_FIELD_SPEC = 1063 ER_PARSE_ERROR = 1064 ER_EMPTY_QUERY = 1065 ER_NONUNIQ_TABLE = 1066 ER_INVALID_DEFAULT = 1067 ER_MULTIPLE_PRI_KEY = 1068 ER_TOO_MANY_KEYS = 1069 ER_TOO_MANY_KEY_PARTS = 1070 ER_TOO_LONG_KEY = 1071 ER_KEY_COLUMN_DOES_NOT_EXIST = 1072 ER_BLOB_USED_AS_KEY = 1073 ER_TOO_BIG_FIELDLENGTH = 1074 ER_WRONG_AUTO_KEY = 1075 ER_BINLOG_CANT_DELETE_GTID_DOMAIN = 1076 ER_NORMAL_SHUTDOWN = 1077 ER_GOT_SIGNAL = 1078 ER_SHUTDOWN_COMPLETE = 1079 ER_FORCING_CLOSE = 1080 ER_IPSOCK_ERROR = 1081 ER_NO_SUCH_INDEX = 1082 ER_WRONG_FIELD_TERMINATORS = 1083 ER_BLOBS_AND_NO_TERMINATED = 1084 ER_TEXTFILE_NOT_READABLE = 1085 ER_FILE_EXISTS_ERROR = 1086 ER_LOAD_INFO = 1087 ER_ALTER_INFO = 1088 ER_WRONG_SUB_KEY = 1089 ER_CANT_REMOVE_ALL_FIELDS = 1090 ER_CANT_DROP_FIELD_OR_KEY = 1091 ER_INSERT_INFO = 1092 ER_UPDATE_TABLE_USED = 1093 ER_NO_SUCH_THREAD = 1094 ER_KILL_DENIED_ERROR = 1095 ER_NO_TABLES_USED = 1096 ER_TOO_BIG_SET = 1097 ER_NO_UNIQUE_LOGFILE = 1098 ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099 ER_TABLE_NOT_LOCKED = 1100 ER_WRONG_DB_NAME = 1102 ER_WRONG_TABLE_NAME = 1103 ER_TOO_BIG_SELECT = 1104 ER_UNKNOWN_ERROR = 1105 ER_UNKNOWN_PROCEDURE = 1106 ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108 ER_UNKNOWN_TABLE = 1109 ER_FIELD_SPECIFIED_TWICE = 1110 ER_INVALID_GROUP_FUNC_USE = 1111 ER_UNSUPPORTED_EXTENSION = 1112 ER_TABLE_MUST_HAVE_COLUMNS = 1113 ER_RECORD_FILE_FULL = 1114 ER_UNKNOWN_CHARACTER_SET = 1115 ER_TOO_MANY_TABLES = 1116 ER_TOO_MANY_FIELDS = 1117 ER_TOO_BIG_ROWSIZE = 1118 ER_STACK_OVERRUN = 1119 ER_WRONG_OUTER_JOIN = 1120 ER_NULL_COLUMN_IN_INDEX = 1121 ER_CANT_FIND_UDF = 1122 ER_CANT_INITIALIZE_UDF = 1123 ER_UDF_NO_PATHS = 1124 ER_UDF_EXISTS = 1125 ER_CANT_OPEN_LIBRARY = 1126 ER_CANT_FIND_DL_ENTRY = 1127 ER_FUNCTION_NOT_DEFINED = 1128 ER_HOST_IS_BLOCKED = 1129 ER_HOST_NOT_PRIVILEGED = 1130 ER_PASSWORD_ANONYMOUS_USER = 1131 ER_PASSWORD_NOT_ALLOWED = 1132 ER_PASSWORD_NO_MATCH = 1133 ER_UPDATE_INFO = 1134 ER_CANT_CREATE_THREAD = 1135 ER_WRONG_VALUE_COUNT_ON_ROW = 1136 ER_CANT_REOPEN_TABLE = 1137 ER_INVALID_USE_OF_NULL = 1138 ER_REGEXP_ERROR = 1139 ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 ER_NONEXISTING_GRANT = 1141 ER_TABLEACCESS_DENIED_ERROR = 1142 ER_COLUMNACCESS_DENIED_ERROR = 1143 ER_ILLEGAL_GRANT_FOR_TABLE = 1144 ER_GRANT_WRONG_HOST_OR_USER = 1145 ER_NO_SUCH_TABLE = 1146 ER_NONEXISTING_TABLE_GRANT = 1147 ER_NOT_ALLOWED_COMMAND = 1148 ER_SYNTAX_ERROR = 1149 ER_DELAYED_CANT_CHANGE_LOCK = 1150 ER_TOO_MANY_DELAYED_THREADS = 1151 ER_ABORTING_CONNECTION = 1152 ER_NET_PACKET_TOO_LARGE = 1153 ER_NET_READ_ERROR_FROM_PIPE = 1154 ER_NET_FCNTL_ERROR = 1155 ER_NET_PACKETS_OUT_OF_ORDER = 1156 ER_NET_UNCOMPRESS_ERROR = 1157 ER_NET_READ_ERROR = 1158 ER_NET_READ_INTERRUPTED = 1159 ER_NET_ERROR_ON_WRITE = 1160 ER_NET_WRITE_INTERRUPTED = 1161 ER_TOO_LONG_STRING = 1162 ER_TABLE_CANT_HANDLE_BLOB = 1163 ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 ER_DELAYED_INSERT_TABLE_LOCKED = 1165 ER_WRONG_COLUMN_NAME = 1166 ER_WRONG_KEY_COLUMN = 1167 ER_WRONG_MRG_TABLE = 1168 ER_DUP_UNIQUE = 1169 ER_BLOB_KEY_WITHOUT_LENGTH = 1170 ER_PRIMARY_CANT_HAVE_NULL = 1171 ER_TOO_MANY_ROWS = 1172 ER_REQUIRES_PRIMARY_KEY = 1173 ER_NO_RAID_COMPILED = 1174 ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 ER_KEY_DOES_NOT_EXISTS = 1176 ER_CHECK_NO_SUCH_TABLE = 1177 ER_CHECK_NOT_IMPLEMENTED = 1178 ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 ER_ERROR_DURING_COMMIT = 1180 ER_ERROR_DURING_ROLLBACK = 1181 ER_ERROR_DURING_FLUSH_LOGS = 1182 ER_ERROR_DURING_CHECKPOINT = 1183 ER_NEW_ABORTING_CONNECTION = 1184 ER_FLUSH_MASTER_BINLOG_CLOSED = 1186 ER_INDEX_REBUILD = 1187 ER_MASTER = 1188 ER_MASTER_NET_READ = 1189 ER_MASTER_NET_WRITE = 1190 ER_FT_MATCHING_KEY_NOT_FOUND = 1191 ER_LOCK_OR_ACTIVE_TRANSACTION = 1192 ER_UNKNOWN_SYSTEM_VARIABLE = 1193 ER_CRASHED_ON_USAGE = 1194 ER_CRASHED_ON_REPAIR = 1195 ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196 ER_TRANS_CACHE_FULL = 1197 ER_SLAVE_MUST_STOP = 1198 ER_SLAVE_NOT_RUNNING = 1199 ER_BAD_SLAVE = 1200 ER_MASTER_INFO = 1201 ER_SLAVE_THREAD = 1202 ER_TOO_MANY_USER_CONNECTIONS = 1203 ER_SET_CONSTANTS_ONLY = 1204 ER_LOCK_WAIT_TIMEOUT = 1205 ER_LOCK_TABLE_FULL = 1206 ER_READ_ONLY_TRANSACTION = 1207 ER_DROP_DB_WITH_READ_LOCK = 1208 ER_CREATE_DB_WITH_READ_LOCK = 1209 ER_WRONG_ARGUMENTS = 1210 ER_NO_PERMISSION_TO_CREATE_USER = 1211 ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212 ER_LOCK_DEADLOCK = 1213 ER_TABLE_CANT_HANDLE_FT = 1214 ER_CANNOT_ADD_FOREIGN = 1215 ER_NO_REFERENCED_ROW = 1216 ER_ROW_IS_REFERENCED = 1217 ER_CONNECT_TO_MASTER = 1218 ER_QUERY_ON_MASTER = 1219 ER_ERROR_WHEN_EXECUTING_COMMAND = 1220 ER_WRONG_USAGE = 1221 ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 ER_CANT_UPDATE_WITH_READLOCK = 1223 ER_MIXING_NOT_ALLOWED = 1224 ER_DUP_ARGUMENT = 1225 ER_USER_LIMIT_REACHED = 1226 ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227 ER_LOCAL_VARIABLE = 1228 ER_GLOBAL_VARIABLE = 1229 ER_NO_DEFAULT = 1230 ER_WRONG_VALUE_FOR_VAR = 1231 ER_WRONG_TYPE_FOR_VAR = 1232 ER_VAR_CANT_BE_READ = 1233 ER_CANT_USE_OPTION_HERE = 1234 ER_NOT_SUPPORTED_YET = 1235 ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236 ER_SLAVE_IGNORED_TABLE = 1237 ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238 ER_WRONG_FK_DEF = 1239 ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 ER_OPERAND_COLUMNS = 1241 ER_SUBQUERY_NO_1_ROW = 1242 ER_UNKNOWN_STMT_HANDLER = 1243 ER_CORRUPT_HELP_DB = 1244 ER_CYCLIC_REFERENCE = 1245 ER_AUTO_CONVERT = 1246 ER_ILLEGAL_REFERENCE = 1247 ER_DERIVED_MUST_HAVE_ALIAS = 1248 ER_SELECT_REDUCED = 1249 ER_TABLENAME_NOT_ALLOWED_HERE = 1250 ER_NOT_SUPPORTED_AUTH_MODE = 1251 ER_SPATIAL_CANT_HAVE_NULL = 1252 ER_COLLATION_CHARSET_MISMATCH = 1253 ER_SLAVE_WAS_RUNNING = 1254 ER_SLAVE_WAS_NOT_RUNNING = 1255 ER_TOO_BIG_FOR_UNCOMPRESS = 1256 ER_ZLIB_Z_MEM_ERROR = 1257 ER_ZLIB_Z_BUF_ERROR = 1258 ER_ZLIB_Z_DATA_ERROR = 1259 ER_CUT_VALUE_GROUP_CONCAT = 1260 ER_WARN_TOO_FEW_RECORDS = 1261 ER_WARN_TOO_MANY_RECORDS = 1262 ER_WARN_NULL_TO_NOTNULL = 1263 ER_WARN_DATA_OUT_OF_RANGE = 1264 WARN_DATA_TRUNCATED = 1265 ER_WARN_USING_OTHER_HANDLER = 1266 ER_CANT_AGGREGATE_2COLLATIONS = 1267 ER_DROP_USER = 1268 ER_REVOKE_GRANTS = 1269 ER_CANT_AGGREGATE_3COLLATIONS = 1270 ER_CANT_AGGREGATE_NCOLLATIONS = 1271 ER_VARIABLE_IS_NOT_STRUCT = 1272 ER_UNKNOWN_COLLATION = 1273 ER_SLAVE_IGNORED_SSL_PARAMS = 1274 ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275 ER_WARN_FIELD_RESOLVED = 1276 ER_BAD_SLAVE_UNTIL_COND = 1277 ER_MISSING_SKIP_SLAVE = 1278 ER_UNTIL_COND_IGNORED = 1279 ER_WRONG_NAME_FOR_INDEX = 1280 ER_WRONG_NAME_FOR_CATALOG = 1281 ER_WARN_QC_RESIZE = 1282 ER_BAD_FT_COLUMN = 1283 ER_UNKNOWN_KEY_CACHE = 1284 ER_WARN_HOSTNAME_WONT_WORK = 1285 ER_UNKNOWN_STORAGE_ENGINE = 1286 ER_WARN_DEPRECATED_SYNTAX = 1287 ER_NON_UPDATABLE_TABLE = 1288 ER_FEATURE_DISABLED = 1289 ER_OPTION_PREVENTS_STATEMENT = 1290 ER_DUPLICATED_VALUE_IN_TYPE = 1291 ER_TRUNCATED_WRONG_VALUE = 1292 ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293 ER_INVALID_ON_UPDATE = 1294 ER_UNSUPPORTED_PS = 1295 ER_GET_ERRMSG = 1296 ER_GET_TEMPORARY_ERRMSG = 1297 ER_UNKNOWN_TIME_ZONE = 1298 ER_WARN_INVALID_TIMESTAMP = 1299 ER_INVALID_CHARACTER_STRING = 1300 ER_WARN_ALLOWED_PACKET_OVERFLOWED = 1301 ER_CONFLICTING_DECLARATIONS = 1302 ER_SP_NO_RECURSIVE_CREATE = 1303 ER_SP_ALREADY_EXISTS = 1304 ER_SP_DOES_NOT_EXIST = 1305 ER_SP_DROP_FAILED = 1306 ER_SP_STORE_FAILED = 1307 ER_SP_LILABEL_MISMATCH = 1308 ER_SP_LABEL_REDEFINE = 1309 ER_SP_LABEL_MISMATCH = 1310 ER_SP_UNINIT_VAR = 1311 ER_SP_BADSELECT = 1312 ER_SP_BADRETURN = 1313 ER_SP_BADSTATEMENT = 1314 ER_UPDATE_LOG_DEPRECATED_IGNORED = 1315 ER_UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 ER_QUERY_INTERRUPTED = 1317 ER_SP_WRONG_NO_OF_ARGS = 1318 ER_SP_COND_MISMATCH = 1319 ER_SP_NORETURN = 1320 ER_SP_NORETURNEND = 1321 ER_SP_BAD_CURSOR_QUERY = 1322 ER_SP_BAD_CURSOR_SELECT = 1323 ER_SP_CURSOR_MISMATCH = 1324 ER_SP_CURSOR_ALREADY_OPEN = 1325 ER_SP_CURSOR_NOT_OPEN = 1326 ER_SP_UNDECLARED_VAR = 1327 ER_SP_WRONG_NO_OF_FETCH_ARGS = 1328 ER_SP_FETCH_NO_DATA = 1329 ER_SP_DUP_PARAM = 1330 ER_SP_DUP_VAR = 1331 ER_SP_DUP_COND = 1332 ER_SP_DUP_CURS = 1333 ER_SP_CANT_ALTER = 1334 ER_SP_SUBSELECT_NYI = 1335 ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 ER_SP_VARCOND_AFTER_CURSHNDLR = 1337 ER_SP_CURSOR_AFTER_HANDLER = 1338 ER_SP_CASE_NOT_FOUND = 1339 ER_FPARSER_TOO_BIG_FILE = 1340 ER_FPARSER_BAD_HEADER = 1341 ER_FPARSER_EOF_IN_COMMENT = 1342 ER_FPARSER_ERROR_IN_PARAMETER = 1343 ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 ER_VIEW_NO_EXPLAIN = 1345 ER_FRM_UNKNOWN_TYPE = 1346 ER_WRONG_OBJECT = 1347 ER_NONUPDATEABLE_COLUMN = 1348 ER_VIEW_SELECT_DERIVED = 1349 ER_VIEW_SELECT_CLAUSE = 1350 ER_VIEW_SELECT_VARIABLE = 1351 ER_VIEW_SELECT_TMPTABLE = 1352 ER_VIEW_WRONG_LIST = 1353 ER_WARN_VIEW_MERGE = 1354 ER_WARN_VIEW_WITHOUT_KEY = 1355 ER_VIEW_INVALID = 1356 ER_SP_NO_DROP_SP = 1357 ER_SP_GOTO_IN_HNDLR = 1358 ER_TRG_ALREADY_EXISTS = 1359 ER_TRG_DOES_NOT_EXIST = 1360 ER_TRG_ON_VIEW_OR_TEMP_TABLE = 1361 ER_TRG_CANT_CHANGE_ROW = 1362 ER_TRG_NO_SUCH_ROW_IN_TRG = 1363 ER_NO_DEFAULT_FOR_FIELD = 1364 ER_DIVISION_BY_ZERO = 1365 ER_TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 ER_ILLEGAL_VALUE_FOR_TYPE = 1367 ER_VIEW_NONUPD_CHECK = 1368 ER_VIEW_CHECK_FAILED = 1369 ER_PROCACCESS_DENIED_ERROR = 1370 ER_RELAY_LOG_FAIL = 1371 ER_PASSWD_LENGTH = 1372 ER_UNKNOWN_TARGET_BINLOG = 1373 ER_IO_ERR_LOG_INDEX_READ = 1374 ER_BINLOG_PURGE_PROHIBITED = 1375 ER_FSEEK_FAIL = 1376 ER_BINLOG_PURGE_FATAL_ERR = 1377 ER_LOG_IN_USE = 1378 ER_LOG_PURGE_UNKNOWN_ERR = 1379 ER_RELAY_LOG_INIT = 1380 ER_NO_BINARY_LOGGING = 1381 ER_RESERVED_SYNTAX = 1382 ER_WSAS_FAILED = 1383 ER_DIFF_GROUPS_PROC = 1384 ER_NO_GROUP_FOR_PROC = 1385 ER_ORDER_WITH_PROC = 1386 ER_LOGGING_PROHIBIT_CHANGING_OF = 1387 ER_NO_FILE_MAPPING = 1388 ER_WRONG_MAGIC = 1389 ER_PS_MANY_PARAM = 1390 ER_KEY_PART_0 = 1391 ER_VIEW_CHECKSUM = 1392 ER_VIEW_MULTIUPDATE = 1393 ER_VIEW_NO_INSERT_FIELD_LIST = 1394 ER_VIEW_DELETE_MERGE_VIEW = 1395 ER_CANNOT_USER = 1396 ER_XAER_NOTA = 1397 ER_XAER_INVAL = 1398 ER_XAER_RMFAIL = 1399 ER_XAER_OUTSIDE = 1400 ER_XAER_RMERR = 1401 ER_XA_RBROLLBACK = 1402 ER_NONEXISTING_PROC_GRANT = 1403 ER_PROC_AUTO_GRANT_FAIL = 1404 ER_PROC_AUTO_REVOKE_FAIL = 1405 ER_DATA_TOO_LONG = 1406 ER_SP_BAD_SQLSTATE = 1407 ER_STARTUP = 1408 ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 ER_CANT_CREATE_USER_WITH_GRANT = 1410 ER_WRONG_VALUE_FOR_TYPE = 1411 ER_TABLE_DEF_CHANGED = 1412 ER_SP_DUP_HANDLER = 1413 ER_SP_NOT_VAR_ARG = 1414 ER_SP_NO_RETSET = 1415 ER_CANT_CREATE_GEOMETRY_OBJECT = 1416 ER_FAILED_ROUTINE_BREAK_BINLOG = 1417 ER_BINLOG_UNSAFE_ROUTINE = 1418 ER_BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 ER_EXEC_STMT_WITH_OPEN_CURSOR = 1420 ER_STMT_HAS_NO_OPEN_CURSOR = 1421 ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 ER_NO_DEFAULT_FOR_VIEW_FIELD = 1423 ER_SP_NO_RECURSION = 1424 ER_TOO_BIG_SCALE = 1425 ER_TOO_BIG_PRECISION = 1426 ER_M_BIGGER_THAN_D = 1427 ER_WRONG_LOCK_OF_SYSTEM_TABLE = 1428 ER_CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 ER_QUERY_ON_FOREIGN_DATA_SOURCE = 1430 ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 ER_FOREIGN_DATA_STRING_INVALID = 1433 ER_CANT_CREATE_FEDERATED_TABLE = 1434 ER_TRG_IN_WRONG_SCHEMA = 1435 ER_STACK_OVERRUN_NEED_MORE = 1436 ER_TOO_LONG_BODY = 1437 ER_WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 ER_TOO_BIG_DISPLAYWIDTH = 1439 ER_XAER_DUPID = 1440 ER_DATETIME_FUNCTION_OVERFLOW = 1441 ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 ER_VIEW_PREVENT_UPDATE = 1443 ER_PS_NO_RECURSION = 1444 ER_SP_CANT_SET_AUTOCOMMIT = 1445 ER_MALFORMED_DEFINER = 1446 ER_VIEW_FRM_NO_USER = 1447 ER_VIEW_OTHER_USER = 1448 ER_NO_SUCH_USER = 1449 ER_FORBID_SCHEMA_CHANGE = 1450 ER_ROW_IS_REFERENCED_2 = 1451 ER_NO_REFERENCED_ROW_2 = 1452 ER_SP_BAD_VAR_SHADOW = 1453 ER_TRG_NO_DEFINER = 1454 ER_OLD_FILE_FORMAT = 1455 ER_SP_RECURSION_LIMIT = 1456 ER_SP_PROC_TABLE_CORRUPT = 1457 ER_SP_WRONG_NAME = 1458 ER_TABLE_NEEDS_UPGRADE = 1459 ER_SP_NO_AGGREGATE = 1460 ER_MAX_PREPARED_STMT_COUNT_REACHED = 1461 ER_VIEW_RECURSIVE = 1462 ER_NON_GROUPING_FIELD_USED = 1463 ER_TABLE_CANT_HANDLE_SPKEYS = 1464 ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 ER_REMOVED_SPACES = 1466 ER_AUTOINC_READ_FAILED = 1467 ER_USERNAME = 1468 ER_HOSTNAME = 1469 ER_WRONG_STRING_LENGTH = 1470 ER_NON_INSERTABLE_TABLE = 1471 ER_ADMIN_WRONG_MRG_TABLE = 1472 ER_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT = 1473 ER_NAME_BECOMES_EMPTY = 1474 ER_AMBIGUOUS_FIELD_TERM = 1475 ER_FOREIGN_SERVER_EXISTS = 1476 ER_FOREIGN_SERVER_DOESNT_EXIST = 1477 ER_ILLEGAL_HA_CREATE_OPTION = 1478 ER_PARTITION_REQUIRES_VALUES_ERROR = 1479 ER_PARTITION_WRONG_VALUES_ERROR = 1480 ER_PARTITION_MAXVALUE_ERROR = 1481 ER_PARTITION_SUBPARTITION_ERROR = 1482 ER_PARTITION_SUBPART_MIX_ERROR = 1483 ER_PARTITION_WRONG_NO_PART_ERROR = 1484 ER_PARTITION_WRONG_NO_SUBPART_ERROR = 1485 ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR = 1486 ER_NOT_CONSTANT_EXPRESSION = 1487 ER_FIELD_NOT_FOUND_PART_ERROR = 1488 ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR = 1489 ER_INCONSISTENT_PARTITION_INFO_ERROR = 1490 ER_PARTITION_FUNC_NOT_ALLOWED_ERROR = 1491 ER_PARTITIONS_MUST_BE_DEFINED_ERROR = 1492 ER_RANGE_NOT_INCREASING_ERROR = 1493 ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR = 1494 ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR = 1495 ER_PARTITION_ENTRY_ERROR = 1496 ER_MIX_HANDLER_ERROR = 1497 ER_PARTITION_NOT_DEFINED_ERROR = 1498 ER_TOO_MANY_PARTITIONS_ERROR = 1499 ER_SUBPARTITION_ERROR = 1500 ER_CANT_CREATE_HANDLER_FILE = 1501 ER_BLOB_FIELD_IN_PART_FUNC_ERROR = 1502 ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF = 1503 ER_NO_PARTS_ERROR = 1504 ER_PARTITION_MGMT_ON_NONPARTITIONED = 1505 ER_FEATURE_NOT_SUPPORTED_WITH_PARTITIONING = 1506 ER_PARTITION_DOES_NOT_EXIST = 1507 ER_DROP_LAST_PARTITION = 1508 ER_COALESCE_ONLY_ON_HASH_PARTITION = 1509 ER_REORG_HASH_ONLY_ON_SAME_NO = 1510 ER_REORG_NO_PARAM_ERROR = 1511 ER_ONLY_ON_RANGE_LIST_PARTITION = 1512 ER_ADD_PARTITION_SUBPART_ERROR = 1513 ER_ADD_PARTITION_NO_NEW_PARTITION = 1514 ER_COALESCE_PARTITION_NO_PARTITION = 1515 ER_REORG_PARTITION_NOT_EXIST = 1516 ER_SAME_NAME_PARTITION = 1517 ER_NO_BINLOG_ERROR = 1518 ER_CONSECUTIVE_REORG_PARTITIONS = 1519 ER_REORG_OUTSIDE_RANGE = 1520 ER_PARTITION_FUNCTION_FAILURE = 1521 ER_PART_STATE_ERROR = 1522 ER_LIMITED_PART_RANGE = 1523 ER_PLUGIN_IS_NOT_LOADED = 1524 ER_WRONG_VALUE = 1525 ER_NO_PARTITION_FOR_GIVEN_VALUE = 1526 ER_FILEGROUP_OPTION_ONLY_ONCE = 1527 ER_CREATE_FILEGROUP_FAILED = 1528 ER_DROP_FILEGROUP_FAILED = 1529 ER_TABLESPACE_AUTO_EXTEND_ERROR = 1530 ER_WRONG_SIZE_NUMBER = 1531 ER_SIZE_OVERFLOW_ERROR = 1532 ER_ALTER_FILEGROUP_FAILED = 1533 ER_BINLOG_ROW_LOGGING_FAILED = 1534 ER_BINLOG_ROW_WRONG_TABLE_DEF = 1535 ER_BINLOG_ROW_RBR_TO_SBR = 1536 ER_EVENT_ALREADY_EXISTS = 1537 ER_EVENT_STORE_FAILED = 1538 ER_EVENT_DOES_NOT_EXIST = 1539 ER_EVENT_CANT_ALTER = 1540 ER_EVENT_DROP_FAILED = 1541 ER_EVENT_INTERVAL_NOT_POSITIVE_OR_TOO_BIG = 1542 ER_EVENT_ENDS_BEFORE_STARTS = 1543 ER_EVENT_EXEC_TIME_IN_THE_PAST = 1544 ER_EVENT_OPEN_TABLE_FAILED = 1545 ER_EVENT_NEITHER_M_EXPR_NOR_M_AT = 1546 ER_EVENT_CANNOT_DELETE = 1549 ER_EVENT_COMPILE_ERROR = 1550 ER_EVENT_SAME_NAME = 1551 ER_EVENT_DATA_TOO_LONG = 1552 ER_DROP_INDEX_FK = 1553 ER_WARN_DEPRECATED_SYNTAX_WITH_VER = 1554 ER_CANT_WRITE_LOCK_LOG_TABLE = 1555 ER_CANT_LOCK_LOG_TABLE = 1556 ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE = 1558 ER_TEMP_TABLE_PREVENTS_SWITCH_OUT_OF_RBR = 1559 ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1560 ER_PARTITION_NO_TEMPORARY = 1562 ER_PARTITION_CONST_DOMAIN_ERROR = 1563 ER_PARTITION_FUNCTION_IS_NOT_ALLOWED = 1564 ER_DDL_LOG_ERROR = 1565 ER_NULL_IN_VALUES_LESS_THAN = 1566 ER_WRONG_PARTITION_NAME = 1567 ER_CANT_CHANGE_TX_CHARACTERISTICS = 1568 ER_DUP_ENTRY_AUTOINCREMENT_CASE = 1569 ER_EVENT_MODIFY_QUEUE_ERROR = 1570 ER_EVENT_SET_VAR_ERROR = 1571 ER_PARTITION_MERGE_ERROR = 1572 ER_CANT_ACTIVATE_LOG = 1573 ER_RBR_NOT_AVAILABLE = 1574 ER_BASE64_DECODE_ERROR = 1575 ER_EVENT_RECURSION_FORBIDDEN = 1576 ER_EVENTS_DB_ERROR = 1577 ER_ONLY_INTEGERS_ALLOWED = 1578 ER_UNSUPORTED_LOG_ENGINE = 1579 ER_BAD_LOG_STATEMENT = 1580 ER_CANT_RENAME_LOG_TABLE = 1581 ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT = 1582 ER_WRONG_PARAMETERS_TO_NATIVE_FCT = 1583 ER_WRONG_PARAMETERS_TO_STORED_FCT = 1584 ER_NATIVE_FCT_NAME_COLLISION = 1585 ER_DUP_ENTRY_WITH_KEY_NAME = 1586 ER_BINLOG_PURGE_EMFILE = 1587 ER_EVENT_CANNOT_CREATE_IN_THE_PAST = 1588 ER_EVENT_CANNOT_ALTER_IN_THE_PAST = 1589 ER_SLAVE_INCIDENT = 1590 ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT = 1591 ER_BINLOG_UNSAFE_STATEMENT = 1592 ER_SLAVE_FATAL_ERROR = 1593 ER_SLAVE_RELAY_LOG_READ_FAILURE = 1594 ER_SLAVE_RELAY_LOG_WRITE_FAILURE = 1595 ER_SLAVE_CREATE_EVENT_FAILURE = 1596 ER_SLAVE_MASTER_COM_FAILURE = 1597 ER_BINLOG_LOGGING_IMPOSSIBLE = 1598 ER_VIEW_NO_CREATION_CTX = 1599 ER_VIEW_INVALID_CREATION_CTX = 1600 ER_SR_INVALID_CREATION_CTX = 1601 ER_TRG_CORRUPTED_FILE = 1602 ER_TRG_NO_CREATION_CTX = 1603 ER_TRG_INVALID_CREATION_CTX = 1604 ER_EVENT_INVALID_CREATION_CTX = 1605 ER_TRG_CANT_OPEN_TABLE = 1606 ER_CANT_CREATE_SROUTINE = 1607 ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT = 1609 ER_SLAVE_CORRUPT_EVENT = 1610 ER_LOAD_DATA_INVALID_COLUMN = 1611 ER_LOG_PURGE_NO_FILE = 1612 ER_XA_RBTIMEOUT = 1613 ER_XA_RBDEADLOCK = 1614 ER_NEED_REPREPARE = 1615 ER_DELAYED_NOT_SUPPORTED = 1616 WARN_NO_MASTER_INFO = 1617 WARN_OPTION_IGNORED = 1618 ER_PLUGIN_DELETE_BUILTIN = 1619 WARN_PLUGIN_BUSY = 1620 ER_VARIABLE_IS_READONLY = 1621 ER_WARN_ENGINE_TRANSACTION_ROLLBACK = 1622 ER_SLAVE_HEARTBEAT_FAILURE = 1623 ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE = 1624 ER_CONFLICT_FN_PARSE_ERROR = 1626 ER_EXCEPTIONS_WRITE_ERROR = 1627 ER_TOO_LONG_TABLE_COMMENT = 1628 ER_TOO_LONG_FIELD_COMMENT = 1629 ER_FUNC_INEXISTENT_NAME_COLLISION = 1630 ER_DATABASE_NAME = 1631 ER_TABLE_NAME = 1632 ER_PARTITION_NAME = 1633 ER_SUBPARTITION_NAME = 1634 ER_TEMPORARY_NAME = 1635 ER_RENAMED_NAME = 1636 ER_TOO_MANY_CONCURRENT_TRXS = 1637 WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED = 1638 ER_DEBUG_SYNC_TIMEOUT = 1639 ER_DEBUG_SYNC_HIT_LIMIT = 1640 ER_DUP_SIGNAL_SET = 1641 ER_SIGNAL_WARN = 1642 ER_SIGNAL_NOT_FOUND = 1643 ER_SIGNAL_EXCEPTION = 1644 ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER = 1645 ER_SIGNAL_BAD_CONDITION_TYPE = 1646 WARN_COND_ITEM_TRUNCATED = 1647 ER_COND_ITEM_TOO_LONG = 1648 ER_UNKNOWN_LOCALE = 1649 ER_SLAVE_IGNORE_SERVER_IDS = 1650 ER_QUERY_CACHE_DISABLED = 1651 ER_SAME_NAME_PARTITION_FIELD = 1652 ER_PARTITION_COLUMN_LIST_ERROR = 1653 ER_WRONG_TYPE_COLUMN_VALUE_ERROR = 1654 ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR = 1655 ER_MAXVALUE_IN_VALUES_IN = 1656 ER_TOO_MANY_VALUES_ERROR = 1657 ER_ROW_SINGLE_PARTITION_FIELD_ERROR = 1658 ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD = 1659 ER_PARTITION_FIELDS_TOO_LONG = 1660 ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE = 1661 ER_BINLOG_ROW_MODE_AND_STMT_ENGINE = 1662 ER_BINLOG_UNSAFE_AND_STMT_ENGINE = 1663 ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE = 1664 ER_BINLOG_STMT_MODE_AND_ROW_ENGINE = 1665 ER_BINLOG_ROW_INJECTION_AND_STMT_MODE = 1666 ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1667 ER_BINLOG_UNSAFE_LIMIT = 1668 ER_BINLOG_UNSAFE_INSERT_DELAYED = 1669 ER_BINLOG_UNSAFE_SYSTEM_TABLE = 1670 ER_BINLOG_UNSAFE_AUTOINC_COLUMNS = 1671 ER_BINLOG_UNSAFE_UDF = 1672 ER_BINLOG_UNSAFE_SYSTEM_VARIABLE = 1673 ER_BINLOG_UNSAFE_SYSTEM_FUNCTION = 1674 ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS = 1675 ER_MESSAGE_AND_STATEMENT = 1676 ER_SLAVE_CONVERSION_FAILED = 1677 ER_SLAVE_CANT_CREATE_CONVERSION = 1678 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT = 1679 ER_PATH_LENGTH = 1680 ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT = 1681 ER_WRONG_NATIVE_TABLE_STRUCTURE = 1682 ER_WRONG_PERFSCHEMA_USAGE = 1683 ER_WARN_I_S_SKIPPED_TABLE = 1684 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1685 ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT = 1686 ER_SPATIAL_MUST_HAVE_GEOM_COL = 1687 ER_TOO_LONG_INDEX_COMMENT = 1688 ER_LOCK_ABORTED = 1689 ER_DATA_OUT_OF_RANGE = 1690 ER_WRONG_SPVAR_TYPE_IN_LIMIT = 1691 ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE = 1692 ER_BINLOG_UNSAFE_MIXED_STATEMENT = 1693 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1694 ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN = 1695 ER_FAILED_READ_FROM_PAR_FILE = 1696 ER_VALUES_IS_NOT_INT_TYPE_ERROR = 1697 ER_ACCESS_DENIED_NO_PASSWORD_ERROR = 1698 ER_SET_PASSWORD_AUTH_PLUGIN = 1699 ER_GRANT_PLUGIN_USER_EXISTS = 1700 ER_TRUNCATE_ILLEGAL_FK = 1701 ER_PLUGIN_IS_PERMANENT = 1702 ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN = 1703 ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX = 1704 ER_STMT_CACHE_FULL = 1705 ER_MULTI_UPDATE_KEY_CONFLICT = 1706 ER_TABLE_NEEDS_REBUILD = 1707 WARN_OPTION_BELOW_LIMIT = 1708 ER_INDEX_COLUMN_TOO_LONG = 1709 ER_ERROR_IN_TRIGGER_BODY = 1710 ER_ERROR_IN_UNKNOWN_TRIGGER_BODY = 1711 ER_INDEX_CORRUPT = 1712 ER_UNDO_RECORD_TOO_BIG = 1713 ER_BINLOG_UNSAFE_INSERT_IGNORE_SELECT = 1714 ER_BINLOG_UNSAFE_INSERT_SELECT_UPDATE = 1715 ER_BINLOG_UNSAFE_REPLACE_SELECT = 1716 ER_BINLOG_UNSAFE_CREATE_IGNORE_SELECT = 1717 ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT = 1718 ER_BINLOG_UNSAFE_UPDATE_IGNORE = 1719 ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT = 1722 ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC = 1723 ER_BINLOG_UNSAFE_INSERT_TWO_KEYS = 1724 ER_VERS_NOT_ALLOWED = 1726 ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST = 1727 ER_CANNOT_LOAD_FROM_TABLE_V2 = 1728 ER_MASTER_DELAY_VALUE_OUT_OF_RANGE = 1729 ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT = 1730 ER_PARTITION_EXCHANGE_DIFFERENT_OPTION = 1731 ER_PARTITION_EXCHANGE_PART_TABLE = 1732 ER_PARTITION_EXCHANGE_TEMP_TABLE = 1733 ER_PARTITION_INSTEAD_OF_SUBPARTITION = 1734 ER_UNKNOWN_PARTITION = 1735 ER_TABLES_DIFFERENT_METADATA = 1736 ER_ROW_DOES_NOT_MATCH_PARTITION = 1737 ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX = 1738 ER_WARN_INDEX_NOT_APPLICABLE = 1739 ER_PARTITION_EXCHANGE_FOREIGN_KEY = 1740 ER_NO_SUCH_KEY_VALUE = 1741 ER_VALUE_TOO_LONG = 1742 ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE = 1743 ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE = 1744 ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX = 1745 ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT = 1746 ER_PARTITION_CLAUSE_ON_NONPARTITIONED = 1747 ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET = 1748 ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE = 1750 ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE = 1751 ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE = 1752 ER_MTS_FEATURE_IS_NOT_SUPPORTED = 1753 ER_MTS_UPDATED_DBS_GREATER_MAX = 1754 ER_MTS_CANT_PARALLEL = 1755 ER_MTS_INCONSISTENT_DATA = 1756 ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING = 1757 ER_DA_INVALID_CONDITION_NUMBER = 1758 ER_INSECURE_PLAIN_TEXT = 1759 ER_INSECURE_CHANGE_MASTER = 1760 ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO = 1761 ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO = 1762 ER_SQLTHREAD_WITH_SECURE_SLAVE = 1763 ER_TABLE_HAS_NO_FT = 1764 ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER = 1765 ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION = 1766 ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST = 1767 ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL = 1768 ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION = 1769 ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL = 1770 ER_SKIPPING_LOGGED_TRANSACTION = 1771 ER_MALFORMED_GTID_SET_SPECIFICATION = 1772 ER_MALFORMED_GTID_SET_ENCODING = 1773 ER_MALFORMED_GTID_SPECIFICATION = 1774 ER_GNO_EXHAUSTED = 1775 ER_BAD_SLAVE_AUTO_POSITION = 1776 ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON = 1777 ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET = 1778 ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON = 1779 ER_GTID_MODE_REQUIRES_BINLOG = 1780 ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF = 1781 ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON = 1782 ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF = 1783 ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF = 1784 ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE = 1785 ER_GTID_UNSAFE_CREATE_SELECT = 1786 ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION = 1787 ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME = 1788 ER_MASTER_HAS_PURGED_REQUIRED_GTIDS = 1789 ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID = 1790 ER_UNKNOWN_EXPLAIN_FORMAT = 1791 ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION = 1792 ER_TOO_LONG_TABLE_PARTITION_COMMENT = 1793 ER_SLAVE_CONFIGURATION = 1794 ER_INNODB_FT_LIMIT = 1795 ER_INNODB_NO_FT_TEMP_TABLE = 1796 ER_INNODB_FT_WRONG_DOCID_COLUMN = 1797 ER_INNODB_FT_WRONG_DOCID_INDEX = 1798 ER_INNODB_ONLINE_LOG_TOO_BIG = 1799 ER_UNKNOWN_ALTER_ALGORITHM = 1800 ER_UNKNOWN_ALTER_LOCK = 1801 ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS = 1802 ER_MTS_RECOVERY_FAILURE = 1803 ER_MTS_RESET_WORKERS = 1804 ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2 = 1805 ER_SLAVE_SILENT_RETRY_TRANSACTION = 1806 ER_TABLE_SCHEMA_MISMATCH = 1808 ER_TABLE_IN_SYSTEM_TABLESPACE = 1809 ER_IO_READ_ERROR = 1810 ER_IO_WRITE_ERROR = 1811 ER_TABLESPACE_MISSING = 1812 ER_TABLESPACE_EXISTS = 1813 ER_TABLESPACE_DISCARDED = 1814 ER_INTERNAL_ERROR = 1815 ER_INNODB_IMPORT_ERROR = 1816 ER_INNODB_INDEX_CORRUPT = 1817 ER_INVALID_YEAR_COLUMN_LENGTH = 1818 ER_NOT_VALID_PASSWORD = 1819 ER_MUST_CHANGE_PASSWORD = 1820 ER_FK_NO_INDEX_CHILD = 1821 ER_FK_NO_INDEX_PARENT = 1822 ER_FK_FAIL_ADD_SYSTEM = 1823 ER_FK_CANNOT_OPEN_PARENT = 1824 ER_FK_INCORRECT_OPTION = 1825 ER_DUP_CONSTRAINT_NAME = 1826 ER_PASSWORD_FORMAT = 1827 ER_FK_COLUMN_CANNOT_DROP = 1828 ER_FK_COLUMN_CANNOT_DROP_CHILD = 1829 ER_FK_COLUMN_NOT_NULL = 1830 ER_DUP_INDEX = 1831 ER_FK_COLUMN_CANNOT_CHANGE = 1832 ER_FK_COLUMN_CANNOT_CHANGE_CHILD = 1833 ER_FK_CANNOT_DELETE_PARENT = 1834 ER_MALFORMED_PACKET = 1835 ER_READ_ONLY_MODE = 1836 ER_GTID_NEXT_TYPE_UNDEFINED_GROUP = 1837 ER_VARIABLE_NOT_SETTABLE_IN_SP = 1838 ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF = 1839 ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY = 1840 ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY = 1841 ER_GTID_PURGED_WAS_CHANGED = 1842 ER_GTID_EXECUTED_WAS_CHANGED = 1843 ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES = 1844 ER_ALTER_OPERATION_NOT_SUPPORTED = 1845 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON = 1846 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY = 1847 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION = 1848 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME = 1849 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE = 1850 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK = 1851 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE = 1852 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK = 1853 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC = 1854 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS = 1855 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS = 1856 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS = 1857 ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE = 1858 ER_DUP_UNKNOWN_IN_INDEX = 1859 ER_IDENT_CAUSES_TOO_LONG_PATH = 1860 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL = 1861 ER_MUST_CHANGE_PASSWORD_LOGIN = 1862 ER_ROW_IN_WRONG_PARTITION = 1863 ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX = 1864 ER_INNODB_NO_FT_USES_PARSER = 1865 ER_BINLOG_LOGICAL_CORRUPTION = 1866 ER_WARN_PURGE_LOG_IN_USE = 1867 ER_WARN_PURGE_LOG_IS_ACTIVE = 1868 ER_AUTO_INCREMENT_CONFLICT = 1869 WARN_ON_BLOCKHOLE_IN_RBR = 1870 ER_SLAVE_MI_INIT_REPOSITORY = 1871 ER_SLAVE_RLI_INIT_REPOSITORY = 1872 ER_ACCESS_DENIED_CHANGE_USER_ERROR = 1873 ER_INNODB_READ_ONLY = 1874 ER_STOP_SLAVE_SQL_THREAD_TIMEOUT = 1875 ER_STOP_SLAVE_IO_THREAD_TIMEOUT = 1876 ER_TABLE_CORRUPT = 1877 ER_TEMP_FILE_WRITE_FAILURE = 1878 ER_INNODB_FT_AUX_NOT_HEX_ID = 1879 ER_LAST_MYSQL_ERROR_MESSAGE = 1880 ER_ERROR_LAST_SECTION_1 = 1880 ER_ERROR_FIRST_SECTION_2 = 1900 ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED = 1901 ER_PRIMARY_KEY_BASED_ON_GENERATED_COLUMN = 1903 ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN = 1904 ER_WRONG_FK_OPTION_FOR_GENERATED_COLUMN = 1905 ER_WARNING_NON_DEFAULT_VALUE_FOR_GENERATED_COLUMN = 1906 ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN = 1907 ER_UNSUPPORTED_ENGINE_FOR_GENERATED_COLUMNS = 1910 ER_UNKNOWN_OPTION = 1911 ER_BAD_OPTION_VALUE = 1912 ER_DATA_OVERFLOW = 1916 ER_DATA_TRUNCATED = 1917 ER_BAD_DATA = 1918 ER_DYN_COL_WRONG_FORMAT = 1919 ER_DYN_COL_IMPLEMENTATION_LIMIT = 1920 ER_DYN_COL_DATA = 1921 ER_DYN_COL_WRONG_CHARSET = 1922 ER_ILLEGAL_SUBQUERY_OPTIMIZER_SWITCHES = 1923 ER_QUERY_CACHE_IS_DISABLED = 1924 ER_QUERY_CACHE_IS_GLOBALY_DISABLED = 1925 ER_VIEW_ORDERBY_IGNORED = 1926 ER_CONNECTION_KILLED = 1927 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SKIP_REPLICATION = 1929 ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION = 1930 ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT = 1931 ER_NO_SUCH_TABLE_IN_ENGINE = 1932 ER_TARGET_NOT_EXPLAINABLE = 1933 ER_CONNECTION_ALREADY_EXISTS = 1934 ER_MASTER_LOG_PREFIX = 1935 ER_CANT_START_STOP_SLAVE = 1936 ER_SLAVE_STARTED = 1937 ER_SLAVE_STOPPED = 1938 ER_SQL_DISCOVER_ERROR = 1939 ER_FAILED_GTID_STATE_INIT = 1940 ER_INCORRECT_GTID_STATE = 1941 ER_CANNOT_UPDATE_GTID_STATE = 1942 ER_DUPLICATE_GTID_DOMAIN = 1943 ER_GTID_OPEN_TABLE_FAILED = 1944 ER_GTID_POSITION_NOT_FOUND_IN_BINLOG = 1945 ER_CANNOT_LOAD_SLAVE_GTID_STATE = 1946 ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG = 1947 ER_MASTER_GTID_POS_MISSING_DOMAIN = 1948 ER_UNTIL_REQUIRES_USING_GTID = 1949 ER_GTID_STRICT_OUT_OF_ORDER = 1950 ER_GTID_START_FROM_BINLOG_HOLE = 1951 ER_SLAVE_UNEXPECTED_MASTER_SWITCH = 1952 ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO = 1953 ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO = 1954 ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2 = 1955 ER_BINLOG_MUST_BE_EMPTY = 1956 ER_NO_SUCH_QUERY = 1957 ER_BAD_BASE64_DATA = 1958 ER_INVALID_ROLE = 1959 ER_INVALID_CURRENT_USER = 1960 ER_CANNOT_GRANT_ROLE = 1961 ER_CANNOT_REVOKE_ROLE = 1962 ER_CHANGE_SLAVE_PARALLEL_THREADS_ACTIVE = 1963 ER_PRIOR_COMMIT_FAILED = 1964 ER_IT_IS_A_VIEW = 1965 ER_SLAVE_SKIP_NOT_IN_GTID = 1966 ER_TABLE_DEFINITION_TOO_BIG = 1967 ER_PLUGIN_INSTALLED = 1968 ER_STATEMENT_TIMEOUT = 1969 ER_SUBQUERIES_NOT_SUPPORTED = 1970 ER_SET_STATEMENT_NOT_SUPPORTED = 1971 ER_USER_CREATE_EXISTS = 1973 ER_USER_DROP_EXISTS = 1974 ER_ROLE_CREATE_EXISTS = 1975 ER_ROLE_DROP_EXISTS = 1976 ER_CANNOT_CONVERT_CHARACTER = 1977 ER_INVALID_DEFAULT_VALUE_FOR_FIELD = 1978 ER_KILL_QUERY_DENIED_ERROR = 1979 ER_NO_EIS_FOR_FIELD = 1980 ER_WARN_AGGFUNC_DEPENDENCE = 1981 WARN_INNODB_PARTITION_OPTION_IGNORED = 1982 ER_ERROR_LAST_SECTION_2 = 1982 ER_ERROR_FIRST_SECTION_3 = 2000 ER_ERROR_LAST_SECTION_3 = 2000 ER_ERROR_FIRST_SECTION_4 = 3000 ER_FILE_CORRUPT = 3000 ER_ERROR_ON_MASTER = 3001 ER_INCONSISTENT_ERROR = 3002 ER_STORAGE_ENGINE_NOT_LOADED = 3003 ER_GET_STACKED_DA_WITHOUT_ACTIVE_HANDLER = 3004 ER_WARN_LEGACY_SYNTAX_CONVERTED = 3005 ER_BINLOG_UNSAFE_FULLTEXT_PLUGIN = 3006 ER_CANNOT_DISCARD_TEMPORARY_TABLE = 3007 ER_FK_DEPTH_EXCEEDED = 3008 ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE_V2 = 3009 ER_WARN_TRIGGER_DOESNT_HAVE_CREATED = 3010 ER_REFERENCED_TRG_DOES_NOT_EXIST_MYSQL = 3011 ER_EXPLAIN_NOT_SUPPORTED = 3012 ER_INVALID_FIELD_SIZE = 3013 ER_MISSING_HA_CREATE_OPTION = 3014 ER_ENGINE_OUT_OF_MEMORY = 3015 ER_PASSWORD_EXPIRE_ANONYMOUS_USER = 3016 ER_SLAVE_SQL_THREAD_MUST_STOP = 3017 ER_NO_FT_MATERIALIZED_SUBQUERY = 3018 ER_INNODB_UNDO_LOG_FULL = 3019 ER_INVALID_ARGUMENT_FOR_LOGARITHM = 3020 ER_SLAVE_CHANNEL_IO_THREAD_MUST_STOP = 3021 ER_WARN_OPEN_TEMP_TABLES_MUST_BE_ZERO = 3022 ER_WARN_ONLY_MASTER_LOG_FILE_NO_POS = 3023 ER_QUERY_TIMEOUT = 3024 ER_NON_RO_SELECT_DISABLE_TIMER = 3025 ER_DUP_LIST_ENTRY = 3026 ER_SQL_MODE_NO_EFFECT = 3027 ER_AGGREGATE_ORDER_FOR_UNION = 3028 ER_AGGREGATE_ORDER_NON_AGG_QUERY = 3029 ER_SLAVE_WORKER_STOPPED_PREVIOUS_THD_ERROR = 3030 ER_DONT_SUPPORT_SLAVE_PRESERVE_COMMIT_ORDER = 3031 ER_SERVER_OFFLINE_MODE = 3032 ER_GIS_DIFFERENT_SRIDS = 3033 ER_GIS_UNSUPPORTED_ARGUMENT = 3034 ER_GIS_UNKNOWN_ERROR = 3035 ER_GIS_UNKNOWN_EXCEPTION = 3036 ER_GIS_INVALID_DATA = 3037 ER_BOOST_GEOMETRY_EMPTY_INPUT_EXCEPTION = 3038 ER_BOOST_GEOMETRY_CENTROID_EXCEPTION = 3039 ER_BOOST_GEOMETRY_OVERLAY_INVALID_INPUT_EXCEPTION = 3040 ER_BOOST_GEOMETRY_TURN_INFO_EXCEPTION = 3041 ER_BOOST_GEOMETRY_SELF_INTERSECTION_POINT_EXCEPTION = 3042 ER_BOOST_GEOMETRY_UNKNOWN_EXCEPTION = 3043 ER_STD_BAD_ALLOC_ERROR = 3044 ER_STD_DOMAIN_ERROR = 3045 ER_STD_LENGTH_ERROR = 3046 ER_STD_INVALID_ARGUMENT = 3047 ER_STD_OUT_OF_RANGE_ERROR = 3048 ER_STD_OVERFLOW_ERROR = 3049 ER_STD_RANGE_ERROR = 3050 ER_STD_UNDERFLOW_ERROR = 3051 ER_STD_LOGIC_ERROR = 3052 ER_STD_RUNTIME_ERROR = 3053 ER_STD_UNKNOWN_EXCEPTION = 3054 ER_GIS_DATA_WRONG_ENDIANESS = 3055 ER_CHANGE_MASTER_PASSWORD_LENGTH = 3056 ER_USER_LOCK_WRONG_NAME = 3057 ER_USER_LOCK_DEADLOCK = 3058 ER_REPLACE_INACCESSIBLE_ROWS = 3059 ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_GIS = 3060 ER_ERROR_LAST_SECTION_4 = 3060 ER_ERROR_FIRST_SECTION_5 = 4000 ER_WITH_COL_WRONG_LIST = 4002 ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE = 4003 ER_DUP_QUERY_NAME = 4004 ER_RECURSIVE_WITHOUT_ANCHORS = 4005 ER_UNACCEPTABLE_MUTUAL_RECURSION = 4006 ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED = 4007 ER_NOT_STANDARD_COMPLIANT_RECURSIVE = 4008 ER_WRONG_WINDOW_SPEC_NAME = 4009 ER_DUP_WINDOW_NAME = 4010 ER_PARTITION_LIST_IN_REFERENCING_WINDOW_SPEC = 4011 ER_ORDER_LIST_IN_REFERENCING_WINDOW_SPEC = 4012 ER_WINDOW_FRAME_IN_REFERENCED_WINDOW_SPEC = 4013 ER_BAD_COMBINATION_OF_WINDOW_FRAME_BOUND_SPECS = 4014 ER_WRONG_PLACEMENT_OF_WINDOW_FUNCTION = 4015 ER_WINDOW_FUNCTION_IN_WINDOW_SPEC = 4016 ER_NOT_ALLOWED_WINDOW_FRAME = 4017 ER_NO_ORDER_LIST_IN_WINDOW_SPEC = 4018 ER_RANGE_FRAME_NEEDS_SIMPLE_ORDERBY = 4019 ER_WRONG_TYPE_FOR_ROWS_FRAME = 4020 ER_WRONG_TYPE_FOR_RANGE_FRAME = 4021 ER_FRAME_EXCLUSION_NOT_SUPPORTED = 4022 ER_WINDOW_FUNCTION_DONT_HAVE_FRAME = 4023 ER_INVALID_NTILE_ARGUMENT = 4024 ER_CONSTRAINT_FAILED = 4025 ER_EXPRESSION_IS_TOO_BIG = 4026 ER_ERROR_EVALUATING_EXPRESSION = 4027 ER_CALCULATING_DEFAULT_VALUE = 4028 ER_EXPRESSION_REFERS_TO_UNINIT_FIELD = 4029 ER_PARTITION_DEFAULT_ERROR = 4030 ER_REFERENCED_TRG_DOES_NOT_EXIST = 4031 ER_INVALID_DEFAULT_PARAM = 4032 ER_BINLOG_NON_SUPPORTED_BULK = 4033 ER_BINLOG_UNCOMPRESS_ERROR = 4034 ER_JSON_BAD_CHR = 4035 ER_JSON_NOT_JSON_CHR = 4036 ER_JSON_EOS = 4037 ER_JSON_SYNTAX = 4038 ER_JSON_ESCAPING = 4039 ER_JSON_DEPTH = 4040 ER_JSON_PATH_EOS = 4041 ER_JSON_PATH_SYNTAX = 4042 ER_JSON_PATH_DEPTH = 4043 ER_JSON_PATH_NO_WILDCARD = 4044 ER_JSON_PATH_ARRAY = 4045 ER_JSON_ONE_OR_ALL = 4046 ER_UNSUPPORTED_COMPRESSED_TABLE = 4047 ER_GEOJSON_INCORRECT = 4048 ER_GEOJSON_TOO_FEW_POINTS = 4049 ER_GEOJSON_NOT_CLOSED = 4050 ER_JSON_PATH_EMPTY = 4051 ER_SLAVE_SAME_ID = 4052 ER_FLASHBACK_NOT_SUPPORTED = 4053 ER_KEYS_OUT_OF_ORDER = 4054 ER_OVERLAPPING_KEYS = 4055 ER_REQUIRE_ROW_BINLOG_FORMAT = 4056 ER_ISOLATION_MODE_NOT_SUPPORTED = 4057 ER_ON_DUPLICATE_DISABLED = 4058 ER_UPDATES_WITH_CONSISTENT_SNAPSHOT = 4059 ER_ROLLBACK_ONLY = 4060 ER_ROLLBACK_TO_SAVEPOINT = 4061 ER_ISOLATION_LEVEL_WITH_CONSISTENT_SNAPSHOT = 4062 ER_UNSUPPORTED_COLLATION = 4063 ER_METADATA_INCONSISTENCY = 4064 ER_CF_DIFFERENT = 4065 ER_RDB_TTL_DURATION_FORMAT = 4066 ER_RDB_STATUS_GENERAL = 4067 ER_RDB_STATUS_MSG = 4068 ER_RDB_TTL_UNSUPPORTED = 4069 ER_RDB_TTL_COL_FORMAT = 4070 ER_PER_INDEX_CF_DEPRECATED = 4071 ER_KEY_CREATE_DURING_ALTER = 4072 ER_SK_POPULATE_DURING_ALTER = 4073 ER_SUM_FUNC_WITH_WINDOW_FUNC_AS_ARG = 4074 ER_NET_OK_PACKET_TOO_LARGE = 4075 ER_GEOJSON_EMPTY_COORDINATES = 4076 ER_MYROCKS_CANT_NOPAD_COLLATION = 4077 ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION = 4078 ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION = 4079 ER_WRONG_PARAMCOUNT_TO_CURSOR = 4080 ER_UNKNOWN_STRUCTURED_VARIABLE = 4081 ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD = 4082 ER_END_IDENTIFIER_DOES_NOT_MATCH = 4083 ER_SEQUENCE_RUN_OUT = 4084 ER_SEQUENCE_INVALID_DATA = 4085 ER_SEQUENCE_INVALID_TABLE_STRUCTURE = 4086 ER_SEQUENCE_ACCESS_ERROR = 4087 ER_SEQUENCE_BINLOG_FORMAT = 4088 ER_NOT_SEQUENCE = 4089 ER_NOT_SEQUENCE2 = 4090 ER_UNKNOWN_SEQUENCES = 4091 ER_UNKNOWN_VIEW = 4092 ER_WRONG_INSERT_INTO_SEQUENCE = 4093 ER_SP_STACK_TRACE = 4094 ER_PACKAGE_ROUTINE_IN_SPEC_NOT_DEFINED_IN_BODY = 4095 ER_PACKAGE_ROUTINE_FORWARD_DECLARATION_NOT_DEFINED = 4096 ER_COMPRESSED_COLUMN_USED_AS_KEY = 4097 ER_UNKNOWN_COMPRESSION_METHOD = 4098 ER_WRONG_NUMBER_OF_VALUES_IN_TVC = 4099 ER_FIELD_REFERENCE_IN_TVC = 4100 ER_WRONG_TYPE_FOR_PERCENTILE_FUNC = 4101 ER_ARGUMENT_NOT_CONSTANT = 4102 ER_ARGUMENT_OUT_OF_RANGE = 4103 ER_WRONG_TYPE_OF_ARGUMENT = 4104 ER_NOT_AGGREGATE_FUNCTION = 4105 ER_INVALID_AGGREGATE_FUNCTION = 4106 ER_INVALID_VALUE_TO_LIMIT = 4107 ER_INVISIBLE_NOT_NULL_WITHOUT_DEFAULT = 4108 ER_UPDATE_INFO_WITH_SYSTEM_VERSIONING = 4109 ER_VERS_FIELD_WRONG_TYPE = 4110 ER_VERS_ENGINE_UNSUPPORTED = 4111 ER_PARTITION_WRONG_TYPE = 4113 WARN_VERS_PART_FULL = 4114 WARN_VERS_PARAMETERS = 4115 ER_VERS_DROP_PARTITION_INTERVAL = 4116 WARN_VERS_PART_NON_HISTORICAL = 4118 ER_VERS_ALTER_NOT_ALLOWED = 4119 ER_VERS_ALTER_ENGINE_PROHIBITED = 4120 ER_VERS_RANGE_PROHIBITED = 4121 ER_CONFLICTING_FOR_SYSTEM_TIME = 4122 ER_VERS_TABLE_MUST_HAVE_COLUMNS = 4123 ER_VERS_NOT_VERSIONED = 4124 ER_MISSING = 4125 ER_VERS_PERIOD_COLUMNS = 4126 ER_PART_WRONG_VALUE = 4127 ER_VERS_WRONG_PARTS = 4128 ER_VERS_NO_TRX_ID = 4129 ER_VERS_ALTER_SYSTEM_FIELD = 4130 ER_DROP_VERSIONING_SYSTEM_TIME_PARTITION = 4131 ER_VERS_DB_NOT_SUPPORTED = 4132 ER_VERS_TRT_IS_DISABLED = 4133 ER_VERS_DUPLICATE_ROW_START_END = 4134 ER_VERS_ALREADY_VERSIONED = 4135 ER_VERS_NOT_SUPPORTED = 4137 ER_VERS_TRX_PART_HISTORIC_ROW_NOT_SUPPORTED = 4138 ER_INDEX_FILE_FULL = 4139 ER_UPDATED_COLUMN_ONLY_ONCE = 4140 ER_EMPTY_ROW_IN_TVC = 4141 ER_VERS_QUERY_IN_PARTITION = 4142 ER_KEY_DOESNT_SUPPORT = 4143 ER_ALTER_OPERATION_TABLE_OPTIONS_NEED_REBUILD = 4144 ER_BACKUP_LOCK_IS_ACTIVE = 4145 ER_BACKUP_NOT_RUNNING = 4146 ER_BACKUP_WRONG_STAGE = 4147 ER_BACKUP_STAGE_FAILED = 4148 ER_BACKUP_UNKNOWN_STAGE = 4149 ER_USER_IS_BLOCKED = 4150 ER_ACCOUNT_HAS_BEEN_LOCKED = 4151 ER_PERIOD_TEMPORARY_NOT_ALLOWED = 4152 ER_PERIOD_TYPES_MISMATCH = 4153 ER_MORE_THAN_ONE_PERIOD = 4154 ER_PERIOD_FIELD_WRONG_ATTRIBUTES = 4155 ER_PERIOD_NOT_FOUND = 4156 ER_PERIOD_COLUMNS_UPDATED = 4157 ER_PERIOD_CONSTRAINT_DROP = 4158 ER_TOO_LONG_KEYPART = 4159 ER_TOO_LONG_DATABASE_COMMENT = 4160 ER_UNKNOWN_DATA_TYPE = 4161 ER_UNKNOWN_OPERATOR = 4162 ER_WARN_HISTORY_ROW_START_TIME = 4163 ER_PART_STARTS_BEYOND_INTERVAL = 4164 ER_GALERA_REPLICATION_NOT_SUPPORTED = 4165 ER_LOAD_INFILE_CAPABILITY_DISABLED = 4166 ER_NO_SECURE_TRANSPORTS_CONFIGURED = 4167 ER_SLAVE_IGNORED_SHARED_TABLE = 4168 ER_NO_AUTOINCREMENT_WITH_UNIQUE = 4169 ER_KEY_CONTAINS_PERIOD_FIELDS = 4170 ER_KEY_CANT_HAVE_WITHOUT_OVERLAPS = 4171 ER_NOT_ALLOWED_IN_THIS_CONTEXT = 4172 ER_DATA_WAS_COMMITED_UNDER_ROLLBACK = 4173 ER_PK_INDEX_CANT_BE_IGNORED = 4174 ER_BINLOG_UNSAFE_SKIP_LOCKED = 4175 ER_JSON_TABLE_ERROR_ON_FIELD = 4176 ER_JSON_TABLE_ALIAS_REQUIRED = 4177 ER_JSON_TABLE_SCALAR_EXPECTED = 4178 ER_JSON_TABLE_MULTIPLE_MATCHES = 4179 ER_WITH_TIES_NEEDS_ORDER = 4180 ER_REMOVED_ORPHAN_TRIGGER = 4181 ER_STORAGE_ENGINE_DISABLED = 4182 WARN_SFORMAT_ERROR = 4183 ER_PARTITION_CONVERT_SUBPARTITIONED = 4184 ER_PROVIDER_NOT_LOADED = 4185 ER_JSON_HISTOGRAM_PARSE_FAILED = 4186 ER_SF_OUT_INOUT_ARG_NOT_ALLOWED = 4187 ER_INCONSISTENT_SLAVE_TEMP_TABLE = 4188 CR_UNKNOWN_ERROR = 2000 CR_SOCKET_CREATE_ERROR = 2001 CR_CONNECTION_ERROR = 2002 CR_CONN_HOST_ERROR = 2003 CR_IPSOCK_ERROR = 2004 CR_UNKNOWN_HOST = 2005 CR_SERVER_GONE_ERROR = 2006 CR_VERSION_ERROR = 2007 CR_OUT_OF_MEMORY = 2008 CR_WRONG_HOST_INFO = 2009 CR_LOCALHOST_CONNECTION = 2010 CR_TCP_CONNECTION = 2011 CR_SERVER_HANDSHAKE_ERR = 2012 CR_SERVER_LOST = 2013 CR_COMMANDS_OUT_OF_SYNC = 2014 CR_NAMEDPIPE_CONNECTION = 2015 CR_NAMEDPIPEWAIT_ERROR = 2016 CR_NAMEDPIPEOPEN_ERROR = 2017 CR_NAMEDPIPESETSTATE_ERROR = 2018 CR_CANT_READ_CHARSET = 2019 CR_NET_PACKET_TOO_LARGE = 2020 CR_SSL_CONNECTION_ERROR = 2026 CR_MALFORMED_PACKET = 2027 CR_NO_PREPARE_STMT = 2030 CR_PARAMS_NOT_BOUND = 2031 CR_INVALID_PARAMETER_NO = 2034 CR_INVALID_BUFFER_USE = 2035 CR_UNSUPPORTED_PARAM_TYPE = 2036 CR_SHARED_MEMORY_CONNECTION = 2037 CR_SHARED_MEMORY_CONNECT_ERROR = 2038 CR_CONN_UNKNOWN_PROTOCOL = 2047 CR_SECURE_AUTH = 2049 CR_NO_DATA = 2051 CR_NO_STMT_METADATA = 2052 CR_NOT_IMPLEMENTED = 2054 CR_SERVER_LOST_EXTENDED = 2055 CR_STMT_CLOSED = 2056 CR_NEW_STMT_METADATA = 2057 CR_ALREADY_CONNECTED = 2058 CR_AUTH_PLUGIN_CANNOT_LOAD = 2059 CR_DUPLICATE_CONNECTION_ATTR = 2060 CR_AUTH_PLUGIN_ERR = 2061 CR_EVENT_CREATE_FAILED = 5000 CR_BIND_ADDR_FAILED = 5001 CR_ASYNC_NOT_SUPPORTED = 5002 CR_FUNCTION_NOT_SUPPORTED = 5003 CR_FILE_NOT_FOUND = 5004 CR_FILE_READ = 5005 CR_BULK_WITHOUT_PARAMETERS = 5006 CR_INVALID_STMT = 5007 CR_VERSION_MISMATCH = 5008 CR_INVALID_PARAMETER = 5009 CR_PLUGIN_NOT_ALLOWED = 5010 CR_CONNSTR_PARSE_ERROR = 5011 CR_ERR_LOAD_PLUGIN = 5012 mariadb-connector-python-1.1.10/mariadb/constants/EXT_FIELD_TYPE.py000066400000000000000000000005431456007477700247140ustar00rootroot00000000000000""" MariaDB EXT_FIELD_TYPE Constants These constants represent the extended field types supported by MariaDB. Extended field types are defined in module *mariadb.constants.EXT_FIELD_TYPE* """ NONE =0 JSON = 1 UUID = 2 INET4 = 3 INET6 = 4 POINT = 5 MULTIPOINT = 6 LINESTRING = 7 MULTILINESTRING = 8 POLYGON = 9 MULTIPOLYGON = 10 GEOMETRYCOLLECTION = 11 mariadb-connector-python-1.1.10/mariadb/constants/FIELD_FLAG.py000066400000000000000000000011411456007477700241170ustar00rootroot00000000000000"""MariaDB FIELD_FLAG Constants These constants represent the various field flags. As an addition to the DBAPI 2.0 standard (PEP-249) these flags are returned as eighth element of the cursor description attribute. Field flags are defined in module *mariadb.constants.FIELD_FLAG* """ # Source: mariadb_com.h (MariaDB Connector(C) NOT_NULL = 1 PRIMARY_KEY = 2 UNIQUE_KEY = 4 MULTIPLE_KEY = 8 BLOB = 16 UNSIGNED = 32 ZEROFILL = 64 BINARY = 128 ENUM = 256 AUTO_INCREMENT = 512 TIMESTAMP = 1024 SET = 2048 NO_DEFAULT = 4096 ON_UPDATE_NOW = 8192 NUMERIC = 32768 PART_OF_KEY = 16384 GROUP = 32768 UNIQUE = 65536 mariadb-connector-python-1.1.10/mariadb/constants/FIELD_TYPE.py000066400000000000000000000011661456007477700241760ustar00rootroot00000000000000""" MariaDB FIELD_TYPE Constants These constants represent the field types supported by MariaDB. The field type is returned as second element of cursor description attribute. Field types are defined in module *mariadb.constants.FIELD_TYPE* """ DECIMAL = 0 TINY = 1 SHORT = 2 LONG = 3 FLOAT = 4 DOUBLE = 5 NULL = 6 TIMESTAMP = 7 LONGLONG = 8 INT24 = 9 DATE = 10 TIME = 11 DATETIME = 12 YEAR = 13 NEWDATE = 14 VARCHAR = 15 BIT = 16 TIMESTAMP2 = 17 DATETIME2 = 18 TIME2 = 19 JSON = 245 NEWDECIMAL = 246 ENUM = 247 SET = 248 TINY_BLOB = 249 MEDIUM_BLOB = 250 LONG_BLOB = 251 BLOB = 252 VAR_STRING = 253 STRING = 254 GEOMETRY = 255 mariadb-connector-python-1.1.10/mariadb/constants/INDICATOR.py000066400000000000000000000005421456007477700240230ustar00rootroot00000000000000''' MariaDB indicator variables Indicator values are used in executemany() method of cursor class to indicate special values. ''' class MrdbIndicator(): indicator = 0 def __init__(self, indicator): self.indicator = indicator NULL = MrdbIndicator(1) DEFAULT = MrdbIndicator(2) IGNORE = MrdbIndicator(3) IGNORE_ROW = MrdbIndicator(4) mariadb-connector-python-1.1.10/mariadb/constants/INFO.py000066400000000000000000000012701456007477700232410ustar00rootroot00000000000000""" Constants for _get_info method of MariadB connection object """ CHARSET_ID = 0 CHARSET_NAME = 1 CLIENT_ERRORS = 2 CLIENT_VERSION = 3 CLIENT_VERSION_ID = 4 ASYNC_TIMEOUT = 5 ASYNC_TIMEOUT_MS = 6 CHARSET_INFO = 7 ERROR = 8 ERROR_ID = 9 HOST = 10 INFO = 11 PORT = 12 PROTOCOL_VERSION_ID = 13 PVIO_TYPE = 14 SCHEMA = 15 SERVER_TYPE = 16 SERVER_VERSION = 17 SERVER_VERSION_ID = 18 SOCKET = 19 SQLSTATE = 20 SSL_CIPHER = 21 TLS_LIBRARY = 22 TLS_VERSION = 23 TLS_VERSION_ID = 24 TYPE = 25 UNIX_SOCKET = 26 USER = 27 MAX_ALLOWED_PACKET = 28 NET_BUFFER_LENGTH = 29 SERVER_STATUS = 30 SERVER_CAPABILITIES = 31 EXTENDED_SERVER_CAPABILITIES = 32 CLIENT_CAPABILITIES = 33 BYTES_READ = 34 BYTES_SENT = 35 mariadb-connector-python-1.1.10/mariadb/constants/STATUS.py000066400000000000000000000006521456007477700235340ustar00rootroot00000000000000''' MariaDB status flags These flags describe the current status of the database server. ''' IN_TRANS = 1 AUTOCOMMIT = 2 MORE_RESULTS_EXIST = 8 QUERY_NO_GOOD_INDEX_USED = 16 QUERY_NO_INDEX_USED = 32 CURSOR_EXISTS = 64 LAST_ROW_SENT = 128 DB_DROPPED = 256 NO_BACKSLASH_ESCAPES = 512 METADATA_CHANGED = 1024 QUERY_WAS_SLOW = 2048 PS_OUT_PARAMS = 4096 IN_TRANS_READONLY = 8192 SESSION_STATE_CHANGED = 16384 ANSI_QUOTES = 32768 mariadb-connector-python-1.1.10/mariadb/constants/TPC_STATE.py000066400000000000000000000000351456007477700240720ustar00rootroot00000000000000NONE = 0 XID = 1 PREPARE = 2 mariadb-connector-python-1.1.10/mariadb/constants/__init__.py000066400000000000000000000001621456007477700242440ustar00rootroot00000000000000__all__ = ["CLIENT", "CURSOR", "FIELD_TYPE", "FIELD_FLAG", "INDICATOR", 'STATUS', 'ERR', 'CAPABILITY'] mariadb-connector-python-1.1.10/mariadb/cursors.py000066400000000000000000000463171456007477700222050ustar00rootroot00000000000000# # Copyright (C) 2020-2021 Georg Richter and MariaDB Corporation AB # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # You should have received a copy of the GNU Library General Public # License along with this library; if not see # or write to the Free Software Foundation, Inc., # 51 Franklin St., Fifth Floor, Boston, MA 02110, USA # import mariadb import datetime from numbers import Number from mariadb.constants import CURSOR, STATUS, CAPABILITY, INDICATOR from typing import Sequence PARAMSTYLE_QMARK = 1 PARAMSTYLE_FORMAT = 2 PARAMSTYLE_PYFORMAT = 3 ROWS_ALL = -1 RESULT_TUPLE = 0 RESULT_NAMEDTUPLE = 1 RESULT_DICTIONARY = 2 # Command types SQL_NONE = 0, SQL_INSERT = 1 SQL_UPDATE = 2 SQL_REPLACE = 3 SQL_DELETE = 4 SQL_CALL = 5 SQL_DO = 6 SQL_SELECT = 7 SQL_OTHER = 255 ROWS_EOF = -1 class Cursor(mariadb._mariadb.cursor): """ MariaDB Connector/Python Cursor Object """ def check_closed(self): if self.closed: self._connection._check_closed() raise mariadb.ProgrammingError("Cursor is closed") def __init__(self, connection, **kwargs): """ initialization """ self._bulk = False self._dictionary = False self._named_tuple = False self._connection = connection self._resulttype = RESULT_TUPLE self._description = None self._transformed_statement = None self._prepared = False self._prev_stmt = None self._force_binary = None self._rowcount = 0 self.buffered = True self._parseinfo = None self._data = None if not connection: raise mariadb.ProgrammingError("Invalid or no connection provided") # parse keywords if kwargs: rtype = kwargs.pop("named_tuple", False) if rtype: self._resulttype = RESULT_NAMEDTUPLE else: rtype = kwargs.pop("dictionary", False) if rtype: self._resulttype = RESULT_DICTIONARY buffered = kwargs.pop("buffered", True) self.buffered = buffered self._prepared = kwargs.pop("prepared", False) self._force_binary = kwargs.pop("binary", False) self._cursor_type = kwargs.pop("cursor_type", 0) # call initialization of main class super().__init__(connection, **kwargs) def _substitute_parameters(self): """ Internal use only. When running in text protocol, this method will replace placeholders by supplied values. For values which aren't numbers, strings or bytes string representation will be used. """ new_stmt = self.statement.encode("utf8") replace_diff = 0 if self._paramlist: for i in range(0, len(self._paramlist)): extra_bytes = 0 if self._paramstyle == PARAMSTYLE_PYFORMAT: val = self._data[self._keys[i]] else: val = self._data[i] if val is None: replace = "NULL" else: if isinstance(val, INDICATOR.MrdbIndicator): if val == INDICATOR.NULL: replace = "NULL" if val == INDICATOR.DEFAULT: replace = "DEFAULT" elif isinstance(val, Number): replace = val.__str__() else: if isinstance(val, (bytes, bytearray)): replace = "'%s'" % self.connection.escape_string( val.decode(encoding='latin1')) else: replace = "'%s'" % self.connection.escape_string( val.__str__()) extra_bytes = len(replace.encode("utf-8")) -\ len(replace) ofs = self._paramlist[i] + replace_diff new_stmt = new_stmt[:ofs] + replace.__str__().encode("utf8") +\ new_stmt[ofs+1:] replace_diff += len(replace) - 1 + extra_bytes return new_stmt def _check_execute_params(self): # check data format if self._paramstyle in (PARAMSTYLE_QMARK, PARAMSTYLE_FORMAT): if not isinstance(self._data, (tuple, list)): raise mariadb.ProgrammingError("Data argument must be " "Tuple or List") if self._paramstyle == PARAMSTYLE_PYFORMAT: if not isinstance(self._data, dict): raise mariadb.ProgrammingError("Data argument must be " "Dictionary") for i in range(0, len(self._keys)): if self._keys[i] not in self._data: raise mariadb.ProgrammingError("Dictionary doesn't contain" " key '%s'" % self._keys[i]) else: # check if number of place holders matches the number of # supplied elements in data tuple if self._paramlist and ( (not self._data and len(self._paramlist) > 0) or (len(self._data) != len(self._paramlist))): raise mariadb.ProgrammingError( "statement (%s) doesn't match the number of data elements" " (%s)." % (len(self._paramlist), len(self._data))) def callproc(self, sp: str, data: Sequence = ()): """ Executes a stored procedure sp. The data sequence must contain an entry for each parameter the procedure expects. Input/Output or Output parameters have to be retrieved by .fetch methods, the .sp_outparams attribute indicates if the result set contains output parameters. Arguments: - sp: Name of stored procedure. - data: Optional sequence containing data for placeholder substitution. """ self.check_closed() # create statement params = "" if data and len(data): params = ("?," * len(data))[:-1] statement = "CALL %s(%s)" % (sp, params) self._rowcount = 0 self.execute(statement, data) def _parse_execute(self, statement: str, data=(), is_bulk=False): """ For internal use Parses SQL statement and checks parameters. """ if not statement: raise mariadb.ProgrammingError("empty statement") # parse statement if self.statement != statement or is_bulk and not self._bulk: super()._parse(statement) self._prev_stmt = statement self._reprepare = True else: self._reprepare = False self._transformed_statement = self.statement if self._cursor_type == CURSOR.READ_ONLY: self._text = False self._data = data self._check_execute_params() def nextset(self): """ Will make the cursor skip to the next available result set, discarding any remaining rows from the current set. """ self.check_closed() return super()._nextset() def execute(self, statement: str, data: Sequence = (), buffered=None): """ Prepare and execute a SQL statement. Parameters may be provided as sequence or mapping and will be bound to variables in the operation. Variables are specified as question marks (paramstyle ='qmark'), however for compatibility reasons MariaDB Connector/Python also supports the 'format' and 'pyformat' paramstyles with the restriction, that different paramstyles can't be mixed within a statement. A reference to the operation will be retained by the cursor. If the cursor was created with attribute prepared =True the statement string for following execute operations will be ignored. This is most effective for algorithms where the same operation is used, but different parameters are bound to it (many times). By default execute() method generates an buffered result unless the optional parameter buffered was set to False or the cursor was generated as an unbuffered cursor. """ self.check_closed() self.connection._last_executed_statement = statement # Parse statement do_parse = True self._rowcount = 0 if buffered is not None: self.buffered = buffered # clear pending result sets if self.field_count: self._clear_result() # if we have a prepared cursor, we have to set statement # to previous statement and don't need to parse if self._prepared and self.statement: statement = self.statement do_parse = False # parse statement and check param style if do_parse: self._parse_execute(statement, (data)) self._description = None # CONPY-218: Allow None as replacement for empty tuple data = data or () if len(data): self._data = data else: self._data = None # If statement doesn't contain parameters we force to run in text # mode, unless a server side cursor or stored procedure will be # executed. if self._command != SQL_CALL and self._cursor_type == 0: self._text = True if self._force_binary: self._text = False # if one of the provided parameters has byte or datetime value, # we don't use text protocol if data and self._check_text_types() == True: self._text = False if self._text: # in text mode we need to substitute parameters # and store transformed statement if (self.paramcount > 0): self._transformed_statement = self._substitute_parameters() else: self._transformed_statement = self.statement self._execute_text(self._transformed_statement) self._readresponse() else: self._data = data self._execute_binary() self._initresult() self._bulk = 0 def executemany(self, statement, parameters): """ Prepare a database operation (INSERT,UPDATE,REPLACE or DELETE statement) and execute it against all parameter found in sequence. Exactly behaves like .execute() but accepts a list of tuples, where each tuple represents data of a row within a table. .executemany() only supports DML (insert, update, delete) statements. If the SQL statement contains a RETURNING clause, executemany() returns a result set containing the values for columns listed in the RETURNING clause. """ self.check_closed() if not parameters or not len(parameters): raise mariadb.ProgrammingError("No data provided") self.connection._last_executed_statement = statement # clear pending results if self.field_count: self._clear_result() # If the server doesn't support bulk operations, we need to emulate # by looping # TODO: insert/replace statements are not optimized yet # rowcount updating if not (self.connection.extended_server_capabilities & (CAPABILITY.BULK_OPERATIONS >> 32)): count = 0 for row in parameters: self.execute(statement, row) count += self.rowcount self._rowcount = count else: # parse statement self._parse_execute(statement, parameters[0], is_bulk=True) self._data = parameters self.is_text = False self._rowcount = 0 self._execute_bulk() self._bulk = 1 def _fetch_row(self): """ Internal use only fetches row and converts values, if connection has a converter. """ if not self.buffered: self.check_closed() # if there is no result set, PEP-249 requires to raise an # exception if not self.field_count: raise mariadb.ProgrammingError("Cursor doesn't have a result set") return super().fetchone() def close(self): """ Closes the cursor. If the cursor has pending or unread results, .close() will cancel them so that further operations using the same connection can be executed. The cursor will be unusable from this point forward; an Error (or subclass) exception will be raised if any operation is attempted with the cursor." """ # CONPY-231: fix memory leak if self._data: del self._data if not self.connection._closed: super().close() def fetchone(self): """ Fetch the next row of a query result set, returning a single sequence, or None if no more data is available. An exception will be raised if the previous call to execute() didn't produce a result set or execute() wasn't called before. """ if not self.buffered: self.check_closed() row = self._fetch_row() return row def fetchmany(self, size: int = 0): """ Fetch the next set of rows of a query result, returning a sequence of sequences (e.g. a list of tuples). An empty sequence is returned when no more rows are available. The number of rows to fetch per call is specified by the parameter. If it is not given, the cursor's arraysize determines the number of rows to be fetched. The method should try to fetch as many rows as indicated by the size parameter. If this is not possible due to the specified number of rows not being available, fewer rows may be returned. An exception will be raised if the previous call to execute() didn't produce a result set or execute() wasn't called before. """ if not self.buffered: self.check_closed() if size == 0: size = self.arraysize return super().fetchrows(size) def fetchall(self): """ Fetch all remaining rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples). An exception will be raised if the previous call to execute() didn't produce a result set or execute() wasn't called before. """ if not self.buffered: self.check_closed() return super().fetchrows(ROWS_EOF) def __iter__(self): return iter(self.fetchone, None) def scroll(self, value: int, mode="relative"): """ Scroll the cursor in the result set to a new position according to mode. If mode is "relative" (default), value is taken as offset to the current position in the result set, if set to absolute, value states an absolute target position. """ if self.field_count == 0: raise mariadb.ProgrammingError("Cursor doesn't have a result set") if not self.buffered: raise mariadb.ProgrammingError("This method is available only " "for cursors with a buffered " "result set.") if mode != "absolute" and mode != "relative": raise mariadb.ProgrammingError("Invalid or unknown scroll " "mode specified.") if value == 0 and mode != "absolute": raise mariadb.ProgrammingError("Invalid position value 0.") if mode == "relative": if self.rownumber + value < 0 or \ self.rownumber + value > self.rowcount: raise mariadb.ProgrammingError("Position value " "is out of range.") new_pos = self.rownumber + value else: if value < 0 or value >= self.rowcount: raise mariadb.ProgrammingError("Position value " "is out of range.") new_pos = value self._seek(new_pos) self._rownumber = new_pos def setinputsizes(self, size: int): """ Required by PEP-249. Does nothing in MariaDB Connector/Python """ return def setoutputsize(self, size: int): """ Required by PEP-249. Does nothing in MariaDB Connector/Python """ return def __enter__(self): """Returns a copy of the cursor.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Closes cursor.""" self.close() @property def rowcount(self): """ This read-only attribute specifies the number of rows that the last\ execute*() produced (for DQL statements like SELECT) or affected (for DML statements like UPDATE or INSERT). The return value is -1 in case no .execute*() has been performed on the cursor or the rowcount of the last operation cannot be determined by the interface. """ # Even if PEP-249 permits operations on a closed cursor, we don't # raise an exception if the cursor or the underlying connection # was closed (See CONPY-269), instead we will return -1 if not self.buffered: try: self.check_closed() except mariadb.ProgrammingError: return -1 if self._rowcount > 0: return self._rowcount return super().rowcount @property def sp_outparams(self): """ Indicates if the current result set contains in out or out parameter from a previous executed stored procedure """ self.check_closed() return bool(self.connection.server_status & STATUS.PS_OUT_PARAMS) @property def lastrowid(self): """ Returns the ID generated by a query on a table with a column having the AUTO_INCREMENT attribute or the value for the last usage of LAST_INSERT_ID(). If the last query wasn't an INSERT or UPDATE statement or if the modified table does not have a column with the AUTO_INCREMENT attribute and LAST_INSERT_ID was not used, the returned value will be None """ self.check_closed() id = self.insert_id if id > 0: return id return None @property def connection(self): """ Read-Only attribute which returns the reference to the connection object on which the cursor was created. """ return self._connection mariadb-connector-python-1.1.10/mariadb/dbapi20.py000066400000000000000000000063571456007477700217260ustar00rootroot00000000000000from mariadb.constants import FIELD_TYPE import time import datetime apilevel = '2.0' paramstyle = 'qmark' threadsafety = True class DbApiType(frozenset): """ Immutable set for type checking By default the following sets are defined: - BINARY: for binary field types - NUMBER: for numeric field types - STRING: for character based (string) field types - DATE: for date field type(s) - DATETIME: for datetime and timestamp field type(s) - TIME: for time field type(s) - TIMESTAMP: for datetime and timestamp field type(s) Example: >>> FIELD_TYPE.GEOMETRY == mariadb.BINARY True >>> FIELD_TYPE.FLOAT == mariadb.BINARY False """ def __eq__(self, field_type): if (isinstance(field_type, DbApiType)): return not self.difference(field_type) return field_type in self BINARY = DbApiType([FIELD_TYPE.GEOMETRY, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB, FIELD_TYPE.TINY_BLOB, FIELD_TYPE.BLOB]) STRING = DbApiType([FIELD_TYPE.ENUM, FIELD_TYPE.JSON, FIELD_TYPE.STRING, FIELD_TYPE.VARCHAR, FIELD_TYPE.VAR_STRING]) NUMBER = DbApiType([FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT, FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG, FIELD_TYPE.NEWDECIMAL, FIELD_TYPE.SHORT, FIELD_TYPE.TINY, FIELD_TYPE.YEAR]) DATE = DbApiType([FIELD_TYPE.DATE]) TIME = DbApiType([FIELD_TYPE.TIME]) DATETIME = TIMESTAMP = DbApiType([FIELD_TYPE.DATETIME, FIELD_TYPE.TIMESTAMP]) ROWID = DbApiType() def Binary(object): """Constructs an object capable of holding a binary value.""" return bytes(object) def Date(year, month, day): """Constructs an object holding a date value.""" return datetime.date(year, month, day) def Time(hour, minute, second): """Constructs an object holding a time value.""" return datetime.time(hour, minute, second) def Timestamp(year, month, day, hour, minute, second): """Constructs an object holding a datetime value.""" return datetime.datetime(year, month, day, hour, minute, second) def DateFromTicks(ticks): """Constructs an object holding a date value from the given ticks value (number of seconds since the epoch). For more information see the documentation of the standard Python time module.""" return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): """Constructs an object holding a time value from the given ticks value (number of seconds since the epoch). For more information see the documentation of the standard Python time module.""" return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): """Constructs an object holding a datetime value from the given ticks value (number of seconds since the epoch). For more information see the documentation of the standard Python time module.""" return datetime.datetime(*time.localtime(ticks)[:6]) mariadb-connector-python-1.1.10/mariadb/field.py000066400000000000000000000044731456007477700215650ustar00rootroot00000000000000from mariadb.constants import FIELD_TYPE, FIELD_FLAG field_types = {FIELD_TYPE.DECIMAL: "DECIMAL", FIELD_TYPE.TINY: "TINY", FIELD_TYPE.SHORT: "SHORT", FIELD_TYPE.LONG: "LONG", FIELD_TYPE.FLOAT: "FLOAT", FIELD_TYPE.DOUBLE: "DOUBLE", FIELD_TYPE.NULL: "NULL", FIELD_TYPE.TIMESTAMP: "TIMESTAMP", FIELD_TYPE.LONGLONG: "LONGLONG", FIELD_TYPE.INT24: "INT24", FIELD_TYPE.DATE: "DATE", FIELD_TYPE.TIME: "TIME", FIELD_TYPE.DATETIME: "DATETIME", FIELD_TYPE.YEAR: "YEAR", FIELD_TYPE.NEWDATE: "NEWDATE", FIELD_TYPE.VARCHAR: "VARCHAR", FIELD_TYPE.BIT: "BIT", FIELD_TYPE.JSON: "JSON", FIELD_TYPE.NEWDECIMAL: "NEWDECIMAL", FIELD_TYPE.ENUM: "ENUM", FIELD_TYPE.SET: "SET", FIELD_TYPE.TINY_BLOB: "TINY_BLOB", FIELD_TYPE.MEDIUM_BLOB: "MEDIUM_BLOB", FIELD_TYPE.LONG_BLOB: "LONG_BLOB", FIELD_TYPE.BLOB: "BLOB", FIELD_TYPE.VAR_STRING: "VAR_STRING", FIELD_TYPE.STRING: "STRING", FIELD_TYPE.GEOMETRY: "GEOMETRY"} field_flags = {FIELD_FLAG.NOT_NULL: "NOT_NULL", FIELD_FLAG.PRIMARY_KEY: "PRIMARY_KEY", FIELD_FLAG.UNIQUE_KEY: "UNIQUE_KEY", FIELD_FLAG.MULTIPLE_KEY: "PART_KEY", FIELD_FLAG.BLOB: "BLOB", FIELD_FLAG.UNSIGNED: "UNSIGNED", FIELD_FLAG.ZEROFILL: "ZEROFILL", FIELD_FLAG.BINARY: "BINARY", FIELD_FLAG.ENUM: "NUMERIC", FIELD_FLAG.AUTO_INCREMENT: "AUTO_INCREMENT", FIELD_FLAG.TIMESTAMP: "TIMESTAMP", FIELD_FLAG.SET: "SET", FIELD_FLAG.NO_DEFAULT: "NO_DEFAULT", FIELD_FLAG.ON_UPDATE_NOW: "UPDATE_TIMESTAMP", FIELD_FLAG.NUMERIC: "NUMERIC"} class fieldinfo(): def type(self, description): if description[1] in field_types: return field_types[description[1]] return None def flag(self, description): flags = [field_flags[f] for f in field_flags.keys() if description[7] & f] return " | ".join(flags) mariadb-connector-python-1.1.10/mariadb/mariadb.c000066400000000000000000000142421456007477700216660ustar00rootroot00000000000000/****************************************************************************** Copyright (C) 2018-2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ******************************************************************************/ #define MARIADB_CONNECTION #include "mariadb_python.h" #include "docs/module.h" #include "docs/exception.h" #include #include extern int codecs_datetime_init(void); PyObject *decimal_module= NULL, *decimal_type= NULL, *socket_module= NULL, *indicator_module= NULL; extern uint16_t max_pool_size; int Mariadb_traverse(PyObject *self, visitproc visit, void *arg) { return 0; } static PyMethodDef Mariadb_Methods[] = { /* PEP-249: mandatory */ {"connect", (PyCFunction)MrdbConnection_connect, METH_VARARGS | METH_KEYWORDS, module_connect__doc__}, /* Todo: add methods for api functions which don't require a connection */ {NULL} /* always last */ }; /* MariaDB module definition */ static struct PyModuleDef mariadb_module= { PyModuleDef_HEAD_INIT, "_mariadb", "MariaDB Connector for Python", -1, Mariadb_Methods }; static int mariadb_datetime_init(void) { PyDateTime_IMPORT; if (!PyDateTimeAPI) { PyErr_SetString(PyExc_ImportError, "DateTimeAPI initialization failed"); return 1; } return 0; } static void mariadb_add_exception(PyObject *module, PyObject **exception, const char *exception_name, PyObject *base_exception, const char *doc, const char *object_name) { *exception= PyErr_NewExceptionWithDoc(exception_name, doc, Mariadb_Error, NULL); Py_INCREF(*exception); PyModule_AddObject(module, object_name, *exception); } /* MariaDB module initialization function */ PyMODINIT_FUNC PyInit__mariadb(void) { PyObject *module= PyModule_Create(&mariadb_module); /* check if client library is compatible */ if (mysql_get_client_version() < MARIADB_PACKAGE_VERSION_ID) { char errmsg[255]; snprintf(errmsg, 254, "MariaDB Connector/Python was build with MariaDB Connector/C %s, " "while the loaded MariaDB Connector/C library has version %s.", MARIADB_PACKAGE_VERSION, mysql_get_client_info()); PyErr_SetString(PyExc_ImportError, errmsg); goto error; } /* Initialize DateTimeAPI */ if (mariadb_datetime_init() || codecs_datetime_init()) { goto error; } Py_SET_TYPE(&MrdbConnection_Type, &PyType_Type); if (PyType_Ready(&MrdbConnection_Type) == -1) { goto error; } /* Import Decimal support (CONPY-49) */ if (!(decimal_module= PyImport_ImportModule("decimal")) || !(decimal_type= PyObject_GetAttr(decimal_module, PyUnicode_FromString("Decimal")))) { goto error; } if (!(socket_module= PyImport_ImportModule("socket"))) { goto error; } Py_SET_TYPE(&MrdbCursor_Type, &PyType_Type); if (PyType_Ready(&MrdbCursor_Type) == -1) { goto error; } PyModule_AddObject(module, "cursor", (PyObject *)&MrdbCursor_Type); /* optional (MariaDB specific) globals */ PyModule_AddObject(module, "mariadbapi_version", PyUnicode_FromString(mysql_get_client_info())); Mariadb_Error= PyErr_NewException("mariadb.Error", PyExc_Exception, NULL); Py_INCREF(Mariadb_Error); PyModule_AddObject(module, "Error", Mariadb_Error); mariadb_add_exception(module, &Mariadb_InterfaceError, "mariadb.InterfaceError", Mariadb_Error, exception_interface__doc__, "InterfaceError"); mariadb_add_exception(module, &Mariadb_DatabaseError, "mariadb.DatabaseError", Mariadb_Error, exception_database__doc__, "DatabaseError"); mariadb_add_exception(module, &Mariadb_OperationalError, "mariadb.OperationalError", Mariadb_Error, exception_operational__doc__, "OperationalError"); mariadb_add_exception(module, &Mariadb_Warning, "mariadb.Warning", NULL, exception_warning__doc__, "Warning"); mariadb_add_exception(module, &Mariadb_IntegrityError, "mariadb.IntegrityError", Mariadb_Error, exception_integrity__doc__, "IntegrityError"); mariadb_add_exception(module, &Mariadb_InternalError, "mariadb.InternalError", Mariadb_Error, exception_internal__doc__, "InternalError"); mariadb_add_exception(module, &Mariadb_ProgrammingError, "mariadb.ProgrammingError", Mariadb_Error, exception_programming__doc__, "ProgrammingError"); mariadb_add_exception(module, &Mariadb_NotSupportedError, "mariadb.NotSupportedError", Mariadb_Error, exception_notsupported__doc__, "NotSupportedError"); mariadb_add_exception(module, &Mariadb_DataError, "mariadb.DataError", Mariadb_DatabaseError, exception_data__doc__, "DataError"); mariadb_add_exception(module, &Mariadb_PoolError, "mariadb.PoolError", Mariadb_Error, exception_pool__doc__, "PoolError"); Py_INCREF(&MrdbConnection_Type); PyModule_AddObject(module, "connection", (PyObject *)&MrdbConnection_Type); return module; error: if (PyErr_Occurred()) { return NULL; } PyErr_SetString(PyExc_ImportError, "Mariadb module initialization failed."); return NULL; } mariadb-connector-python-1.1.10/mariadb/mariadb_codecs.c000066400000000000000000001324611456007477700232120ustar00rootroot00000000000000/***************************************************************************** Copyright (C) 2018-2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ****************************************************************************/ #include "mariadb_python.h" #include #define CHARSET_BINARY 63 #define IS_DECIMAL_TYPE(type) \ ((type) == MYSQL_TYPE_NEWDECIMAL || (type) == MYSQL_TYPE_DOUBLE || (type) == MYSQL_TYPE_FLOAT) long MrdbIndicator_AsLong(PyObject *column) { PyObject *pyLong= PyObject_GetAttrString(column, "indicator"); return PyLong_AsLong(pyLong); } int codecs_datetime_init(void) { PyDateTime_IMPORT; if (!PyDateTimeAPI) { PyErr_SetString(PyExc_ImportError, "DateTimeAPI initialization failed"); return 1; } return 0; } Mrdb_ExtFieldType extended_field_types[] = { {EXT_TYPE_JSON, {"json", 4}}, {EXT_TYPE_UUID, {"uuid", 4}}, {EXT_TYPE_INET4, {"inet4", 5}}, {EXT_TYPE_INET6, {"inet6", 5}}, {EXT_TYPE_POINT, {"point", 5}}, {EXT_TYPE_MULTIPOINT, {"multipoint", 10}}, {EXT_TYPE_LINESTRING, {"linestring", 10}}, {EXT_TYPE_MULTILINESTRING, {"multilinestring", 15}}, {EXT_TYPE_POLYGON, {"polygon", 7}}, {EXT_TYPE_MULTIPOLYGON, {"multipolygon", 12}}, {EXT_TYPE_GEOMETRYCOLLECTION, {"geometrycollection", 18}}, {0, {NULL, 0}} }; Mrdb_ExtFieldType *mariadb_extended_field_type(const MYSQL_FIELD *field) { #if MARIADB_PACKAGE_VERSION_ID > 30107 MARIADB_CONST_STRING str= {0,0}; /* Extended field type has either format name or type name */ if (mariadb_field_attr(&str, field, MARIADB_FIELD_ATTR_FORMAT_NAME)) return NULL; if (!str.length && mariadb_field_attr(&str, field, MARIADB_FIELD_ATTR_DATA_TYPE_NAME)) return NULL; if (str.length) { uint8_t i= 0; while (extended_field_types[i].ext_type) { if (extended_field_types[i].str.length == str.length && !strncmp(str.str, extended_field_types[i].str.str, str.length)) { return &extended_field_types[i]; } i++; } } #endif return NULL; } /* converts a Python date/time/datetime object to MYSQL_TIME */ static void mariadb_pydate_to_tm(enum enum_field_types type, PyObject *obj, MYSQL_TIME *tm) { memset(tm, 0, sizeof(MYSQL_TIME)); if (type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATETIME) { uint8_t is_time= PyTime_CheckExact(obj); tm->hour= is_time ? PyDateTime_TIME_GET_HOUR(obj) : PyDateTime_DATE_GET_HOUR(obj); tm->minute= is_time ? PyDateTime_TIME_GET_MINUTE(obj) : PyDateTime_DATE_GET_MINUTE(obj); tm->second= is_time ? PyDateTime_TIME_GET_SECOND(obj) : PyDateTime_DATE_GET_SECOND(obj); tm->second_part= is_time ? PyDateTime_TIME_GET_MICROSECOND(obj) : PyDateTime_DATE_GET_MICROSECOND(obj); if (type == MYSQL_TYPE_TIME) { tm->time_type= MYSQL_TIMESTAMP_TIME; return; } } if (type == MYSQL_TYPE_DATE || type == MYSQL_TYPE_DATETIME) { tm->year= PyDateTime_GET_YEAR(obj); tm->month= PyDateTime_GET_MONTH(obj); tm->day= PyDateTime_GET_DAY(obj); if (type == MYSQL_TYPE_DATE) tm->time_type= MYSQL_TIMESTAMP_DATE; else tm->time_type= MYSQL_TIMESTAMP_DATETIME; } } static void mariadb_pydelta_to_tm(PyObject *obj, MYSQL_TIME *tm) { int remain= 0, total_seconds= 0; memset(tm, 0, sizeof(MYSQL_TIME)); tm->second_part= ((PyDateTime_Delta *)obj)->microseconds; tm->neg= ((PyDateTime_Delta *)obj)->days < 0; /* todo: there must be a function obj->total_seconds() */ total_seconds= abs(((PyDateTime_Delta *)obj)->days * 3600 * 24 + ((PyDateTime_Delta *)obj)->seconds); if (tm->second_part && tm->neg) { total_seconds-= 1; tm->second_part= 1000000 - tm->second_part; } tm->hour= total_seconds / 3600; remain= total_seconds % 3600; tm->minute= remain / 60; tm->second= remain % 60; } static unsigned long long my_strtoull(const char *str, size_t len, const char **end, int *err) { unsigned long long val = 0; const char *p = str; const char *end_str = p + len; for (; p < end_str; p++) { if (*p < '0' || *p > '9') break; if (val > ULLONG_MAX /10 || val*10 > ULLONG_MAX - (*p - '0')) { *err = ERANGE; break; } val = val * 10 + *p -'0'; } if (p == str) /* Did not parse anything.*/ *err = ERANGE; *end = p; return val; } /* strtoui() version, that works for non-null terminated strings */ static unsigned int my_strtoui(const char *str, size_t len, const char **end, int *err) { unsigned long long ull = my_strtoull(str, len, end, err); if (ull > UINT_MAX) *err = ERANGE; return (unsigned int)ull; } /* Parse time, in MySQL format. the input string needs is in form "hour:minute:second[.fraction]" hour, minute and second can have leading zeroes or not, they are not necessarily 2 chars. Hour must be < 838, minute < 60, second < 60 Only 6 places of fraction are considered, the value is truncated after 6 places. */ static const unsigned int frac_mul[] = { 1000000,100000,10000,1000,100,10 }; static int parse_time(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm) { int err= 0; const char *p = str; const char *end = str + length; size_t frac_len; int ret=1; tm->hour = my_strtoui(p, end-p, &p, &err); if (err || tm->hour > 838 || p == end || *p != ':' ) goto end; p++; tm->minute = my_strtoui(p, end-p, &p, &err); if (err || tm->minute > 59 || p == end || *p != ':') goto end; p++; tm->second = my_strtoui(p, end-p, &p, &err); if (err || tm->second > 59) goto end; ret = 0; tm->second_part = 0; if (p == end) goto end; /* Check for fractional part*/ if (*p != '.') goto end; p++; frac_len = MIN(6,end-p); tm->second_part = my_strtoui(p, frac_len, &p, &err); if (err) goto end; if (frac_len < 6) tm->second_part *= frac_mul[frac_len]; ret = 0; /* Consume whole fractional part, even after 6 digits.*/ p += frac_len; while(p < *end_ptr) { if (*p < '0' || *p > '9') break; p++; } end: *end_ptr = p; return ret; } static uint8_t check_date(uint16_t year, uint8_t month, uint8_t day) { uint8_t is_leap= 0; if (year < 1 || year > 9999) return 0; if (month < 1 || month > 12) return 0; if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) is_leap= 1; if (month == 2) { if (is_leap && day > 29) return 0; if (!is_leap && day > 28) return 0; } if ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) return 0; return 1; } static uint8_t check_time(MYSQL_TIME *tm) { if (tm->hour > 838) return 0; if (tm->minute < 0 || tm->minute > 59) return 0; if (tm->second < 0 || tm->second > 59) return 0; if (tm->second_part < 0 || tm->second_part > 999999) return 0; return 1; } /* Parse date, in MySQL format. The input string needs is in form "year-month-day" year, month and day can have leading zeroes or not, they do not have fixed length. Year must be < 10000, month < 12, day < 32 Years with 2 digits, are converted to values 1970-2069 according to usual rules: 00-69 is converted to 2000-2069. 70-99 is converted to 1970-1999. */ static int parse_date(const char *str, size_t length, const char **end_ptr, MYSQL_TIME *tm) { int err = 0; const char *p = str; const char *end = str + length; int ret = 1; tm->year = my_strtoui(p, end - p, &p, &err); if (err || tm->year > 9999 || p == end || *p != '-') goto end; if (p - str == 2) // 2-digit year tm->year += (tm->year >= 70) ? 1900 : 2000; p++; tm->month = my_strtoui(p,end -p, &p, &err); if (err || tm->month > 12 || p == end || *p != '-') goto end; p++; tm->day = my_strtoui(p, end -p , &p, &err); if (err || tm->day > 31) goto end; ret = 0; end: *end_ptr = p; return ret; } int Py_str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm) { const char *p = str; const char *end = str + length; int is_time = 0; if (!p) goto error; while (p < end && isspace(*p)) p++; while (p < end && isspace(end[-1])) end--; if (end -p < 5) goto error; if (*p == '-') { tm->neg = 1; /* Only TIME can't be negative.*/ is_time = 1; p++; } else { int i; tm->neg = 0; /* Date parsing (in server) accepts leading zeroes, thus position of the delimiters is not fixed. Scan the string to find out what we need to parse. */ for (i = 1; p + i < end; i++) { if(p[i] == '-' || p [i] == ':') { is_time = p[i] == ':'; break; } } } if (is_time) { if (parse_time(p, end - p, &p, tm)) goto error; tm->year = tm->month = tm->day = 0; tm->time_type = MYSQL_TIMESTAMP_TIME; return 0; } if (parse_date(p, end - p, &p, tm)) goto error; if (p == end || p[0] != ' ') { tm->hour = tm->minute = tm->second = tm->second_part = 0; tm->time_type = MYSQL_TIMESTAMP_DATE; return 0; } /* Skip space. */ p++; if (parse_time(p, end - p, &p, tm)) goto error; /* In DATETIME, hours must be < 24.*/ if (tm->hour > 23) goto error; tm->time_type = MYSQL_TIMESTAMP_DATETIME; return 0; error: memset(tm, 0, sizeof(*tm)); tm->time_type = MYSQL_TIMESTAMP_ERROR; return 1; } static PyObject *Mrdb_GetTimeDelta(MYSQL_TIME *tm) { int days, hour, minute, second, second_part; hour= (tm->neg) ? -1 * tm->hour : tm->hour; minute= (tm->neg) ? -1 * tm->minute : tm->minute; second= (tm->neg) ? -1 * tm->second : tm->second; second_part= (tm->neg) ? -1 * tm->second_part : tm->second_part; days= hour / 24; hour= hour % 24; second= hour * 3600 + minute * 60 + second; return PyDelta_FromDSU(days, second, second_part); } static PyObject *ma_convert_value(MrdbCursor *self, enum enum_field_types type, PyObject *value) { PyObject *key= PyLong_FromLongLong(type); PyObject *func; PyObject *new_value= NULL; if (!self->connection->converter) return NULL; if ((func= PyDict_GetItem(self->connection->converter, key)) && PyCallable_Check(func)) { PyObject *arglist= PyTuple_New(1); PyTuple_SetItem(arglist, 0, value); new_value= PyObject_CallObject(func, arglist); } return new_value; } void field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column) { MYSQL_TIME tm; unsigned long *length; Mrdb_ExtFieldType *ext_field_type; uint16_t type= self->fields[column].type; ext_field_type= mariadb_extended_field_type(&self->fields[column]); if (!data) type= MYSQL_TYPE_NULL; length= mysql_fetch_lengths(self->result); switch (type) { case MYSQL_TYPE_NULL: Py_INCREF(Py_None); self->values[column]= Py_None; break; case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: { char *p= data; /* CONPY-258: remove leading zero's */ if (strlen(p) > 1) { while (*p && *p == '0') p++; } self->values[column]= PyLong_FromString(p, NULL, 0); break; } case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: { double d= atof(data); self->values[column]= PyFloat_FromDouble(d); break; } case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: memset(&tm, 0, sizeof(MYSQL_TIME)); Py_str_to_TIME(data, strlen(data), &tm); if (self->fields[column].type == MYSQL_TYPE_TIME) { if (check_time(&tm)) { self->values[column]= Mrdb_GetTimeDelta(&tm); } else { Py_INCREF(Py_None); self->values[column]= Py_None; } } else if (self->fields[column].type == MYSQL_TYPE_DATE) { if (check_date(tm.year, tm.month, tm.day)) { self->values[column]= PyDate_FromDate(tm.year, tm.month, tm.day); } else { Py_INCREF(Py_None); self->values[column]= Py_None; } } else { if (check_date(tm.year, tm.month, tm.day) && check_time(&tm)) { self->values[column]= PyDateTime_FromDateAndTime(tm.year, tm.month, tm.day, tm.hour, tm.minute, tm.second, tm.second_part); } else { Py_INCREF(Py_None); self->values[column]= Py_None; } } break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_BIT: if (length[column] > self->fields[column].max_length) { self->fields[column].max_length= length[column]; } if (self->fields[column].charsetnr== CHARSET_BINARY) { self->values[column]= PyBytes_FromStringAndSize((const char *)data, (Py_ssize_t)length[column]); } else { self->values[column]= PyUnicode_FromStringAndSize((const char *)data, (Py_ssize_t)length[column]); } break; case MYSQL_TYPE_NEWDECIMAL: { PyObject *decimal; decimal= PyObject_CallFunction(decimal_type, "s", (const char *)data); self->values[column]= decimal; break; } case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_JSON: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_SET: case MYSQL_TYPE_ENUM: { unsigned long len; if ( self->fields[column].charsetnr == CHARSET_BINARY) { self->values[column]= PyBytes_FromStringAndSize((const char *)data, (Py_ssize_t)length[column]); len= (unsigned long)length[column]; } else { self->values[column]= PyUnicode_FromStringAndSize((const char *)data, (Py_ssize_t)length[column]); len= (unsigned long)PyUnicode_GET_LENGTH(self->values[column]); } if (len > self->fields[column].max_length) { self->fields[column].max_length= len; } break; } default: break; } /* check if values need to be converted */ if (self->connection->converter) { PyObject *val; enum enum_field_types type; if (ext_field_type && ext_field_type->ext_type == EXT_TYPE_JSON) type= MYSQL_TYPE_JSON; else type= self->fields[column].type; if ((val= ma_convert_value(self, type, self->values[column]))) self->values[column]= val; } } /* field_fetch_callback This function was previously registered with mysql_stmt_attr_set and STMT_ATTR_FIELD_FETCH_CALLBACK parameter. Instead of filling a bind buffer MariaDB Connector/C sends raw data in row for the specified column. In case of a NULL value row ptr will be NULL. The cursor handle was also previously registered with mysql_stmt_attr_set and STMT_ATTR_USER_DATA parameter and will be passed in data variable. */ void field_fetch_callback(void *data, unsigned int column, unsigned char **row) { MrdbCursor *self= (MrdbCursor *)data; Mrdb_ExtFieldType *ext_field_type; ext_field_type= mariadb_extended_field_type(&self->fields[column]); if (!row) { Py_INCREF(Py_None); self->values[column]= Py_None; return; } switch(self->fields[column].type) { case MYSQL_TYPE_NULL: Py_INCREF(Py_None); self->values[column]= Py_None; break; case MYSQL_TYPE_TINY: self->values[column]= (self->fields[column].flags & UNSIGNED_FLAG) ? PyLong_FromUnsignedLong((unsigned long)*row[0]) : PyLong_FromLong((long)*row[0]); *row+= 1; break; case MYSQL_TYPE_SHORT: case MYSQL_TYPE_YEAR: self->values[column]= (self->fields[column].flags & UNSIGNED_FLAG) ? PyLong_FromUnsignedLong((unsigned long)uint2korr(*row)) : PyLong_FromLong((long)sint2korr(*row)); *row+= 2; break; case MYSQL_TYPE_INT24: self->values[column]= (self->fields[column].flags & UNSIGNED_FLAG) ? PyLong_FromUnsignedLong((unsigned long)uint3korr(*row)) : PyLong_FromLong((long)sint3korr(*row)); *row+= 4; break; case MYSQL_TYPE_LONG: self->values[column]= (self->fields[column].flags & UNSIGNED_FLAG) ? PyLong_FromUnsignedLong((unsigned long)uint4korr(*row)) : PyLong_FromLong((long)sint4korr(*row)); *row+= 4; break; case MYSQL_TYPE_LONGLONG: { long long l= sint8korr(*row); self->values[column]= (self->fields[column].flags & UNSIGNED_FLAG) ? PyLong_FromUnsignedLongLong((unsigned long long)l) : PyLong_FromLongLong(l); *row+= 8; break; } case MYSQL_TYPE_FLOAT: { float f; float4get(f, *row); self->values[column]= PyFloat_FromDouble((double)f); *row+= 4; break; } case MYSQL_TYPE_DOUBLE: { double d; float8get(d, *row); self->values[column]= PyFloat_FromDouble(d); *row+= 8; break; } case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: { uint8_t len= 0; int year= 0, month= 0, day= 0, hour= 0, minute= 0, second= 0, second_part= 0; len= (uint8_t)mysql_net_field_length(row); if (!len) { self->values[column]= PyDateTime_FromDateAndTime(0,0,0,0,0,0,0); break; } year= uint2korr(*row); month= uint1korr(*row + 2); day= uint1korr(*row + 3); if (len > 4) { hour= uint1korr(*row + 4); minute= uint1korr(*row + 5); second= uint1korr(*row + 6); } if (len == 11) second_part= uint4korr(*row + 7); self->values[column]= PyDateTime_FromDateAndTime(year, month, day, hour, minute, second, second_part); *row+= len; break; } case MYSQL_TYPE_DATE: { uint8_t len= 0; int year, month, day; len= (uint8_t)mysql_net_field_length(row); if (!len) { self->values[column]= PyDate_FromDate(0,0,0); break; } year= uint2korr(*row); month= uint1korr(*row + 2); day= uint1korr(*row + 3); self->values[column]= PyDate_FromDate(year, month, day); *row+= len; break; } case MYSQL_TYPE_TIME: { uint8_t len= 0; MYSQL_TIME tm; memset(&tm, 0, sizeof(MYSQL_TIME)); len= (uint8_t)mysql_net_field_length(row); if (!len) { self->values[column]= Mrdb_GetTimeDelta(&tm); break; } tm.neg= uint1korr(*row); tm.day= uint4korr(*row + 1); tm.hour= uint1korr(*row + 5); tm.minute= uint1korr(*row + 6); tm.second= uint1korr(*row + 7); if (len > 8) tm.second_part= uint4korr(*row + 8); if (tm.day) tm.hour+= (tm.day * 24); self->values[column]= Mrdb_GetTimeDelta(&tm); *row+= len; break; } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BIT: { unsigned long length= mysql_net_field_length(row); if (length > self->fields[column].max_length) self->fields[column].max_length= length; if (self->fields[column].charsetnr== CHARSET_BINARY) { self->values[column]= PyBytes_FromStringAndSize((const char *)*row, (Py_ssize_t)length); } else { self->values[column]= PyUnicode_FromStringAndSize((const char *)*row, (Py_ssize_t)length); } *row+= length; break; } case MYSQL_TYPE_NEWDECIMAL: { unsigned long length= mysql_net_field_length(row); if (length > 0) { char *tmp= alloca(length + 1); memcpy(tmp, (const char *)*row, length); tmp[length]= 0; self->values[column]= PyObject_CallFunction(decimal_type, "s", tmp); } else { self->values[column]= PyObject_CallFunction(decimal_type, "s", "0"); } *row+= length; break; } case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_JSON: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_SET: case MYSQL_TYPE_ENUM: { unsigned long length; unsigned long utf8len; length= mysql_net_field_length(row); if (self->fields[column].charsetnr== CHARSET_BINARY) { self->values[column]= PyBytes_FromStringAndSize((const char *)*row, (Py_ssize_t)length); if (length > self->fields[column].max_length) self->fields[column].max_length= length; } else { self->values[column]= PyUnicode_FromStringAndSize((const char *)*row, (Py_ssize_t)length); utf8len= (unsigned long)PyUnicode_GET_LENGTH(self->values[column]); if (utf8len > self->fields[column].max_length) self->fields[column].max_length= utf8len; } *row+= length; } default: break; } /* check if values need to be converted */ if (self->connection->converter) { PyObject *val; enum enum_field_types type; if (ext_field_type && ext_field_type->ext_type == EXT_TYPE_JSON) type= MYSQL_TYPE_JSON; else type= self->fields[column].type; if ((val= ma_convert_value(self, type, self->values[column]))) self->values[column]= val; } } /* mariadb_get_column_info This function analyzes the Python object and calculates the corresponding MYSQL_TYPE, unsigned flag or NULL values and stores the information in MrdbParamInfo pointer. */ static uint8_t mariadb_get_column_info(PyObject *obj, MrdbParamInfo *paraminfo) { if (obj == NULL) { paraminfo->type= MYSQL_TYPE_NULL; return 0; } if (CHECK_TYPE(obj, &PyLong_Type)) { size_t b= _PyLong_NumBits(obj); if (b > paraminfo->bits) paraminfo->bits= b; paraminfo->type= MYSQL_TYPE_LONGLONG; return 0; } else if (CHECK_TYPE(obj, &PyBool_Type)) { paraminfo->type= MYSQL_TYPE_TINY; return 0; } else if (CHECK_TYPE(obj, &PyFloat_Type)) { paraminfo->type= MYSQL_TYPE_DOUBLE; return 0; } else if (CHECK_TYPE(obj, &PyBytes_Type)) { paraminfo->type= MYSQL_TYPE_LONG_BLOB; return 0; } else if (PyDate_CheckExact(obj)) { paraminfo->type= MYSQL_TYPE_DATE; return 0; } else if (PyTime_CheckExact(obj) || PyDelta_CheckExact(obj)) { paraminfo->type= MYSQL_TYPE_TIME; return 0; } else if (PyDateTime_CheckExact(obj)) { paraminfo->type= MYSQL_TYPE_DATETIME; return 0; } else if (CHECK_TYPE(obj, &PyUnicode_Type)) { paraminfo->type= MYSQL_TYPE_VAR_STRING; return 0; } else if (obj == Py_None) { paraminfo->type= MYSQL_TYPE_NULL; return 0; } else if (!strcmp(Py_TYPE(obj)->tp_name, "decimal.Decimal") || !strcmp(Py_TYPE(obj)->tp_name, "Decimal")) { /* CONPY-49: C-API has no correspondent data type for DECIMAL column type, so we need to convert decimal.Decimal Object to string during callback */ paraminfo->type= MYSQL_TYPE_NEWDECIMAL; return 0; } else { /* If Object has string representation, we will use string representation */ /* no corresponding object, return error */ return 2; } return 1; } PyObject *ListOrTuple_GetItem(PyObject *obj, Py_ssize_t index) { if (CHECK_TYPE(obj, &PyList_Type)) { return PyList_GetItem(obj, index); } else if (CHECK_TYPE(obj, &PyTuple_Type)) { return PyTuple_GetItem(obj, index); } /* this should never happen, since the type was checked before */ return NULL; } /* mariadb_get_parameter() @brief Returns a bulk parameter which was passed to cursor.executemany() or a parameter which was passed to cursor.execute() @param self[in] Cursor @param row_nr[in] row number @paran column_nr[in] column number @param paran[in][out] bulk parameter pointer @return 0 on success, 1 on error */ static uint8_t mariadb_get_parameter(MrdbCursor *self, uint8_t is_bulk, uint32_t row_nr, uint32_t column_nr, MrdbParamValue *param) { PyObject *row= NULL, *column= NULL; uint8_t rc= 1; long caps; mariadb_get_infov(self->connection->mysql, MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES, &caps); if (is_bulk) { /* check if row_nr and column_nr are in the range from 0 to (value - 1) */ if (row_nr > (self->array_size - 1) || column_nr > (self->parseinfo.paramcount - 1)) { mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 0, "Can't access data at row %d, column %d", row_nr + 1, column_nr + 1); goto end; } if (!(row= ListOrTuple_GetItem(self->data, row_nr))) { mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 0, "Can't access row number %d", row_nr + 1); goto end; } } else row= self->data; if (self->parseinfo.paramstyle != PYFORMAT) { if (!(column= ListOrTuple_GetItem(row, column_nr))) { mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 0, "Can't access column number %d at row %d", column_nr + 1, row_nr + 1); goto end; } } else { PyObject *key; key= PyTuple_GetItem(self->parseinfo.keys, column_nr); if (!PyDict_Contains(row, key)) { mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 0, "Can't find key in parameter data"); goto end; } column= PyDict_GetItem(row, key); } /* check if an indicator was passed */ if (MrdbIndicator_Check(column)) { if (!(caps & (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))) { mariadb_throw_exception(NULL, Mariadb_NotSupportedError, 0, "MariaDB %s doesn't support indicator variables. "\ "Required version is 10.2.6 or newer", mysql_get_server_info(self->stmt->mysql)); goto end; } param->indicator= (uint8_t)MrdbIndicator_AsLong(column); param->value= NULL; /* you can't have both indicator and value */ } else if (column == Py_None) { param->value= NULL; if (caps & (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32)) { param->indicator= STMT_INDICATOR_NULL; } } else { param->value= column; param->indicator= STMT_INDICATOR_NONE; } rc= 0; end: return rc; } /* mariadb_get_parameter_info mariadb_get_parameter_info fills the MYSQL_BIND structure with correct field_types for the Python objects. In case of a bulk operation (executemany()) we will also optimize the field type (e.g. by checking maxbit size for a PyLong). If the types in this column differ we will return an error. */ static uint8_t mariadb_get_parameter_info(MrdbCursor *self, MYSQL_BIND *param, uint32_t column_nr) { uint32_t i, bits= 0; MrdbParamValue paramvalue; MrdbParamInfo pinfo; param->is_unsigned= 0; paramvalue.indicator= 0; if (!self->array_size) { uint8_t rc; memset(&pinfo, 0, sizeof(MrdbParamInfo)); if (mariadb_get_parameter(self, 0, 0, column_nr, ¶mvalue)) return 1; if ((rc= mariadb_get_column_info(paramvalue.value, &pinfo))) { if (rc == 1) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Can't retrieve column information for parameter %d", column_nr); } if (rc == 2) { mariadb_throw_exception(NULL, Mariadb_NotSupportedError, 0, "Data type '%s' in column %d not supported in MariaDB Connector/Python", Py_TYPE(paramvalue.value)->tp_name, column_nr); } return 1; } param->buffer_type= pinfo.type; bits= (uint32_t)pinfo.bits; } else for (i=0; i < self->array_size; i++) { if (mariadb_get_parameter(self, 1, i, column_nr, ¶mvalue)) return 1; memset(&pinfo, 0, sizeof(MrdbParamInfo)); if (mariadb_get_column_info(paramvalue.value, &pinfo) && !paramvalue.indicator) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 1, "Invalid parameter type at row %d, column %d", i+1, column_nr + 1); return 1; } if (pinfo.type == MYSQL_TYPE_LONGLONG) { if (pinfo.bits > bits) { bits= (uint32_t)pinfo.bits; } } if (!param->buffer_type || param->buffer_type == MYSQL_TYPE_NULL) { param->buffer_type= pinfo.type; } else { /* except for NULL the parameter types must match */ if (param->buffer_type != pinfo.type && pinfo.type != MYSQL_TYPE_NULL && !paramvalue.indicator) { if ((param->buffer_type == MYSQL_TYPE_TINY || param->buffer_type == MYSQL_TYPE_SHORT || param->buffer_type == MYSQL_TYPE_LONG) && pinfo.type == MYSQL_TYPE_LONGLONG) break; if (IS_DECIMAL_TYPE(pinfo.type) && IS_DECIMAL_TYPE(param->buffer_type)) { param->buffer_type= MYSQL_TYPE_NEWDECIMAL; break; } mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 1, "Invalid parameter type at row %d, column %d", i+1, column_nr + 1); return 1; } } } /* check the bit size for long types and set the appropriate field type */ if (param->buffer_type == MYSQL_TYPE_LONGLONG) { if ((bits <= 8 && param->is_unsigned) || bits < 8) { param->buffer_type= MYSQL_TYPE_TINY; } else if ((bits <= 16 && param->is_unsigned) || bits < 16) { param->buffer_type= MYSQL_TYPE_SHORT; } else if ((bits <= 32 && param->is_unsigned) || bits < 32) { param->buffer_type= MYSQL_TYPE_LONG; } else { param->buffer_type= MYSQL_TYPE_LONGLONG; } } return 0; } static Py_ssize_t ListOrTuple_Size(PyObject *obj) { if (CHECK_TYPE(obj, &PyList_Type)) { return PyList_Size(obj); } else if (CHECK_TYPE(obj, &PyTuple_Type)) { return PyTuple_Size(obj); } /* this should never happen, since the type was checked before */ return 0; } /* mariadb_check_bulk_parameters This function validates the specified bulk parameters and translates the field types to MYSQL_TYPE_*. */ uint8_t mariadb_check_bulk_parameters(MrdbCursor *self, PyObject *data) { uint32_t i; if (!CHECK_TYPE((data), &PyList_Type) && !CHECK_TYPE(data, &PyTuple_Type)) { mariadb_throw_exception(self->stmt, Mariadb_InterfaceError, 1, "Data must be passed as sequence (Tuple or List)"); return 1; } if (!(self->array_size= (uint32_t)ListOrTuple_Size(data))) { mariadb_throw_exception(self->stmt, Mariadb_InterfaceError, 1, "Empty parameter list. At least one row must be specified"); return 1; } for (i=0; i < self->array_size; i++) { PyObject *obj= ListOrTuple_GetItem(data, i); if (self->parseinfo.paramstyle != PYFORMAT && (!CHECK_TYPE(obj, &PyTuple_Type) && !CHECK_TYPE(obj, &PyList_Type))) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Invalid parameter type in row %d. "\ " (Row data must be provided as tuple(s))", i+1); return 1; } if (self->parseinfo.paramstyle == PYFORMAT && !CHECK_TYPE(obj, &PyDict_Type)) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Invalid parameter type in row %d. "\ " (Row data must be provided as dict)", i+1); return 1; } if (!self->parseinfo.paramcount || (self->parseinfo.paramstyle != PYFORMAT && self->parseinfo.paramcount != ListOrTuple_Size(obj))) { mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 1, "Invalid number of parameters in row %d", i+1); return 1; } } if (!self->is_prepared && !(self->params= PyMem_RawCalloc(self->parseinfo.paramcount, sizeof(MYSQL_BIND)))) { mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0, "Not enough memory (tried to allocated %lld bytes)", self->parseinfo.paramcount * sizeof(MYSQL_BIND)); goto error; } if (!(self->value= PyMem_RawCalloc(self->parseinfo.paramcount, sizeof(MrdbParamValue)))) { mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0, "Not enough memory (tried to allocated %lld bytes)", self->parseinfo.paramcount * sizeof(MrdbParamValue)); goto error; } for (i=0; i < self->parseinfo.paramcount; i++) { if (mariadb_get_parameter_info(self, &self->params[i], i)) goto error; } return 0; error: MARIADB_FREE_MEM(self->paraminfo); MARIADB_FREE_MEM(self->value); return 1; } uint8_t mariadb_check_execute_parameters(MrdbCursor *self, PyObject *data) { uint32_t i; if (!self->parseinfo.paramcount) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Invalid number of parameters"); goto error; } if (!self->params && !(self->params= PyMem_RawCalloc(self->parseinfo.paramcount, sizeof(MYSQL_BIND)))) { mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0, "Not enough memory (tried to allocated %lld bytes)", self->parseinfo.paramcount * sizeof(MYSQL_BIND)); goto error; } if (!self->value && !(self->value= PyMem_RawCalloc(self->parseinfo.paramcount, sizeof(MrdbParamValue)))) { mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0, "Not enough memory (tried to allocated %lld bytes)", self->parseinfo.paramcount * sizeof(MrdbParamValue)); goto error; } for (i=0; i < self->parseinfo.paramcount; i++) { if (mariadb_get_parameter_info(self, &self->params[i], i)) { goto error; } } return 0; error: MARIADB_FREE_MEM(self->paraminfo); MARIADB_FREE_MEM(self->value); return 1; } /* mariadb_param_to_bind() @brief Set the current value for the specified bind buffer @param self cursor @param bind[in] bind structure @param value[in] current column value @return 0 on success, otherwise error */ static uint8_t mariadb_param_to_bind(MrdbCursor *self, MYSQL_BIND *bind, MrdbParamValue *value) { uint8_t rc= 0; uint8_t is_negative= 0; if (value->indicator > 0) { bind->u.indicator[0]= value->indicator; goto end; } if (!value->value) { bind->buffer_type= MYSQL_TYPE_NULL; } else { if (IS_NUM(bind->buffer_type)) { bind->buffer= value->num; } if (CHECK_TYPE(value->value, &PyLong_Type)) { if (_PyLong_Sign(value->value) < 0) is_negative= 1; } } switch(bind->buffer_type) { case MYSQL_TYPE_TINY: if (!is_negative) { if ((value->num[0]= (uint8_t)PyLong_AsUnsignedLong(value->value)) > 0x7F) bind->is_unsigned= 1; } else { value->num[0]= (int8_t)PyLong_AsLong(value->value); } break; case MYSQL_TYPE_SHORT: if (!is_negative) { if ((*(uint16_t *)&value->num= (uint16_t)PyLong_AsUnsignedLong(value->value)) > 0x7FFF) bind->is_unsigned= 1; } else { *(int16_t *)&value->num= (int16_t)PyLong_AsLong(value->value); } break; case MYSQL_TYPE_LONG: if (!is_negative) { if ((*(uint32_t *)&value->num= (uint32_t)PyLong_AsUnsignedLong(value->value)) > 0x7FFFFFFF) bind->is_unsigned= 1; } else { *(int32_t *)&value->num= (int32_t)PyLong_AsLong(value->value); } break; case MYSQL_TYPE_LONGLONG: if (!is_negative) { if ((*(uint64_t *)value->num= (uint64_t)PyLong_AsUnsignedLongLong(value->value)) > 0x7FFFFFFFFFFFFFFF) bind->is_unsigned= 1; } else { *(int64_t *)value->num= (int64_t)PyLong_AsLongLong(value->value); } break; case MYSQL_TYPE_DOUBLE: *(double *)value->num= (double)PyFloat_AsDouble(value->value); break; case MYSQL_TYPE_LONG_BLOB: bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(value->value); bind->buffer= (void *) PyBytes_AS_STRING(value->value); break; case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: bind->buffer= &value->tm; if (PyDelta_CheckExact(value->value)) mariadb_pydelta_to_tm(value->value, &value->tm); else mariadb_pydate_to_tm(bind->buffer_type, value->value, &value->tm); break; case MYSQL_TYPE_NEWDECIMAL: { Py_ssize_t len; PyObject *obj= NULL; char *p; if (value->free_me) MARIADB_FREE_MEM(value->buffer); if (!strcmp(Py_TYPE(value->value)->tp_name, "decimal.Decimal") || !strcmp(Py_TYPE(value->value)->tp_name, "Decimal")) { obj= PyObject_Str(value->value); p= (void *)PyUnicode_AsUTF8AndSize(obj, &len); } else { obj= PyObject_Str(value->value); p= (void *)PyUnicode_AsUTF8AndSize(obj, &len); } bind->buffer= value->buffer= PyMem_RawCalloc(1, len); memcpy(value->buffer, p, len); value->free_me= 1; bind->buffer_length= (unsigned long)len; Py_DECREF(obj); } break; case MYSQL_TYPE_VAR_STRING: { Py_ssize_t len; bind->buffer= (void *)PyUnicode_AsUTF8AndSize(value->value, &len); bind->buffer_length= (unsigned long)len; break; } case MYSQL_TYPE_NULL: break; default: rc= 1; } end: return rc; } /* mariadb_param_update() @brief Callback function which updates the bind structure's buffer and length with data from the specified row number. This callback function must be registered via api function mysql_stmt_attr_set with STMT_ATTR_PARAM_CALLBACK option @param data[in] A pointer to a MrdbCursor object which was passed via mysql_stmt_attr_set before data[in][out] An array of bind structures data[in] row number @return 0 on success, otherwise error (=1) */ uint8_t mariadb_param_update(void *data, MYSQL_BIND *bind, uint32_t row_nr) { MrdbCursor *self= (MrdbCursor *)data; uint32_t i; uint8_t rc= 1; for (i=0; i < self->parseinfo.paramcount; i++) { if (mariadb_get_parameter(self, (self->array_size > 0), row_nr, i, &self->value[i])) { goto end; } if (self->value[i].indicator) { bind[i].u.indicator= &self->value[i].indicator; } if (self->value[i].indicator < 1) { if (mariadb_param_to_bind(self, &bind[i], &self->value[i])) { goto end; } } } rc= 0; end: return rc; } #ifdef _WIN32 /* windows equivalent for clock_gettime. Code based on https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows */ static uint8_t g_first_time = 1; static LARGE_INTEGER g_counts_per_sec; int clock_gettime(int dummy, struct timespec *ct) { LARGE_INTEGER count; if (g_first_time) { g_first_time = 0; if (0 == QueryPerformanceFrequency(&g_counts_per_sec)) { g_counts_per_sec.QuadPart = 0; } } if ((NULL == ct) || (g_counts_per_sec.QuadPart <= 0) || (0 == QueryPerformanceCounter(&count))) { return -1; } ct->tv_sec = count.QuadPart / g_counts_per_sec.QuadPart; ct->tv_nsec = (long)(((count.QuadPart % g_counts_per_sec.QuadPart) * 1E09) / g_counts_per_sec.QuadPart); return 0; } #endif mariadb-connector-python-1.1.10/mariadb/mariadb_connection.c000066400000000000000000000626011456007477700241070ustar00rootroot00000000000000/***************************************************************************** Copyright (C) 2018-2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *****************************************************************************/ #include "mariadb_python.h" #include "docs/connection.h" #include "docs/exception.h" #define MADB_SET_OPTION(m,o,v)\ if (mysql_optionsv((m), (o), (v)))\ {\ mariadb_throw_exception(self->mysql, NULL, 0, NULL);\ return -1;\ } char *dsn_keys[]= { "dsn", "host", "user", "password", "database", "port", "unix_socket", "connect_timeout", "read_timeout", "write_timeout", "local_infile", "compress", "init_command", "default_file", "default_group", "ssl_key", "ssl_ca", "ssl_cert", "ssl_crl", "ssl_cipher", "ssl_capath", "ssl_crlpath", "ssl_verify_cert", "ssl", "client_flag", "pool_name", "pool_size", "pool_reset_connection", "plugin_dir", "username", "db", "passwd", "status_callback", "tls_version", NULL }; const char *mariadb_default_charset= "utf8mb4"; const char *mariadb_default_collation= "utf8mb4_general_ci"; static void MrdbConnection_finalize(MrdbConnection *self); static PyObject * MrdbConnection_exception(PyObject *self, void *closure); #define GETTER_EXCEPTION(name, exception, doc)\ { name,MrdbConnection_exception, NULL, doc, &exception } static PyObject * MrdbConnection_getreconnect(MrdbConnection *self, void *closure); static PyObject * MrdbConnection_connection_id(MrdbConnection *self); static int MrdbConnection_setreconnect(MrdbConnection *self, PyObject *args, void *closure); static PyObject * MrdbConnection_escape_string(MrdbConnection *self, PyObject *str); static PyObject * MrdbConnection_getinfo(MrdbConnection *self, PyObject *optionval); static PyObject * MrdbConnection_dump_debug_info(MrdbConnection *self); static PyObject * MrdbConnection_warnings(MrdbConnection *self); static PyObject * MrdbConnection_executecommand(MrdbConnection *self, PyObject *command); static PyObject * MrdbConnection_readresponse(MrdbConnection *self); static PyObject *MrdbConnection_socket(MrdbConnection *self); static PyGetSetDef MrdbConnection_sets[]= { {"auto_reconnect", (getter)MrdbConnection_getreconnect, (setter)MrdbConnection_setreconnect, connection_auto_reconnect__doc__, NULL}, {"connection_id", (getter)MrdbConnection_connection_id, NULL, "Id of current connection", NULL}, {"warnings", (getter)MrdbConnection_warnings, NULL, connection_warnings__doc__, NULL}, GETTER_EXCEPTION("Error", Mariadb_Error, ""), GETTER_EXCEPTION("Warning", Mariadb_Warning, exception_warning__doc__), GETTER_EXCEPTION("InterfaceError", Mariadb_InterfaceError, exception_interface__doc__), GETTER_EXCEPTION("ProgrammingError", Mariadb_ProgrammingError, exception_programming__doc__), GETTER_EXCEPTION("IntegrityError", Mariadb_IntegrityError, exception_integrity__doc__), GETTER_EXCEPTION("DatabaseError", Mariadb_DatabaseError, exception_database__doc__), GETTER_EXCEPTION("NotSupportedError", Mariadb_NotSupportedError, exception_notsupported__doc__), GETTER_EXCEPTION("InternalError", Mariadb_InternalError, exception_internal__doc__), GETTER_EXCEPTION("OperationalError", Mariadb_OperationalError, exception_operational__doc__), GETTER_EXCEPTION("PoolError", Mariadb_PoolError, exception_pool__doc__), GETTER_EXCEPTION("DataError", Mariadb_DataError, exception_data__doc__), {NULL} }; static PyMethodDef MrdbConnection_Methods[] = { /* PEP-249 methods */ {"close", (PyCFunction)MrdbConnection_close, METH_NOARGS, connection_close__doc__}, {"connect", (PyCFunction)MrdbConnection_connect, METH_VARARGS | METH_KEYWORDS, connection_connect__doc__}, /* additional methods */ { "ping", (PyCFunction)MrdbConnection_ping, METH_NOARGS, connection_ping__doc__ }, { "change_user", (PyCFunction)MrdbConnection_change_user, METH_VARARGS, connection_change_user__doc__ }, { "reconnect", (PyCFunction)MrdbConnection_reconnect, METH_NOARGS, connection_reconnect__doc__ }, { "reset", (PyCFunction)MrdbConnection_reset, METH_NOARGS, connection_reset__doc__, }, { "escape_string", (PyCFunction)MrdbConnection_escape_string, METH_O, connection_escape_string__doc__ }, { "dump_debug_info", (PyCFunction)MrdbConnection_dump_debug_info, METH_NOARGS, connection_dump_debug_info__doc__ }, /* Internal methods */ { "_execute_command", (PyCFunction)MrdbConnection_executecommand, METH_O, "For internal use only"}, {"_read_response", (PyCFunction)MrdbConnection_readresponse, METH_NOARGS, "For internal use only"}, {"_mariadb_get_info", (PyCFunction)MrdbConnection_getinfo, METH_O, "For internal use only"}, {"_get_socket", (PyCFunction)MrdbConnection_socket, METH_NOARGS, "For internal use only"}, {NULL} /* always last */ }; static struct PyMemberDef MrdbConnection_Members[] = { {"dsn", T_OBJECT, offsetof(MrdbConnection, dsn), READONLY, "Data source name (dsn)"}, {"_closed", T_BOOL, offsetof(MrdbConnection, closed), READONLY, "Indicates if connection was closed"}, {"_converter", T_OBJECT, offsetof(MrdbConnection, converter), 0, "Conversion dictionary"}, {NULL} /* always last */ }; #if MARIADB_PACKAGE_VERSION_ID > 30301 void MrdbConnection_process_status_info(void *data, enum enum_mariadb_status_info type, ...) { va_list ap; PyThreadState *_save= NULL; MrdbConnection *self= (MrdbConnection *)data; PyObject *dict= NULL; PyObject *dict_key= NULL, *dict_val= NULL; va_start(ap, type); if (self->status_callback) { if (type == STATUS_TYPE) { unsigned int server_status= va_arg(ap, int); MARIADB_UNBLOCK_THREADS(self); dict_key= PyUnicode_FromString("server_status"); dict_val= PyLong_FromLong(server_status); dict= PyDict_New(); PyDict_SetItem(dict, dict_key, dict_val); Py_DECREF(dict_key); Py_DECREF(dict_val); PyObject_CallFunction(self->status_callback, "OO", (PyObject *)data, dict); MARIADB_BLOCK_THREADS(self); } } if (type == SESSION_TRACK_TYPE) { enum enum_session_state_type track_type= va_arg(ap, enum enum_session_state_type); MARIADB_UNBLOCK_THREADS(self); if (self->status_callback) { switch (track_type) { case SESSION_TRACK_SCHEMA: dict_key= PyUnicode_FromString("schema"); break; case SESSION_TRACK_STATE_CHANGE: dict_key= PyUnicode_FromString("state_change"); break; default: break; } } if (dict_key) { MARIADB_CONST_STRING *val= va_arg(ap, MARIADB_CONST_STRING *); dict_val= PyUnicode_FromStringAndSize(val->str, val->length); dict= PyDict_New(); PyDict_SetItem(dict, dict_key, dict_val); Py_DECREF(dict_key); Py_DECREF(dict_val); PyObject_CallFunction(self->status_callback, "OO", (PyObject *)data, dict); } if (track_type == SESSION_TRACK_SYSTEM_VARIABLES) { MARIADB_CONST_STRING *key= va_arg(ap, MARIADB_CONST_STRING *); MARIADB_CONST_STRING *val= va_arg(ap, MARIADB_CONST_STRING *); if (!strncmp(key->str, "character_set_client", key->length) && strncmp(val->str, "utf8mb4", val->length)) { /* mariadb_throw_exception (PyUnicode_FormatV) doesn't support string with length, so we need a temporary variable */ char charset[128]; memcpy(charset, val->str, val->length); charset[val->length]= 0; va_end(ap); mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 1, "Character set '%s' is not supported", charset); } if (self->status_callback) { dict_key= PyUnicode_FromStringAndSize(key->str, key->length); dict_val= PyUnicode_FromStringAndSize(val->str, val->length); dict= PyDict_New(); PyDict_SetItem(dict, dict_key, dict_val); Py_DECREF(dict_key); Py_DECREF(dict_val); PyObject_CallFunction(self->status_callback, "OO", (PyObject *)data, dict); } } MARIADB_BLOCK_THREADS(self); } va_end(ap); } #endif static int MrdbConnection_Initialize(MrdbConnection *self, PyObject *args, PyObject *dsnargs) { uint8_t has_error= 1; char *dsn= NULL, *host=NULL, *user= NULL, *password= NULL, *schema= NULL, *socket= NULL, *init_command= NULL, *default_file= NULL, *default_group= NULL, *ssl_key= NULL, *ssl_cert= NULL, *ssl_ca= NULL, *ssl_capath= NULL, *ssl_crl= NULL, *ssl_crlpath= NULL, *ssl_cipher= NULL, *plugin_dir= NULL, *tls_version= NULL; char *pool_name= 0; uint32_t pool_size= 0; uint8_t ssl_enforce= 0; uint8_t reset_session= 1; unsigned int client_flags= 0, port= 0; unsigned int local_infile= 0xFF; unsigned int connect_timeout=0, read_timeout=0, write_timeout=0, compress= 0, ssl_verify_cert= 0; PyObject *status_callback= NULL; if (!PyArg_ParseTupleAndKeywords(args, dsnargs, "|zzzzziziiibbzzzzzzzzzzibizibzzzzOz:connect", dsn_keys, &dsn, &host, &user, &password, &schema, &port, &socket, &connect_timeout, &read_timeout, &write_timeout, &local_infile, &compress, &init_command, &default_file, &default_group, &ssl_key, &ssl_ca, &ssl_cert, &ssl_crl, &ssl_cipher, &ssl_capath, &ssl_crlpath, &ssl_verify_cert, &ssl_enforce, &client_flags, &pool_name, &pool_size, &reset_session, &plugin_dir, &user, &schema, &password, &status_callback, &tls_version)) { return -1; } if (dsn) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 1, "dsn keyword is not supported"); return -1; } #if MARIADB_PACKAGE_VERSION_ID < 30302 if (status_callback) { /* status callback requires C/C 3.3.2 */ PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "status_callback support requires MariaDB Connector/C >= 3.3.2 "\ "(found version %s)", mysql_get_client_info()); } #else self->status_callback= status_callback; #endif if (!(self->mysql= mysql_init(NULL))) { mariadb_throw_exception(self->mysql, Mariadb_OperationalError, 1, "Can't allocate memory for connection"); return -1; } #if MARIADB_PACKAGE_VERSION_ID > 30301 if (mysql_optionsv(self->mysql, MARIADB_OPT_STATUS_CALLBACK, MrdbConnection_process_status_info, self)) { /* Generate a warning, not an error - this will allow to run the module if Connector/C installation was overwritten */ PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "MariaDB Connector/Python was build with MariaDB Connector/C version %s "\ "but loaded Connector/C library has version %s", MARIADB_PACKAGE_VERSION, mysql_get_client_info()); } #endif MARIADB_BEGIN_ALLOW_THREADS(self); if (mysql_options(self->mysql, MYSQL_SET_CHARSET_NAME, mariadb_default_charset)) goto end; if (local_infile != 0xFF) { if (mysql_options(self->mysql, MYSQL_OPT_LOCAL_INFILE, &local_infile)) goto end; } if (compress) { if (mysql_options(self->mysql, MYSQL_OPT_COMPRESS, "1")) goto end; } if (init_command) { if (mysql_options(self->mysql, MYSQL_INIT_COMMAND, init_command)) goto end; } if (plugin_dir) { if (mysql_options(self->mysql, MYSQL_PLUGIN_DIR, plugin_dir)) goto end; } else { #if defined(DEFAULT_PLUGINS_SUBDIR) if (mysql_options(self->mysql, MYSQL_PLUGIN_DIR, DEFAULT_PLUGINS_SUBDIR)) goto end; #endif } /* read defaults from configuration file(s) */ if (default_file) { if (mysql_options(self->mysql, MYSQL_READ_DEFAULT_FILE, default_file)) goto end; } if (default_group) { if (mysql_options(self->mysql, MYSQL_READ_DEFAULT_GROUP, default_group)) goto end; } /* set timeouts */ if (connect_timeout) { if (mysql_options(self->mysql, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout)) goto end; } if (read_timeout) { if (mysql_options(self->mysql, MYSQL_OPT_READ_TIMEOUT, &read_timeout)) goto end; } if (write_timeout) { if (mysql_options(self->mysql, MYSQL_OPT_WRITE_TIMEOUT, &write_timeout)) goto end; } /* set TLS/SSL options */ if (ssl_enforce || ssl_key || ssl_ca || ssl_cert || ssl_capath || ssl_cipher || tls_version) mysql_ssl_set(self->mysql, (const char *)ssl_key, (const char *)ssl_cert, (const char *)ssl_ca, (const char *)ssl_capath, (const char *)ssl_cipher); if (ssl_crl) { if (mysql_options(self->mysql, MYSQL_OPT_SSL_CRL, ssl_crl)) goto end; } if (ssl_crlpath) { if (mysql_options(self->mysql, MYSQL_OPT_SSL_CRLPATH, ssl_crlpath)) goto end; } if (ssl_verify_cert) { if (mysql_options(self->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (unsigned char *) &ssl_verify_cert)) goto end; } if (tls_version) { if (mysql_options(self->mysql, MARIADB_OPT_TLS_VERSION, tls_version)) goto end; } mysql_real_connect(self->mysql, host, user, password, schema, port, socket, client_flags); if (mysql_errno(self->mysql)) { goto end; } mariadb_get_infov(self->mysql, MARIADB_CONNECTION_HOST, (void *)&self->host); has_error= 0; end: MARIADB_END_ALLOW_THREADS(self); if (has_error) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return -1; } if (PyErr_Occurred()) return -1; return 0; } static int MrdbConnection_traverse( MrdbConnection *self, visitproc visit, void *arg) { return 0; } static PyObject *MrdbConnection_repr(MrdbConnection *self) { char cobj_repr[384]; if (!self->closed) snprintf(cobj_repr, 384, "", self->host, self); else snprintf(cobj_repr, 384, "", self); return PyUnicode_FromString(cobj_repr); } PyTypeObject MrdbConnection_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mariadb.connection", .tp_basicsize = (Py_ssize_t)sizeof(MrdbConnection), .tp_repr = (reprfunc)MrdbConnection_repr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, .tp_doc = connection__doc__, .tp_new = PyType_GenericNew, .tp_traverse = (traverseproc)MrdbConnection_traverse, .tp_methods = (struct PyMethodDef *)MrdbConnection_Methods, .tp_members = (struct PyMemberDef *)MrdbConnection_Members, .tp_getset = MrdbConnection_sets, .tp_init = (initproc)MrdbConnection_Initialize, .tp_alloc = PyType_GenericAlloc, .tp_finalize = (destructor)MrdbConnection_finalize }; PyObject * MrdbConnection_connect( PyObject *self, PyObject *args, PyObject *kwargs) { MrdbConnection *c; if (!(c= (MrdbConnection *)PyType_GenericAlloc(&MrdbConnection_Type, 1))) return NULL; if (MrdbConnection_Initialize(c, args, kwargs)) { Py_DECREF(c); return NULL; } return (PyObject *) c; } static void MrdbConnection_finalize(MrdbConnection *self) { if (self) { if (self->mysql) { MARIADB_BEGIN_ALLOW_THREADS(self) mysql_close(self->mysql); MARIADB_END_ALLOW_THREADS(self) self->mysql= NULL; } } } static PyObject * MrdbConnection_executecommand(MrdbConnection *self, PyObject *command) { const char *cmd; int rc; MARIADB_CHECK_CONNECTION(self, NULL); cmd= PyUnicode_AsUTF8AndSize(command, NULL); MARIADB_BEGIN_ALLOW_THREADS(self); rc= mysql_send_query(self->mysql, cmd, (long)strlen(cmd)); MARIADB_END_ALLOW_THREADS(self); if (rc) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } PyObject *MrdbConnection_close(MrdbConnection *self) { MARIADB_CHECK_CONNECTION(self, NULL); /* Todo: check if all the cursor stuff is deleted (when using prepared statements this should be handled in mysql_close) */ MARIADB_BEGIN_ALLOW_THREADS(self) mysql_close(self->mysql); MARIADB_END_ALLOW_THREADS(self) self->mysql= NULL; self->closed= 1; Py_RETURN_NONE; } static PyObject * MrdbConnection_exception(PyObject *self, void *closure) { PyObject *exception = *(PyObject **)closure; Py_INCREF(exception); return exception; } /* {{{ MrdbConnection_ping */ PyObject *MrdbConnection_ping(MrdbConnection *self) { int rc; MARIADB_CHECK_CONNECTION(self, NULL); MARIADB_BEGIN_ALLOW_THREADS(self); rc= mysql_ping(self->mysql); MARIADB_END_ALLOW_THREADS(self); if (rc) { mariadb_throw_exception(self->mysql, Mariadb_InterfaceError, 0, NULL); return NULL; } Py_RETURN_NONE; } /* }}} */ /* {{{ MrdbConnection_change_user */ PyObject *MrdbConnection_change_user(MrdbConnection *self, PyObject *args) { const char *user= NULL, *password= NULL, *database= NULL; int rc= 0; MARIADB_CHECK_CONNECTION(self, NULL); if (!PyArg_ParseTuple(args, "szz", &user, &password, &database)) return NULL; MARIADB_BEGIN_ALLOW_THREADS(self); rc= mysql_change_user(self->mysql, user, password, database); MARIADB_END_ALLOW_THREADS(self); if (rc) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } /* }}} */ /* {{{ MrdbConnection_getreconnect */ static PyObject *MrdbConnection_getreconnect(MrdbConnection *self, void *closure) { uint8_t reconnect= 0; if (self->mysql) { mysql_get_option(self->mysql, MYSQL_OPT_RECONNECT, &reconnect); } if (reconnect) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } /* }}} */ /* MrdbConnection_setreconnect */ static int MrdbConnection_setreconnect(MrdbConnection *self, PyObject *args, void *closure) { uint8_t reconnect; if (!self->mysql) { return 0; } if (!args || !CHECK_TYPE(args, &PyBool_Type)) { PyErr_SetString(PyExc_TypeError, "Argument must be boolean"); return -1; } reconnect= PyObject_IsTrue(args); mysql_optionsv(self->mysql, MYSQL_OPT_RECONNECT, &reconnect); return 0; } /* }}} */ static PyObject * MrdbConnection_getinfo(MrdbConnection *self, PyObject *optionval) { union { char *str; uint64_t num; uint8_t b; } val; uint32_t option; if (!optionval || !CHECK_TYPE_NO_NONE(optionval, &PyLong_Type)) { PyErr_SetString(PyExc_TypeError, "Parameter must be an integer value"); return NULL; } option= (uint32_t)PyLong_AsUnsignedLong(optionval); memset(&val, 0, sizeof(val)); if (mariadb_get_infov(self->mysql, option, &val)) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 1, "Parameter option not supported"); return NULL; } switch (option) { case MARIADB_CONNECTION_UNIX_SOCKET: case MARIADB_CONNECTION_USER: case MARIADB_CHARSET_NAME: case MARIADB_TLS_LIBRARY: case MARIADB_CLIENT_VERSION: case MARIADB_CONNECTION_HOST: case MARIADB_CONNECTION_INFO: case MARIADB_CONNECTION_SCHEMA: case MARIADB_CONNECTION_SQLSTATE: case MARIADB_CONNECTION_SOCKET: case MARIADB_CONNECTION_SSL_CIPHER: case MARIADB_CONNECTION_TLS_VERSION: case MARIADB_CONNECTION_SERVER_VERSION: return PyUnicode_FromString(val.str ? val.str : ""); break; case MARIADB_CHARSET_ID: case MARIADB_CLIENT_VERSION_ID: case MARIADB_CONNECTION_ASYNC_TIMEOUT: case MARIADB_CONNECTION_ASYNC_TIMEOUT_MS: case MARIADB_CONNECTION_PORT: case MARIADB_CONNECTION_PROTOCOL_VERSION_ID: case MARIADB_CONNECTION_SERVER_TYPE: case MARIADB_CONNECTION_SERVER_VERSION_ID: case MARIADB_CONNECTION_TLS_VERSION_ID: case MARIADB_MAX_ALLOWED_PACKET: case MARIADB_NET_BUFFER_LENGTH: case MARIADB_CONNECTION_SERVER_STATUS: case MARIADB_CONNECTION_SERVER_CAPABILITIES: case MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES: case MARIADB_CONNECTION_CLIENT_CAPABILITIES: #ifdef MARIADB_CONNECTION_BYTES_READ case MARIADB_CONNECTION_BYTES_READ: case MARIADB_CONNECTION_BYTES_SENT: #endif return PyLong_FromLong((long)val.num); break; default: Py_RETURN_NONE; } } /* {{{ MrdbConnection_reconnect */ PyObject *MrdbConnection_reconnect(MrdbConnection *self) { int rc; uint8_t reconnect= 1; uint8_t save_reconnect; MARIADB_CHECK_CONNECTION(self, NULL); mysql_get_option(self->mysql, MYSQL_OPT_RECONNECT, &save_reconnect); /* coverity[copy_paste_error] */ if (!save_reconnect) mysql_optionsv(self->mysql, MYSQL_OPT_RECONNECT, &reconnect); MARIADB_BEGIN_ALLOW_THREADS(self); rc= mariadb_reconnect(self->mysql); MARIADB_END_ALLOW_THREADS(self); if (!save_reconnect) mysql_optionsv(self->mysql, MYSQL_OPT_RECONNECT, &save_reconnect); if (rc) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return NULL; } /* get capabilities */ Py_RETURN_NONE; } /* }}} */ /* {{{ MrdbConnection_reset */ PyObject *MrdbConnection_reset(MrdbConnection *self) { int rc; MARIADB_CHECK_CONNECTION(self, NULL); MARIADB_BEGIN_ALLOW_THREADS(self); rc= mysql_reset_connection(self->mysql); MARIADB_END_ALLOW_THREADS(self); if (rc) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } /* }}} */ /* {{{ MrdbConnection_connection_id */ static PyObject *MrdbConnection_connection_id(MrdbConnection *self) { MARIADB_CHECK_CONNECTION(self, NULL); return PyLong_FromUnsignedLong(mysql_thread_id(self->mysql)); } /* }}} */ /* {{{ MrdbConnection_warnings */ static PyObject *MrdbConnection_warnings(MrdbConnection *self) { MARIADB_CHECK_CONNECTION(self, NULL); return PyLong_FromLong((long)mysql_warning_count(self->mysql)); } /* }}} */ /* {{{ MrdbConnection_escape_string */ static PyObject *MrdbConnection_escape_string(MrdbConnection *self, PyObject *str) { PyObject *string= NULL, *new_string= NULL; size_t from_length, to_length; char *from, *to; /* escaping depends on the server status, so we need a valid connection */ MARIADB_CHECK_CONNECTION(self, NULL); if (!CHECK_TYPE_NO_NONE(str, &PyUnicode_Type)) { PyErr_SetString(PyExc_TypeError, "Parameter must be a string"); return NULL; } from= (char *)PyUnicode_AsUTF8AndSize(str, (Py_ssize_t *)&from_length); if (!(to= (char *)PyMem_Calloc(1, from_length * 2 + 1))) { return NULL; } to_length= mysql_real_escape_string(self->mysql, to, from, (unsigned long)from_length); new_string= PyUnicode_FromStringAndSize(to, to_length); PyMem_Free(to); return new_string; } /* }}} */ static PyObject * MrdbConnection_dump_debug_info(MrdbConnection *self) { int rc; MARIADB_CHECK_CONNECTION(self, NULL); MARIADB_BEGIN_ALLOW_THREADS(self); rc= mysql_dump_debug_info(self->mysql); MARIADB_END_ALLOW_THREADS(self); if (rc) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } static PyObject *MrdbConnection_readresponse(MrdbConnection *self) { int rc; MARIADB_BEGIN_ALLOW_THREADS(self); rc= self->mysql->methods->db_read_query_result(self->mysql); MARIADB_END_ALLOW_THREADS(self); if (rc) { mariadb_throw_exception(self->mysql, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } static PyObject *MrdbConnection_socket(MrdbConnection *self) { MARIADB_CHECK_CONNECTION(self, NULL); return PyLong_FromLong((unsigned long)mysql_get_socket(self->mysql)); } /* vim: set tabstop=4 */ /* vim: set shiftwidth=4 */ /* vim: set expandtab */ /* vim: set foldmethod=indent */ /* vim: set foldnestmax=10 */ /* vim: set nofoldenable */ /* vim: set foldlevel=2 */ mariadb-connector-python-1.1.10/mariadb/mariadb_cursor.c000066400000000000000000001115631456007477700232670ustar00rootroot00000000000000 /***************************************************************************** Copyright (C) 2018-2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ****************************************************************************/ #include #include #include static void MrdbCursor_finalize(MrdbCursor *self); static PyObject * MrdbCursor_close(MrdbCursor *self); static PyObject * MrdbCursor_nextset(MrdbCursor *self); static PyObject * MrdbCursor_execute_binary(MrdbCursor *self); static PyObject * MrdbCursor_InitResultSet(MrdbCursor *self); static PyObject * MrdbCursor_execute_text(MrdbCursor *self, PyObject *stmt); static PyObject * MrdbCursor_check_text_types(MrdbCursor *self); static PyObject * MrdbCursor_fetchrows(MrdbCursor *self, PyObject *rows); static PyObject * MrdbCursor_parse(MrdbCursor *self, PyObject *stmt); static PyObject * MrdbCursor_description(MrdbCursor *self); static PyObject * MrdbCursor_fetchone(MrdbCursor *self); static PyObject * MrdbCursor_seek(MrdbCursor *self, PyObject *offset); static PyObject * MrdbCursor_execute_bulk(MrdbCursor *self); void field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column); static PyObject * MrdbCursor_readresponse(MrdbCursor *self); PyObject *MrdbCursor_clear_result(MrdbCursor *self); void field_fetch_callback(void *data, unsigned int column, unsigned char **row); static PyObject *mariadb_get_sequence_or_tuple(MrdbCursor *self); /* todo: write more documentation, this is just a placeholder */ static char mariadb_cursor_documentation[] = "Returns a MariaDB cursor object"; #define CURSOR_SET_STATEMENT(a,s,l)\ MARIADB_FREE_MEM((a)->statement);\ (a)->statement= PyMem_RawMalloc((l)+ 1);\ strncpy((a)->statement, (s), (l));\ (a)->statement_len= (unsigned long)(l);\ (a)->statement[(l)]= 0; #define CURSOR_FIELD_COUNT(a)\ ((a)->parseinfo.is_text ? mysql_field_count((a)->connection->mysql) : (a)->stmt ? mysql_stmt_field_count((a)->stmt) : 0) #define CURSOR_WARNING_COUNT(a)\ (((a)->parseinfo.is_text) ? (long)mysql_warning_count((a)->connection->mysql) : ((a)->stmt) ? (long)mysql_stmt_warning_count((a)->stmt) : 0L) #define CURSOR_AFFECTED_ROWS(a)\ (int64_t)((a)->parseinfo.is_text ? mysql_affected_rows((a)->connection->mysql) : (a)->stmt ? mysql_stmt_affected_rows((a)->stmt) : 0) #define CURSOR_INSERT_ID(a)\ ((a)->parseinfo.is_text ? mysql_insert_id((a)->connection->mysql) : (a)->stmt ? mysql_stmt_insert_id((a)->stmt) : 0) #define CURSOR_NUM_ROWS(a)\ ((a)->parseinfo.is_text ? mysql_num_rows((a)->result) : (a)->stmt ? mysql_stmt_num_rows((a)->stmt) : 0) static char *mariadb_named_tuple_name= "mariadb.Row"; static char *mariadb_named_tuple_desc= "Named tupled row"; static PyObject *Mariadb_row_count(MrdbCursor *self); static PyObject *Mariadb_row_number(MrdbCursor *self); static PyObject *MrdbCursor_warnings(MrdbCursor *self); static PyObject *MrdbCursor_closed(MrdbCursor *self); static PyObject *MrdbCursor_metadata(MrdbCursor *self); static PyGetSetDef MrdbCursor_sets[]= { {"description", (getter)MrdbCursor_description, NULL, cursor_description__doc__, NULL}, {"metadata", (getter)MrdbCursor_metadata, NULL, cursor_metadata__doc__, NULL}, {"rowcount", (getter)Mariadb_row_count, NULL, NULL, NULL}, {"warnings", (getter)MrdbCursor_warnings, NULL, cursor_warnings__doc__, NULL}, {"closed", (getter)MrdbCursor_closed, NULL, cursor_closed__doc__, NULL}, {"rownumber", (getter)Mariadb_row_number, NULL, cursor_rownumber__doc__, NULL}, {NULL} }; static PyMethodDef MrdbCursor_Methods[] = { /* PEP-249 methods */ {"close", (PyCFunction)MrdbCursor_close, METH_NOARGS, cursor_close__doc__}, {"fetchone", (PyCFunction)MrdbCursor_fetchone, METH_NOARGS, cursor_fetchone__doc__,}, {"fetchrows", (PyCFunction)MrdbCursor_fetchrows, METH_O, NULL}, {"_nextset", (PyCFunction)MrdbCursor_nextset, METH_NOARGS, cursor_nextset__doc__}, {"next", (PyCFunction)MrdbCursor_fetchone, METH_NOARGS, cursor_next__doc__}, /* internal helper functions */ {"_check_text_types", (PyCFunction) MrdbCursor_check_text_types, METH_NOARGS, NULL}, {"_seek", (PyCFunction)MrdbCursor_seek, METH_O, NULL}, {"_initresult", (PyCFunction)MrdbCursor_InitResultSet, METH_NOARGS, NULL}, {"_parse", (PyCFunction)MrdbCursor_parse, METH_O, NULL}, {"_readresponse", (PyCFunction)MrdbCursor_readresponse, METH_NOARGS, NULL}, {"_execute_text", (PyCFunction)MrdbCursor_execute_text, METH_O, NULL}, {"_execute_binary", (PyCFunction)MrdbCursor_execute_binary, METH_NOARGS, NULL}, {"_execute_bulk", (PyCFunction)MrdbCursor_execute_bulk, METH_NOARGS, NULL}, {"_initresult", (PyCFunction)MrdbCursor_InitResultSet, METH_NOARGS, NULL}, {"_readresponse", (PyCFunction)MrdbCursor_readresponse, METH_NOARGS, NULL}, {"_clear_result", (PyCFunction)MrdbCursor_clear_result, METH_NOARGS, NULL}, {NULL} /* always last */ }; static struct PyMemberDef MrdbCursor_Members[] = { {"statement", T_STRING, offsetof(MrdbCursor, parseinfo.statement), READONLY, cursor_statement__doc__}, {"_paramstyle", T_UINT, offsetof(MrdbCursor, parseinfo.paramstyle), READONLY, MISSING_DOC}, {"_reprepare", T_UINT, offsetof(MrdbCursor, reprepare), 0, MISSING_DOC}, {"_command", T_BYTE, offsetof(MrdbCursor, parseinfo.command), 0, MISSING_DOC}, {"_text", T_BOOL, offsetof(MrdbCursor, parseinfo.is_text), 0, MISSING_DOC}, {"_paramlist", T_OBJECT, offsetof(MrdbCursor, parseinfo.paramlist), READONLY, MISSING_DOC}, {"_resulttype", T_UINT, offsetof(MrdbCursor, result_format), 0, MISSING_DOC}, {"_keys", T_OBJECT, offsetof(MrdbCursor, parseinfo.keys), READONLY, MISSING_DOC}, {"paramcount", T_UINT, offsetof(MrdbCursor, parseinfo.paramcount), READONLY, cursor_paramcount__doc__}, {"_data", T_OBJECT, offsetof(MrdbCursor, data), 0, MISSING_DOC}, {"_cursor_type", T_ULONG, offsetof(MrdbCursor, cursor_type), 0, MISSING_DOC}, {"buffered", T_BOOL, offsetof(MrdbCursor, is_buffered), 0, cursor_buffered__doc__}, {"arraysize", T_LONG, offsetof(MrdbCursor, row_array_size), 0, cursor_arraysize__doc__}, {"field_count", T_UINT, offsetof(MrdbCursor, field_count), READONLY, cursor_field_count__doc__}, {"affected_rows", T_ULONGLONG, offsetof(MrdbCursor, affected_rows), READONLY, "This property is deprecated - use rowcount instead."}, {"_rownumber", T_ULONGLONG, offsetof(MrdbCursor, row_number), 0, NULL}, {"insert_id", T_UINT, offsetof(MrdbCursor, lastrow_id), READONLY, "returns the ID generated by a query on a table with a column " \ "having the AUTO_INCREMENT attribute or the value for the last "\ "usage of LAST_INSERT_ID()"}, {NULL} }; /* {{{ MrdbCursor_initialize Cursor initialization Optional keywprds: named_tuple (Boolean): return rows as named tuple instead of tuple prefetch_size: Prefetch size for readonly cursors cursor_type: Type of cursor: CURSOR_TYPE_READONLY or CURSOR_TYPE_NONE (default) buffered: buffered or unbuffered result sets */ static int MrdbCursor_initialize(MrdbCursor *self, PyObject *args, PyObject *kwargs) { char *key_words[]= {"", "prefetch_size", "cursor_type", "prepared", "binary", NULL}; PyObject *connection; unsigned long cursor_type= 0, prefetch_rows= 0; uint8_t is_prepared= 0; uint8_t is_binary= 0; if (!self) return -1; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|kkii", key_words, &MrdbConnection_Type, &connection, &prefetch_rows, &cursor_type, &is_prepared, &is_binary)) return -1; if (!((MrdbConnection *)connection)->mysql) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Connection isn't valid anymore"); return -1; } if (self->cursor_type != CURSOR_TYPE_READ_ONLY && self->cursor_type != CURSOR_TYPE_NO_CURSOR) { mariadb_throw_exception(NULL, Mariadb_DataError, 0, "Invalid value %ld for cursor_type", cursor_type); return -1; } self->connection= (MrdbConnection *)connection; self->is_prepared= is_prepared; self->parseinfo.is_text= 0; self->stmt= NULL; self->prefetch_rows= prefetch_rows; self->row_array_size= 1; return 0; } /* }}} */ static int MrdbCursor_traverse( MrdbCursor *self, visitproc visit, void *arg) { return 0; } static PyObject *MrdbCursor_repr(MrdbCursor *self) { char cobj_repr[384]; if (!self->closed) snprintf(cobj_repr, 384, "", self); else snprintf(cobj_repr, 384, "", self); return PyUnicode_FromString(cobj_repr); } PyTypeObject MrdbCursor_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mariadb.cursor", .tp_basicsize= (Py_ssize_t)sizeof(MrdbCursor), .tp_repr= (reprfunc)MrdbCursor_repr, .tp_flags= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, .tp_doc= mariadb_cursor_documentation, .tp_traverse= (traverseproc)MrdbCursor_traverse,/* tp_traverse */ .tp_methods= (struct PyMethodDef *)MrdbCursor_Methods, .tp_members= (struct PyMemberDef *)MrdbCursor_Members, .tp_getset= MrdbCursor_sets, .tp_init= (initproc)MrdbCursor_initialize, .tp_new= PyType_GenericNew, .tp_finalize= (destructor)MrdbCursor_finalize }; void MrdbCursor_clearparseinfo(MrdbParseInfo *parseinfo) { if (parseinfo->statement) MARIADB_FREE_MEM(parseinfo->statement); Py_XDECREF(parseinfo->keys); if (parseinfo->paramlist) Py_XDECREF(parseinfo->paramlist); memset(parseinfo, 0, sizeof(MrdbParseInfo)); } /* {{{ MrdbCursor_clear_result(MrdbCursor *self) clear pending result sets */ PyObject *MrdbCursor_clear_result(MrdbCursor *self) { if (!self->parseinfo.is_text && self->stmt) { /* free current result */ if (mysql_stmt_field_count(self->stmt)) { mysql_stmt_free_result(self->stmt); } /* check if there are more pending result sets */ while (mysql_stmt_next_result(self->stmt) == 0) { if (mysql_stmt_field_count(self->stmt)) { mysql_stmt_free_result(self->stmt); } } } else if (self->parseinfo.is_text) { /* free current result */ if (self->result) { mysql_free_result(self->result); } /* clear pending result sets */ if (self->connection->mysql) { do { MYSQL_RES *res; if ((res= mysql_use_result(self->connection->mysql))) mysql_free_result(res); } while (!mysql_next_result(self->connection->mysql)); } } MARIADB_END_ALLOW_THREADS(self->connection); /* CONPY-52: Avoid possible double free */ self->result= NULL; Py_RETURN_NONE; } static void MrdbCursor_FreeValues(MrdbCursor *self) { uint32_t i; if (!self->value) return; for (i= 0; i < self->parseinfo.paramcount; i++) if (self->value[i].free_me) MARIADB_FREE_MEM(self->value[i].buffer); MARIADB_FREE_MEM(self->value); } /* {{{ MrdbCursor_clear Resets statement attributes and frees associated memory */ static void MrdbCursor_clear(MrdbCursor *self, uint8_t new_stmt) { /* clear pending result sets */ MrdbCursor_clear_result(self); if (!self->parseinfo.is_text && self->stmt) { if (new_stmt) { mysql_stmt_close(self->stmt); self->stmt= mysql_stmt_init(self->connection->mysql); } else { uint32_t val= 0; mysql_stmt_reset(self->stmt); /* we need to unset array size only */ mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &val); } } self->fetched= 0; if (self->sequence_fields) { MARIADB_FREE_MEM(self->sequence_fields); } self->fields= NULL; self->row_count= 0; self->affected_rows= 0; MrdbCursor_FreeValues(self); MrdbCursor_clearparseinfo(&self->parseinfo); MARIADB_FREE_MEM(self->values); MARIADB_FREE_MEM(self->bind); MARIADB_FREE_MEM(self->statement); MARIADB_FREE_MEM(self->value); MARIADB_FREE_MEM(self->params); } /* }}} */ static void ma_set_result_column_value(MrdbCursor *self, PyObject *row, uint32_t column) { switch (self->result_format) { case RESULT_NAMED_TUPLE: PyStructSequence_SET_ITEM(row, column, self->values[column]); break; case RESULT_DICTIONARY: PyDict_SetItemString(row, self->fields[column].name, self->values[column]); Py_DECREF(self->values[column]); /* CONPY-119 */ break; default: PyTuple_SET_ITEM(row, column, (self)->values[column]); } } /* {{{ ma_cursor_close closes the statement handle of current cursor. After call to cursor_close the cursor can't be reused anymore */ static void ma_cursor_close(MrdbCursor *self) { if (!self->closed) { MrdbCursor_clear_result(self); if (!self->parseinfo.is_text && self->stmt) { /* Todo: check if all the cursor stuff is deleted (when using prepared statements this should be handled in mysql_stmt_close) */ MARIADB_BEGIN_ALLOW_THREADS(self->connection); mysql_stmt_close(self->stmt); MARIADB_END_ALLOW_THREADS(self->connection); self->stmt= NULL; } MrdbCursor_clear(self, 0); MrdbCursor_clearparseinfo(&self->parseinfo); self->closed= 1; } } static PyObject * MrdbCursor_close(MrdbCursor *self) { ma_cursor_close(self); Py_RETURN_NONE; } /* }}} */ /* {{{ MrdbCursor_Finalize */ static void MrdbCursor_finalize(MrdbCursor *self) { if (self->connection && self->connection->mysql) ma_cursor_close(self); } /* }}} */ static int Mrdb_GetFieldInfo(MrdbCursor *self) { self->row_number= 0; if (self->field_count) { if (self->parseinfo.is_text) { self->result= (self->is_buffered) ? mysql_store_result(self->connection->mysql) : mysql_use_result(self->connection->mysql); if (!self->result) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); return 1; } } else if (self->is_buffered) { if (mysql_stmt_store_result(self->stmt)) { mariadb_throw_exception(self->stmt, NULL, 1, NULL); return 1; } } self->affected_rows= CURSOR_AFFECTED_ROWS(self); self->fields= (self->parseinfo.is_text) ? mysql_fetch_fields(self->result) : mariadb_stmt_fetch_fields(self->stmt); if (self->result_format == RESULT_NAMED_TUPLE) { unsigned int i; PyStructSequence_Desc sequence_desc; if (!(self->sequence_fields= (PyStructSequence_Field *) PyMem_RawCalloc(self->field_count + 1, sizeof(PyStructSequence_Field)))) return 1; sequence_desc.name= mariadb_named_tuple_name; sequence_desc.doc= mariadb_named_tuple_desc; sequence_desc.fields= self->sequence_fields; sequence_desc.n_in_sequence= self->field_count; for (i=0; i < self->field_count; i++) { self->sequence_fields[i].name= self->fields[i].name; } self->sequence_type= PyStructSequence_NewType(&sequence_desc); #if PY_VERSION_HEX < 0x03070000 self->sequence_type->tp_flags|= Py_TPFLAGS_HEAPTYPE; #endif } } return 0; } PyObject *MrdbCursor_InitResultSet(MrdbCursor *self) { MARIADB_FREE_MEM(self->sequence_fields); MARIADB_FREE_MEM(self->values); if (self->result) { mysql_free_result(self->result); self->result= NULL; } if (self->field_count) { if (Mrdb_GetFieldInfo(self)) { return NULL; } if (!(self->values= (PyObject**)PyMem_RawCalloc(self->field_count, sizeof(PyObject *)))) return NULL; if (!self->parseinfo.is_text) mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_RESULT, field_fetch_callback); self->row_count= CURSOR_NUM_ROWS(self); self->affected_rows= 0; } else { self->row_count= self->affected_rows= CURSOR_AFFECTED_ROWS(self); } self->lastrow_id= CURSOR_INSERT_ID(self); Py_RETURN_NONE; } static int Mrdb_execute_direct(MrdbCursor *self, const char *statement, size_t statement_len) { int rc; MARIADB_BEGIN_ALLOW_THREADS(self->connection); long ext_caps; mariadb_get_infov(self->connection->mysql, MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES, &ext_caps); /* clear pending result sets */ MrdbCursor_clear_result(self); /* if stmt is already prepared */ if (!self->reprepare) { rc= mysql_stmt_execute(self->stmt); goto end; } /* execute_direct was implemented together with bulk operations, so we need to check if MARIADB_CLIENT_STMT_BULK_OPERATIONS is set in extended server capabilities */ if (!(ext_caps & (MARIADB_CLIENT_STMT_BULK_OPERATIONS >> 32))) { if (!(rc= mysql_stmt_prepare(self->stmt, statement, (unsigned long)statement_len))) { rc= mysql_stmt_execute(self->stmt); } } else { rc= mariadb_stmt_execute_direct(self->stmt, statement, statement_len); } end: MARIADB_END_ALLOW_THREADS(self->connection); return rc; } /* {{{ MrdbCursor_metadata */ static PyObject *MrdbCursor_metadata(MrdbCursor *self) { uint32_t i; PyObject *dict = NULL; const char *keys[14]= {"catalog", "schema", "field", "org_field", "table", "org_table", "type", "charset", "length", "max_length", "decimals", "flags", "ext_type_or_format"}; PyObject *tuple[14]= {0}; Mrdb_ExtFieldType *ext_field_type= NULL; if (!self->field_count) Py_RETURN_NONE; if (PyErr_Occurred()) return NULL; for (i=0; i < 13; i++) if (!(tuple[i] = PyTuple_New(self->field_count))) goto error; for (i=0; i < self->field_count; i++) { PyTuple_SetItem(tuple[0], i, PyUnicode_FromString(self->fields[i].catalog)); PyTuple_SetItem(tuple[1], i, PyUnicode_FromString(self->fields[i].db)); PyTuple_SetItem(tuple[2], i, PyUnicode_FromString(self->fields[i].name)); PyTuple_SetItem(tuple[3], i, PyUnicode_FromString(self->fields[i].org_name)); PyTuple_SetItem(tuple[4], i, PyUnicode_FromString(self->fields[i].table)); PyTuple_SetItem(tuple[5], i, PyUnicode_FromString(self->fields[i].org_table)); PyTuple_SetItem(tuple[6], i, PyLong_FromLong((long)self->fields[i].type)); PyTuple_SetItem(tuple[7], i, PyLong_FromLong((long)self->fields[i].charsetnr)); PyTuple_SetItem(tuple[8], i, PyLong_FromLongLong((long long)self->fields[i].max_length)); PyTuple_SetItem(tuple[9], i, PyLong_FromLongLong((long long)self->fields[i].length)); PyTuple_SetItem(tuple[10], i, PyLong_FromLong((long)self->fields[i].decimals)); PyTuple_SetItem(tuple[11], i, PyLong_FromLong((long)self->fields[i].flags)); if ((ext_field_type= mariadb_extended_field_type(&self->fields[i]))) PyTuple_SetItem(tuple[12], i, PyLong_FromLong((long)ext_field_type->ext_type)); else PyTuple_SetItem(tuple[12], i, PyLong_FromLong((long)EXT_TYPE_NONE)); } if (!(dict =PyDict_New())) goto error; for (i=0; i < 13; i++) { if (PyDict_SetItem(dict, PyUnicode_FromString(keys[i]), tuple[i])) goto error; Py_DECREF(tuple[i]); tuple[i]= NULL; } return dict; error: for (i=0; i < 13; i++) if (tuple[i]) Py_DECREF(tuple[i]); if (dict) Py_DECREF(dict); return NULL; } /* }}}*/ /* {{{ MrdbCursor_description PEP-249 description method() Please note that the returned tuple contains eight (instead of seven items, since we need the field flag */ static PyObject *MrdbCursor_description(MrdbCursor *self) { PyObject *obj= NULL; unsigned int field_count= self->field_count; if (PyErr_Occurred()) return NULL; if (self->fields && field_count) { uint32_t i; if (!(obj= PyTuple_New(field_count))) return NULL; for (i=0; i < field_count; i++) { uint32_t precision= 0; uint32_t decimals= 0; MY_CHARSET_INFO cs; unsigned long display_length; long packed_len= 0; PyObject *desc; Mrdb_ExtFieldType *ext_field_type= mariadb_extended_field_type(&self->fields[i]); display_length= self->fields[i].max_length > self->fields[i].length ? self->fields[i].max_length : self->fields[i].length; mysql_get_character_set_info(self->connection->mysql, &cs); if (cs.mbmaxlen > 1) { packed_len= display_length; display_length/= cs.mbmaxlen; } else { packed_len= mysql_ps_fetch_functions[self->fields[i].type].pack_len; } if (self->fields[i].decimals) { if (self->fields[i].decimals < 31) { decimals= self->fields[i].decimals; precision= self->fields[i].length; display_length= precision + 1; } } if (ext_field_type) { if (ext_field_type->ext_type == EXT_TYPE_JSON) self->fields[i].type= MYSQL_TYPE_JSON; } if (!(desc= Py_BuildValue("(sIIiIIOIsss)", self->fields[i].name, self->fields[i].type, display_length, packed_len >= 0 ? packed_len : -1, precision, decimals, PyBool_FromLong(!IS_NOT_NULL(self->fields[i].flags)), self->fields[i].flags, self->fields[i].table, self->fields[i].org_name, self->fields[i].org_table))) { Py_XDECREF(obj); mariadb_throw_exception(NULL, Mariadb_OperationalError, 0, "Can't build descriptor record"); return NULL; } PyTuple_SetItem(obj, i, desc); } return obj; } Py_RETURN_NONE; } /* }}} */ static int MrdbCursor_fetchinternal(MrdbCursor *self) { unsigned int field_count= self->field_count; MYSQL_ROW row; int rc; unsigned int i; self->fetched= 1; if (!self->parseinfo.is_text) { rc= mysql_stmt_fetch(self->stmt); if (rc == MYSQL_NO_DATA) return 1; return 0; } if (!(row= mysql_fetch_row(self->result))) { return 1; } for (i= 0; i < field_count; i++) { field_fetch_fromtext(self, row[i], i); } return 0; } static PyObject * MrdbCursor_fetchone(MrdbCursor *self) { PyObject *row; uint32_t i; unsigned int field_count= self->field_count; if (self->cursor_type == CURSOR_TYPE_READ_ONLY) MARIADB_CHECK_STMT(self); if (PyErr_Occurred()) { return NULL; } if (!field_count) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Cursor doesn't have a result set"); return NULL; } if (MrdbCursor_fetchinternal(self)) { Py_RETURN_NONE; } self->row_number++; if (!(row= mariadb_get_sequence_or_tuple(self))) { return NULL; } for (i= 0; i < field_count; i++) { ma_set_result_column_value(self, row, i); } return row; } static PyObject *MrdbCursor_seek(MrdbCursor *self, PyObject *pos) { uint64_t new_position= 0; if (!CHECK_TYPE_NO_NONE(pos, &PyLong_Type)) { PyErr_SetString(PyExc_TypeError, "Parameter must be an integer value"); return NULL; } new_position= (uint64_t)PyLong_AsUnsignedLongLong(pos); MARIADB_BEGIN_ALLOW_THREADS(self->connection); if (self->parseinfo.is_text) mysql_data_seek(self->result, new_position); else mysql_stmt_data_seek(self->stmt, new_position); MARIADB_END_ALLOW_THREADS(self->connection); Py_RETURN_NONE; } static PyObject * mariadb_get_sequence_or_tuple(MrdbCursor *self) { switch (self->result_format) { case RESULT_NAMED_TUPLE: return PyStructSequence_New(self->sequence_type); case RESULT_DICTIONARY: return PyDict_New(); default: return PyTuple_New(self->field_count); } } static PyObject * MrdbCursor_nextset(MrdbCursor *self) { int rc; MARIADB_CHECK_STMT(self); if (PyErr_Occurred()) { return NULL; } if (!self->parseinfo.is_text) { if (!self->stmt) Py_RETURN_NONE; MARIADB_BEGIN_ALLOW_THREADS(self->connection); rc= mysql_stmt_next_result(self->stmt); MARIADB_END_ALLOW_THREADS(self->connection); } else { if (self->result) { mysql_free_result(self->result); self->result= NULL; } MARIADB_BEGIN_ALLOW_THREADS(self->connection); rc= mysql_next_result(self->connection->mysql); MARIADB_END_ALLOW_THREADS(self->connection); } if (rc) { Py_RETURN_NONE; } if ((self->field_count= CURSOR_FIELD_COUNT(self))) { if (!MrdbCursor_InitResultSet(self)) { return NULL; } } else { self->fields= 0; } Py_RETURN_TRUE; } static PyObject * Mariadb_row_count(MrdbCursor *self) { if (!self->parseinfo.statement) return PyLong_FromLongLong(-1); if (self->field_count) return PyLong_FromLongLong(CURSOR_NUM_ROWS(self)); return PyLong_FromLongLong(CURSOR_AFFECTED_ROWS(self)); } static PyObject * Mariadb_row_number(MrdbCursor *self) { if (!self->field_count) { Py_RETURN_NONE; } return PyLong_FromLongLong(self->row_number); } static PyObject * MrdbCursor_warnings(MrdbCursor *self) { MARIADB_CHECK_STMT(self); return PyLong_FromLong((long)CURSOR_WARNING_COUNT(self)); } static PyObject *MrdbCursor_closed(MrdbCursor *self) { if (self->closed || self->connection->mysql == NULL) Py_RETURN_TRUE; Py_RETURN_FALSE; } static PyObject * MrdbCursor_parse(MrdbCursor *self, PyObject *stmt) { const char *statement= NULL; Py_ssize_t statement_len= 0; MrdbParser *parser= NULL; char errmsg[128]; uint32_t old_paramcount= 0; if (self->parseinfo.statement) { old_paramcount= self->parseinfo.paramcount; MrdbCursor_clearparseinfo(&self->parseinfo); } statement = (char *)PyUnicode_AsUTF8AndSize(stmt, (Py_ssize_t *)&statement_len); if (!(parser= MrdbParser_init(self->connection->mysql, statement, statement_len))) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Can't initialize parser."); return NULL; } if (MrdbParser_parse(parser, 0, errmsg, 128)) { MrdbParser_end(parser); PyErr_SetString(Mariadb_ProgrammingError, errmsg); return NULL; } /* cleanup and save some parser stuff */ if (parser->param_count && parser->param_count != old_paramcount) { MARIADB_FREE_MEM(self->params); MrdbCursor_FreeValues(self); MARIADB_FREE_MEM(self->values); MARIADB_FREE_MEM(self->bind); } self->parseinfo.paramcount= parser->param_count; self->parseinfo.paramstyle= parser->paramstyle; if (self->parseinfo.statement) PyMem_RawFree(self->parseinfo.statement); self->parseinfo.statement= PyMem_RawCalloc(parser->statement.length + 1, 1); memcpy(self->parseinfo.statement, parser->statement.str, parser->statement.length); self->parseinfo.statement_len= parser->statement.length; self->parseinfo.paramlist= parser->param_list; parser->param_list= NULL; self->parseinfo.is_text= (parser->command == SQL_NONE || parser->command == SQL_OTHER); self->parseinfo.command= parser->command; if (parser->paramstyle == PYFORMAT && parser->keys) { PyObject *tmp= PyTuple_New(parser->param_count); for (uint32_t i= 0; i < parser->param_count; i++) { PyObject *key; key= PyUnicode_FromString(parser->keys[i].str); PyTuple_SetItem(tmp, i, key); } self->parseinfo.keys= tmp; } MrdbParser_end(parser); Py_RETURN_NONE; } static PyObject * MrdbCursor_execute_binary(MrdbCursor *self) { int rc; unsigned char *buf= NULL; size_t buflen; MARIADB_CHECK_CONNECTION(self->connection, NULL); if (!self->stmt && !(self->stmt= mysql_stmt_init(self->connection->mysql))) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); goto error; } /* CONPY-164: reset array_size */ self->array_size= 0; mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &self->array_size); if (self->data && self->parseinfo.paramcount) { if (mariadb_check_execute_parameters(self, self->data)) goto error; /* Load values */ if (mariadb_param_update(self, self->params, 0)) goto error; } if (self->reprepare) { mysql_stmt_attr_set(self->stmt, STMT_ATTR_CURSOR_TYPE, &self->cursor_type); mysql_stmt_attr_set(self->stmt, STMT_ATTR_PREBIND_PARAMS, &self->parseinfo.paramcount); mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, (void *)self); } if (self->parseinfo.paramcount) mysql_stmt_bind_param(self->stmt, self->params); if (!(buf= self->connection->mysql->methods->db_execute_generate_request(self->stmt, &buflen, 1))) goto error; if ((rc= Mrdb_execute_direct(self, self->parseinfo.statement, self->parseinfo.statement_len))) { mariadb_throw_exception(self->stmt, NULL, 1, NULL); goto error; } self->field_count= mysql_stmt_field_count(self->stmt); Py_RETURN_NONE; error: return NULL; } static PyObject * MrdbCursor_execute_text(MrdbCursor *self, PyObject *stmt) { int rc; MYSQL *db; const char *statement; size_t statement_len= 0; MARIADB_CHECK_CONNECTION(self->connection, NULL); if (Py_TYPE(stmt) == &PyUnicode_Type) { statement = PyUnicode_AsUTF8AndSize(stmt, (Py_ssize_t *)&statement_len); } else if (Py_TYPE(stmt) == &PyBytes_Type) { PyBytes_AsStringAndSize(stmt, &statement, (Py_ssize_t *)&statement_len); } else { PyErr_SetString(PyExc_TypeError, "Parameter must be a string or bytes"); return NULL; } db= self->connection->mysql; MARIADB_BEGIN_ALLOW_THREADS(self->connection); rc= mysql_send_query(db, statement, (long)statement_len); MARIADB_END_ALLOW_THREADS(self->connection); if (rc) { mariadb_throw_exception(db, NULL, 0, NULL); return NULL; } Py_RETURN_NONE; } static PyObject * MrdbCursor_readresponse(MrdbCursor *self) { int rc; MYSQL *db; MARIADB_CHECK_CONNECTION(self->connection, NULL); db= self->connection->mysql; if (self->parseinfo.is_text) { MARIADB_BEGIN_ALLOW_THREADS(self->connection); rc= db->methods->db_read_query_result(db); MARIADB_END_ALLOW_THREADS(self->connection); if (rc) { mariadb_throw_exception(db, NULL, 0, NULL); return NULL; } self->field_count= mysql_field_count(self->connection->mysql); } Py_RETURN_NONE; } static PyObject * MrdbCursor_execute_bulk(MrdbCursor *self) { int rc; unsigned char *buf= NULL; size_t buflen; MARIADB_CHECK_STMT(self); if (PyErr_Occurred()) { return NULL; } if (!self->data) { PyErr_SetString(PyExc_TypeError, "No data provided"); return NULL; } if (!self->stmt) { if (!(self->stmt= mysql_stmt_init(self->connection->mysql))) { mariadb_throw_exception(self->connection->mysql, NULL, 0, NULL); goto error; } } if (mariadb_check_bulk_parameters(self, self->data)) goto error; /* If the server doesn't support bulk execution (< 10.2.6), we need to call a fallback routine */ if (self->reprepare) { mysql_stmt_attr_set(self->stmt, STMT_ATTR_PREBIND_PARAMS, &self->parseinfo.paramcount); mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_USER_DATA, (void *)self); mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_PARAM, mariadb_param_update); } mysql_stmt_attr_set(self->stmt, STMT_ATTR_ARRAY_SIZE, &self->array_size); mysql_stmt_bind_param(self->stmt, self->params); if (!(buf= self->connection->mysql->methods->db_execute_generate_request(self->stmt, &buflen, 1))) goto error; if ((rc= Mrdb_execute_direct(self, self->parseinfo.statement, self->parseinfo.statement_len))) { mariadb_throw_exception(self->stmt, NULL, 1, NULL); goto error; } if ((self->field_count= CURSOR_FIELD_COUNT(self))) { if (!MrdbCursor_InitResultSet(self)) { return NULL; } } else { self->affected_rows= CURSOR_AFFECTED_ROWS(self); self->lastrow_id= CURSOR_INSERT_ID(self); MARIADB_FREE_MEM(self->values); } Py_RETURN_NONE; error: MrdbCursor_clear(self, 0); return NULL; } static PyObject * MrdbCursor_fetchrows(MrdbCursor *self, PyObject *rows) { PyObject *List; unsigned int field_count= self->field_count; uint64_t row_count; MARIADB_CHECK_STMT_FETCH(self); if (!field_count) { mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, "Cursor doesn't have a result set"); return NULL; } if (!CHECK_TYPE_NO_NONE(rows, &PyLong_Type)) { PyErr_SetString(PyExc_TypeError, "Parameter must be an integer value"); return NULL; } row_count= (uint64_t)PyLong_AsLongLong(rows); if (!(List= PyList_New(0))) { return NULL; } for (uint64_t i=0; i < row_count && !MrdbCursor_fetchinternal(self); i++) { uint32_t j; PyObject *Row; self->row_number++; if (!(Row= mariadb_get_sequence_or_tuple(self))) { return NULL; } for (j=0; j < field_count; j++) { ma_set_result_column_value(self, Row, j); } PyList_Append(List, Row); /* CONPY-99: Decrement Row to prevent memory leak */ Py_DECREF(Row); } self->row_count= CURSOR_NUM_ROWS(self); return List; } static PyObject * MrdbCursor_check_text_types(MrdbCursor *self) { PyDateTime_IMPORT; Py_ssize_t ofs= 0; if (!self || !self->data || !self->parseinfo.paramcount) { Py_RETURN_NONE; } for (uint32_t i= 0; i < self->parseinfo.paramcount; i++) { PyObject *obj; if (PyDict_Check(self->data)) { PyDict_Next(self->data, &ofs, NULL, &obj); } else obj= ListOrTuple_GetItem(self->data, i); if (PyBytes_Check(obj) || PyByteArray_Check(obj) || PyDate_Check(obj)) Py_RETURN_TRUE; } Py_RETURN_NONE; } mariadb-connector-python-1.1.10/mariadb/mariadb_exception.c000066400000000000000000000165171456007477700237530ustar00rootroot00000000000000/************************************************************************************ Copyright (C) 2018 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA *************************************************************************************/ #include #include /* Exceptions */ PyObject *Mariadb_InterfaceError; PyObject *Mariadb_Error; PyObject *Mariadb_DatabaseError; PyObject *Mariadb_DataError; PyObject *Mariadb_PoolError; PyObject *Mariadb_OperationalError; PyObject *Mariadb_IntegrityError; PyObject *Mariadb_InternalError; PyObject *Mariadb_ProgrammingError; PyObject *Mariadb_NotSupportedError; PyObject *Mariadb_Warning; struct st_error_map { char sqlstate[3]; uint8_t type; }; static PyObject *get_exception_type(int error_number) { /* This list might be incomplete, special error values which are not handled yet will be returned as Internal or Operational errors. error codes are defined in errmsg.h (client errors) and mysqld_error.h (server errors) */ switch (error_number) { /* InterfaceError */ case 0: case CR_SERVER_LOST: case CR_SERVER_GONE_ERROR: case CR_SERVER_HANDSHAKE_ERR: case CR_IPSOCK_ERROR: case CR_COMMANDS_OUT_OF_SYNC: return Mariadb_InterfaceError; /* DataError: Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range, etc */ case ER_DATA_TOO_LONG: case ER_DATETIME_FUNCTION_OVERFLOW: case ER_DIVISION_BY_ZERO: case ER_NO_DEFAULT: case ER_PRIMARY_CANT_HAVE_NULL: case ER_WARN_DATA_OUT_OF_RANGE: case WARN_DATA_TRUNCATED: return Mariadb_DataError; /* ProgrammingError: Exception raised for programming errors, e.g. table not found or already exists, syntax error in the SQL statement, wrong number of parameters specified, etc. */ case ER_EMPTY_QUERY: case ER_CANT_DO_THIS_DURING_AN_TRANSACTION: case ER_DB_CREATE_EXISTS: case ER_FIELD_SPECIFIED_TWICE: case ER_INVALID_GROUP_FUNC_USE: case ER_NO_SUCH_INDEX: case ER_NO_SUCH_KEY_VALUE: case ER_NO_SUCH_TABLE: case ER_NO_SUCH_USER: case ER_PARSE_ERROR: case ER_SYNTAX_ERROR: case ER_TABLE_MUST_HAVE_COLUMNS: case ER_UNSUPPORTED_EXTENSION: case ER_WRONG_DB_NAME: case ER_WRONG_TABLE_NAME: case ER_BAD_DB_ERROR: return Mariadb_ProgrammingError; /* IntegrityError: Exception raised when the relational integrity of the database is affected, e.g. a foreign key check fails */ case ER_CANNOT_ADD_FOREIGN: case ER_DUP_ENTRY: case ER_DUP_UNIQUE: case ER_NO_DEFAULT_FOR_FIELD: case ER_NO_REFERENCED_ROW: case ER_NO_REFERENCED_ROW_2: case ER_ROW_IS_REFERENCED: case ER_ROW_IS_REFERENCED_2: case ER_XAER_OUTSIDE: case ER_XAER_RMERR: case ER_BAD_NULL_ERROR: case ER_DATA_OUT_OF_RANGE: case ER_CONSTRAINT_FAILED: case ER_DUP_CONSTRAINT_NAME: return Mariadb_IntegrityError; default: /* MariaDB Error */ if (error_number >= 1000) return Mariadb_OperationalError; /* same behavior as in MySQLdb: we return an InternalError, in case of system errors */ return Mariadb_InternalError; } return NULL; } void mariadb_exception_connection_gone(PyObject *exception_type, int error_no, const char *message, ...) { va_list ap; PyObject *ErrorMsg= 0; PyObject *ErrorNo= 0; PyObject *SqlState= 0; PyObject *Exception= 0; ErrorNo= PyLong_FromLong(CR_UNKNOWN_ERROR); SqlState= PyUnicode_FromString("HY000"); va_start(ap, message); ErrorMsg= PyUnicode_FromFormatV(message, ap); va_end(ap); if (!(Exception= PyObject_CallFunctionObjArgs(exception_type, ErrorMsg, NULL))) { PyErr_SetString(PyExc_RuntimeError, "Failed to create exception"); return; } PyObject_SetAttr(Exception, PyUnicode_FromString("sqlstate"), SqlState); PyObject_SetAttr(Exception, PyUnicode_FromString("errno"), ErrorNo); PyObject_SetAttr(Exception, PyUnicode_FromString("errmsg"), ErrorMsg); /* For MySQL Connector/Python compatibility */ PyObject_SetAttr(Exception, PyUnicode_FromString("msg"), ErrorMsg); PyErr_SetObject(exception_type, Exception); Py_XDECREF(ErrorMsg); Py_XDECREF(ErrorNo); Py_XDECREF(SqlState); } /** mariadb_throw_exception() @brief raises an exception @param handle[in] a connection or statement handle @param exception_type[in] type of exception @param handle_type[in] -1 no handle (use error_no) 0 MYSQL 1 MYSQL_STMT @param message[in] Error message. If message is NULL, the error message will be retrieved from specified handle. @param ... [in] message parameter @return void */ void mariadb_throw_exception(void *handle, PyObject *exception_type, int8_t is_statement, const char *message, ...) { va_list ap; PyObject *ErrorMsg= 0; PyObject *ErrorNo= 0; PyObject *SqlState= 0; PyObject *Exception= 0; if (message) { ErrorNo= PyLong_FromLong(CR_UNKNOWN_ERROR); SqlState= PyUnicode_FromString("HY000"); va_start(ap, message); ErrorMsg= PyUnicode_FromFormatV(message, ap); va_end(ap); } else { exception_type= get_exception_type(is_statement ? mysql_stmt_errno((MYSQL_STMT*) handle) : mysql_errno((MYSQL *)handle)); if (!exception_type) exception_type= Mariadb_DatabaseError; ErrorNo= PyLong_FromLong(is_statement ? mysql_stmt_errno((MYSQL_STMT *)handle) : mysql_errno((MYSQL *)handle)); ErrorMsg= PyUnicode_FromString(is_statement ? mysql_stmt_error((MYSQL_STMT *)handle) : mysql_error((MYSQL *)handle)); SqlState= PyUnicode_FromString(is_statement ? mysql_stmt_sqlstate((MYSQL_STMT *)handle) : mysql_sqlstate((MYSQL *)handle)); } if (!(Exception= PyObject_CallFunctionObjArgs(exception_type, ErrorMsg, NULL))) { PyErr_SetString(PyExc_RuntimeError, "Failed to create exception"); return; } PyObject_SetAttr(Exception, PyUnicode_FromString("sqlstate"), SqlState); PyObject_SetAttr(Exception, PyUnicode_FromString("errno"), ErrorNo); PyObject_SetAttr(Exception, PyUnicode_FromString("errmsg"), ErrorMsg); /* For MySQL Connector/Python compatibility */ PyObject_SetAttr(Exception, PyUnicode_FromString("msg"), ErrorMsg); PyErr_SetObject(exception_type, Exception); Py_XDECREF(ErrorMsg); Py_XDECREF(ErrorNo); Py_XDECREF(SqlState); } mariadb-connector-python-1.1.10/mariadb/mariadb_parser.c000077500000000000000000000274461456007477700232570ustar00rootroot00000000000000/***************************************************************************** Copyright (C) 2019,2020 Georg Richter and MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not see or write to the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110, USA ****************************************************************************/ #include #define IS_WHITESPACE(a) (a==32 || a==9 || a==10 || a==13) #define IN_LITERAL(p) ((p)->in_literal[0] ||\ (p)->in_literal[1] ||\ (p)->in_literal[2]) const char *comment_start= "/*"; const char *comment_end= "*/"; const char literals[3]= {'\'', '\"', '`'}; static struct { enum enum_binary_command command; MrdbString str; } binary_command[] = { {SQL_INSERT, {"INSERT", 6}}, {SQL_UPDATE, {"UPDATE", 6}}, {SQL_REPLACE, {"REPLACE", 7}}, {SQL_DELETE, {"DELETE", 6}}, {SQL_CALL, {"CALL", 4}}, {SQL_DO, {"DO", 2}}, {SQL_NONE, {NULL, 0}} }; static uint8_t check_keyword(char* ofs, char* end, char* keyword, size_t keylen) { int i; if ((size_t)(end - ofs) < keylen + 1) { return 0; } for (i = 0; i < (int)keylen; i++) { if (toupper(*(ofs + i)) != keyword[i]) { return 0; } } if (!IS_WHITESPACE(*(ofs + keylen))) { return 0; } return 1; } void MrdbParser_end(MrdbParser* p) { if (p) { if (p->keys) { uint32_t i; for (i=0; i < p->param_count; i++) { MARIADB_FREE_MEM(p->keys[i].str); } MARIADB_FREE_MEM(p->keys); } MARIADB_FREE_MEM(p->statement.str); MARIADB_FREE_MEM(p); } } MrdbParser * MrdbParser_init(MYSQL *mysql, const char *statement, size_t length) { MrdbParser *p; if (!statement || !length) { return NULL; } if ((p= PyMem_RawCalloc(1, sizeof(MrdbParser)))) { if (!(p->statement.str = (char *)PyMem_RawCalloc(1, length + 1))) { MARIADB_FREE_MEM(p); return NULL; } memcpy(p->statement.str, statement, length); p->statement.length= length; p->mysql= mysql; p->param_count= 0; } p->param_list= PyList_New(0); return p; } static void parser_error(char *errmsg, size_t errmsg_len, const char *errstr) { if (errmsg_len) { strncpy(errmsg, errstr, errmsg_len - 1); } } #define isutf8(c) (((c)&0xC0)!=0x80) uint8_t MrdbParser_parse(MrdbParser *p, uint8_t is_batch, char *errmsg, size_t errmsg_len) { char *a, *end; char lastchar= 0; uint8_t i; if (errmsg_len) *errmsg= 0; if (!p) { parser_error(errmsg, errmsg_len, "Parser not initialized"); return 1; } if (!p->statement.str || !p->statement.length) { parser_error(errmsg, errmsg_len, "Invalid (empty) statement"); return 1; } a= p->statement.str; end= a + p->statement.length - 1; while (a <= end) { cont: /* if (isutf8(*a)) { a++; continue; } */ /* check literals */ for (i=0; i < 3; i++) { if (*a == literals[i]) { p->in_literal[i]= !(p->in_literal[i]); a++; goto cont; } } /* nothing to do, if we are inside a comment or literal */ if (IN_LITERAL(p)) { a++; continue; } /* check comment */ if (!p->in_comment) { /* Style 1 */ if (*a == '/' && *(a + 1) == '*') { a+= 2; if (a+1 < end && *a == '!') { /* check special syntax: 1. comment followed by '!' and whitespace */ if (isspace(*(a+1))) { a+= 2; continue; } /* check special syntax: 3. comment followed by '!' 5 or 6 digit version number */ if (a + 7 < end && isdigit(*(a+1))) { char *x; unsigned long version_number= strtol(a+1, &x, 10); a= x; if ((version_number >= 50700 && version_number <= 99999) || !(version_number <= mysql_get_server_version(p->mysql))) { p->in_comment= 1; } continue; } } if (a+2 < end && *a == 'M' && *(a+1) == '!') { a+= 2; /* check special syntax: 2. comment followed by 'M! ' (MariaDB only) */ if (isspace(*(a))) continue; /* check special syntax: 2. comment followed by 'M!' and version number */ if (a + 6 < end && isdigit(*a)) { char *x; unsigned long version_number= strtol(a, &x, 10); a= x; if (!(version_number <= mysql_get_server_version(p->mysql))) { p->in_comment= 1; } continue; } } p->in_comment= 1; continue; } /* Style 2 */ if (*a == '#') { a++; p->comment_eol= 1; } /* Style 3 */ if (*a == '-' && *(a+1) == '-') { if (((a+2) < end) && *(a+2) == ' ') { a+= 3; p->comment_eol= 1; } } } else { if (*a == '*' && *(a + 1) == '/') { a+= 2; p->in_comment= 0; continue; } else { a++; continue; } } if (p->comment_eol) { if (*a == '\0' || *a == '\n') { a++; p->comment_eol= 0; continue; } a++; continue; } /* checking for different paramstyles */ /* parmastyle = qmark */ if (*a == '?') { PyObject *tmp; if (p->paramstyle && p->paramstyle != QMARK) { parser_error(errmsg, errmsg_len, "Mixing different parameter styles is not supported"); return 1; } p->paramstyle= QMARK; p->param_count++; tmp= PyLong_FromLong((long)(a - p->statement.str)); PyList_Append(p->param_list, tmp); Py_DECREF(tmp); a++; continue; } if (*a == '%' && lastchar != '\\') { /* paramstyle format */ if (*(a+1) == 's' || *(a+1) == 'd') { PyObject *tmp; if (p->paramstyle && p->paramstyle != FORMAT) { parser_error(errmsg, errmsg_len, "Mixing different parameter styles is not supported"); return 1; } p->paramstyle= FORMAT; *a= '?'; memmove(a+1, a+2, end - a); end--; tmp= PyLong_FromLong((long)(a - p->statement.str)); PyList_Append(p->param_list, tmp); Py_DECREF(tmp); a++; p->param_count++; continue; } if (*(a+1) == '(') { char *val_end= strstr(a+1, ")s"); PyObject *tmp; if (val_end) { ssize_t keylen= val_end - a + 1; if (p->paramstyle && p->paramstyle != PYFORMAT) { parser_error(errmsg, errmsg_len, "Mixing different parameter styles is not supported"); return 1; } p->paramstyle= PYFORMAT; *a= '?'; p->param_count++; tmp= PyLong_FromLong((long)(a - p->statement.str)); PyList_Append(p->param_list, tmp); Py_DECREF(tmp); if (p->keys) { MrdbString *m; if (!(m= PyMem_RawRealloc(p->keys, p->param_count * sizeof(MrdbString)))) { parser_error(errmsg, errmsg_len, "Not enough memory"); return 1; } p->keys= m; } else { if (!(p->keys= PyMem_RawMalloc(sizeof(MrdbString)))) { parser_error(errmsg, errmsg_len, "Not enough memory"); return 1; } } if (!(p->keys[p->param_count - 1].str= PyMem_RawCalloc(1, keylen - 2))) { parser_error(errmsg, errmsg_len, "Not enough memory"); return 1; } memcpy(p->keys[p->param_count - 1].str, a + 2, keylen - 3); p->keys[p->param_count - 1].length= keylen - 3; memmove(a+1, val_end+2, end - a - keylen + 1); a+= 1; end -= keylen; continue; } } } if (is_batch) { /* Do we have an insert statement ? */ if (!p->is_insert && check_keyword(a, end, "INSERT", 6)) { if (lastchar == 0 || (IS_WHITESPACE(lastchar)) || lastchar == '/') { p->is_insert = 1; a += 7; } } if (p->is_insert && check_keyword(a, end, "VALUES", 6)) { p->value_ofs = a + 7; a += 7; continue; } } else { /* determine SQL command */ if (p->command == SQL_NONE) { for (uint8_t i=0; binary_command[i].str.str; i++) { if (check_keyword(a, end, binary_command[i].str.str, binary_command[i].str.length)) { p->command= binary_command[i].command; break; } } if (p->command == SQL_NONE) p->command= SQL_OTHER; } } lastchar= *a; a++; } /* Update length */ p->statement.length= end - p->statement.str + 1; return 0; } mariadb-connector-python-1.1.10/mariadb_posix.py000066400000000000000000000053731456007477700217240ustar00rootroot00000000000000#!/usr/bin/env python import subprocess from packaging import version import sys import os class MariaDBConfiguration(): lib_dirs = [] libs = [] version = [] includes = [] extra_objects = [] extra_compile_args = [] extra_link_args = [] def mariadb_config(config, option): from os import popen file = popen("%s --%s" % (config, option)) data = file.read().strip().split() rc = file.close() if rc: if rc / 256: data = [] if rc / 256 > 1: raise EnvironmentError( """mariadb_config not found. This error typically indicates that MariaDB Connector/C, a dependency which must be preinstalled, is not found. If MariaDB Connector/C is not installed, see installation instructions If MariaDB Connector/C is installed, either set the environment variable MARIADB_CONFIG or edit the configuration file 'site.cfg' to set the 'mariadb_config' option to the file location of the mariadb_config utility. """) return data def dequote(s): if s[0] in "\"'" and s[0] == s[-1]: s = s[1:-1] return s def get_config(options): required_version = "3.3.1" static = options["link_static"] try: try: config_prg = os.environ["MARIADB_CONFIG"] except KeyError: config_prg = options["mariadb_config"] subprocess.call([config_prg, "--cc_version"]) except FileNotFoundError: # using default from path config_prg = "mariadb_config" cc_version = mariadb_config(config_prg, "cc_version") if version.Version(cc_version[0]) < version.Version(required_version): print('MariaDB Connector/Python requires MariaDB Connector/C ' '>= %s, found version %s' % (required_version, cc_version[0])) sys.exit(2) cfg = MariaDBConfiguration() cfg.version = cc_version[0] plugindir = mariadb_config(config_prg, "plugindir") libs = mariadb_config(config_prg, "libs") extra_libs = mariadb_config(config_prg, "libs_sys") cfg.lib_dirs = [dequote(i[2:]) for i in libs if i.startswith("-L")] cfg.libs = [dequote(i[2:]) for i in libs if i.startswith("-l")] includes = mariadb_config(config_prg, "include") mariadb_includes = [dequote(i[2:]) for i in includes if i.startswith("-I")] mariadb_includes.extend(["./include"]) if static.lower() == "on": cfg.extra_link_args = ["-u mysql_ps_fetch_functions"] cfg.extra_objects = ['{}/lib{}.a'.format(cfg.lib_dirs[0], lib) for lib in ["mariadbclient"]] cfg.libs = [dequote(i[2:]) for i in extra_libs if i.startswith("-l")] cfg.includes = mariadb_includes cfg.extra_compile_args = ["-DDEFAULT_PLUGINS_SUBDIR=\"%s\"" % plugindir[0]] return cfg mariadb-connector-python-1.1.10/mariadb_windows.py000077500000000000000000000060711456007477700222530ustar00rootroot00000000000000# # Windows configuration # import os import platform import sys from packaging import version from winreg import ConnectRegistry, OpenKey, QueryValueEx,\ HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_64KEY class MariaDBConfiguration(): lib_dirs = [] libs = [] version = [] includes = [] extra_objects = [] extra_compile_args = [] extra_link_args = [] def get_config(options): static = options["link_static"] mariadb_dir = options["install_dir"] required_version = "3.2.4" if not os.path.exists(mariadb_dir): try: mariadb_dir = os.environ["MARIADB_CC_INSTALL_DIR"] cc_version = ["", ""] print("using environment configuration " + mariadb_dir) except KeyError: try: local_reg = ConnectRegistry(None, HKEY_LOCAL_MACHINE) if platform.architecture()[0] == '32bit': connector_key = OpenKey(local_reg, 'SOFTWARE\\MariaDB Corporation\\' 'MariaDB Connector C') else: connector_key = OpenKey(local_reg, 'SOFTWARE\\MariaDB Corporation\\' 'MariaDB Connector C 64-bit', access=KEY_READ | KEY_WOW64_64KEY) cc_version = QueryValueEx(connector_key, "Version") if (version.Version(cc_version[0]) < version.Version(required_version)): print("MariaDB Connector/Python requires " "MariaDB Connector/C " ">= %s (found version: %s") \ % (required_version, cc_version[0]) sys.exit(2) mariadb_dir = QueryValueEx(connector_key, "InstallDir")[0] except Exception: print("Could not find InstallationDir of MariaDB Connector/C. " "Please make sure MariaDB Connector/C is installed or " "specify the InstallationDir of MariaDB Connector/C by " "setting the environment variable " "MARIADB_CC_INSTALL_DIR.") sys.exit(3) print("Found MariaDB Connector/C in '%s'" % mariadb_dir) cfg = MariaDBConfiguration() cfg.includes = [".\\include", mariadb_dir + "\\include", mariadb_dir + "\\include\\mysql"] cfg.lib_dirs = [mariadb_dir + "\\lib"] cfg.libs = ["ws2_32", "advapi32", "kernel32", "shlwapi", "crypt32", "secur32", "bcrypt"] if static.lower() == "on" or static.lower() == "default": cfg.libs.append("mariadbclient") else: print("dynamic") cfg.extra_link_args = ["/NODEFAULTLIB:LIBCMT"] cfg.extra_compile_args = ["/MD"] f = open("./include/config_win.h", "w") f.write("#define DEFAULT_PLUGINS_SUBDIR \"%s\\\\lib\\\\plugin\"" % options["install_dir"].replace(""'\\', '\\\\')) f.close() return cfg mariadb-connector-python-1.1.10/pyproject.toml000066400000000000000000000001101456007477700214250ustar00rootroot00000000000000[build-system] requires = [ "wheel", "setuptools", "packaging", ] mariadb-connector-python-1.1.10/setup.py000066400000000000000000000140651456007477700202410ustar00rootroot00000000000000#!/usr/bin/env python import os from setuptools import setup, Extension from configparser import ConfigParser # read the contents of your README file from os import path if os.name == "posix": from mariadb_posix import get_config if os.name == "nt": from mariadb_windows import get_config # noqa: F811 this_directory = path.abspath(path.dirname(__file__)) with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: long_description = f.read() define_macros = [] # read settings from site.cfg c = ConfigParser() c.read(['site.cfg']) options = dict(c.items('cc_options')) cfg = get_config(options) PY_MARIADB_AUTHORS = "Georg Richter" PY_MARIADB_MAJOR_VERSION = 1 PY_MARIADB_MINOR_VERSION = 1 PY_MARIADB_PATCH_VERSION = 10 PY_MARIADB_PRE_RELEASE_SEGMENT = None PY_MARIADB_PRE_RELEASE_NR = 0 PY_MARIADB_POST_RELEASE_SEGMENT = None PY_MARIADB_POST_RELEASE_NR = 0 PY_MARIADB_VERSION = "%s.%s.%s" % (PY_MARIADB_MAJOR_VERSION, PY_MARIADB_MINOR_VERSION, PY_MARIADB_PATCH_VERSION) if PY_MARIADB_POST_RELEASE_SEGMENT: PY_MARIADB_VERSION += ".%s" % (PY_MARIADB_POST_RELEASE_SEGMENT + PY_MARIADB_POST_RELEASE_NR) PY_MARIADB_VERSION_INFO = (PY_MARIADB_MAJOR_VERSION, PY_MARIADB_MINOR_VERSION, PY_MARIADB_PATCH_VERSION) if PY_MARIADB_PRE_RELEASE_SEGMENT: PY_MARIADB_VERSION_INFO = PY_MARIADB_VERSION_INFO + ( PY_MARIADB_PRE_RELEASE_SEGMENT, PY_MARIADB_PRE_RELEASE_NR) if PY_MARIADB_POST_RELEASE_SEGMENT: PY_MARIADB_VERSION_INFO = PY_MARIADB_VERSION_INFO + ( PY_MARIADB_POST_RELEASE_SEGMENT, PY_MARIADB_POST_RELEASE_NR) define_macros.append(("PY_MARIADB_MAJOR_VERSION", PY_MARIADB_MAJOR_VERSION)) define_macros.append(("PY_MARIADB_MINOR_VERSION", PY_MARIADB_MINOR_VERSION)) define_macros.append(("PY_MARIADB_PATCH_VERSION", PY_MARIADB_PATCH_VERSION)) define_macros.append(("PY_MARIADB_PRE_RELEASE_SEGMENT", "\"%s\"" % PY_MARIADB_PRE_RELEASE_SEGMENT)) define_macros.append(("PY_MARIADB_PRE_RELEASE_NR", "\"%s\"" % PY_MARIADB_PRE_RELEASE_NR)) define_macros.append(("PY_MARIADB_POST_RELEASE_SEGMENT", "\"%s\"" % PY_MARIADB_POST_RELEASE_SEGMENT)) define_macros.append(("PY_MARIADB_POST_RELEASE_NR", "\"%s\"" % PY_MARIADB_POST_RELEASE_NR)) with open("mariadb/release_info.py", "w") as rel_info: rel_info.write("__author__ = '%s'\n__version__ = '%s'\n__version_info__" " = %s\n" % (PY_MARIADB_AUTHORS, PY_MARIADB_VERSION, PY_MARIADB_VERSION_INFO)) setup(name='mariadb', version=PY_MARIADB_VERSION, python_requires='>=3.8', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Environment :: MacOS X', 'Environment :: Win32 (MS Windows)', 'License :: OSI Approved :: GNU Lesser General Public License' ' v2 or later (LGPLv2+)', 'Programming Language :: C', 'Programming Language :: Python', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Operating System :: Microsoft :: Windows', 'Operating System :: MacOS', 'Operating System :: POSIX', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Topic :: Database' ], description='Python MariaDB extension', long_description=long_description, long_description_content_type='text/markdown', author=PY_MARIADB_AUTHORS, license='LGPL 2.1', url='https://www.github.com/mariadb-corporation/' 'mariadb-connector-python', project_urls={ "Bug Tracker": "https://jira.mariadb.org/", "Documentation": "https://mariadb-corporation.github.io/" "mariadb-connector-python/", "Source Code": "https://www.github.com/mariadb-corporation/" "mariadb-connector-python", }, install_requires=['packaging'], ext_modules=[Extension('mariadb._mariadb', ['mariadb/mariadb.c', 'mariadb/mariadb_codecs.c', 'mariadb/mariadb_connection.c', 'mariadb/mariadb_cursor.c', 'mariadb/mariadb_exception.c', 'mariadb/mariadb_parser.c'], define_macros=define_macros, include_dirs=cfg.includes, library_dirs=cfg.lib_dirs, libraries=cfg.libs, extra_compile_args=cfg.extra_compile_args, extra_link_args=cfg.extra_link_args, extra_objects=cfg.extra_objects )], py_modules=['mariadb.__init__', 'mariadb.connectionpool', 'mariadb.connections', 'mariadb.constants.CAPABILITY', 'mariadb.constants.CLIENT', 'mariadb.constants.CURSOR', 'mariadb.constants.ERR', 'mariadb.constants.FIELD_FLAG', 'mariadb.constants.FIELD_TYPE', 'mariadb.constants.EXT_FIELD_TYPE', 'mariadb.constants.INDICATOR', 'mariadb.constants.INFO', 'mariadb.constants.STATUS', 'mariadb.constants.TPC_STATE', 'mariadb.cursors', 'mariadb.dbapi20', 'mariadb.field', 'mariadb.release_info']) mariadb-connector-python-1.1.10/site.cfg000066400000000000000000000006141456007477700201470ustar00rootroot00000000000000# configuration file for building MariaDB Connector/C Python [cc_options] # static or dynamic linking link_static=default # Windows: location of MySQL Connector/C installation install_dir=c:\Program Files\MariaDB\MariaDB Connector C 64-bit #install_dir=c:\Program Files (x86)\MariaDB\MariaDB Connector C # Posix: location of mariadb_config executable mariadb_config=/usr/local/bin/mariadb_config mariadb-connector-python-1.1.10/testing/000077500000000000000000000000001456007477700201765ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/bench.py000066400000000000000000000007771456007477700216420ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- # requirement: pip install pyperf import importlib from benchmarks.internal_bench import test_suite from benchmarks.internal_bench import run_test from test.conf_test import conf, glob module = glob() dbdrv = importlib.import_module(module["module"]) def main(): default_conf = conf() conn = dbdrv.connect(**default_conf) run_test(test_suite(dbdrv.paramstyle), conn, dbdrv.paramstyle) conn.close() if __name__ == "__main__": main() mariadb-connector-python-1.1.10/testing/bench_init.py000066400000000000000000000006471456007477700226610ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- # requirement: pip install pyperf import importlib from test.conf_test import conf, glob from benchmarks.setup_db import init_db module = glob() dbdrv = importlib.import_module(module["module"]) def main(): default_conf = conf() conn = dbdrv.connect(**default_conf) init_db(conn, dbdrv.paramstyle) conn.close() if __name__ == "__main__": main() mariadb-connector-python-1.1.10/testing/benchmarks/000077500000000000000000000000001456007477700223135ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/benchmarks/README.md000066400000000000000000000027211456007477700235740ustar00rootroot00000000000000# Benchmark ``` pip install mysql-connector-python pyperf python bench_mariadb.py -o mariadb_bench.json --inherit-environ=TEST_USER,TEST_HOST,TEST_PORT python bench_mysql.py -o mysql_bench.json --inherit-environ=TEST_USER,TEST_HOST,TEST_PORT ``` Results are available to pyperf json format An example of ``` >python -m pyperf compare_to mysql_bench.json mariadb_bench.json --table +----------------------------------------------------+-------------+------------------------------+ | Benchmark | mysql_bench | mariadb_bench | +====================================================+=============+==============================+ | do 1 | 114 us | 45.4 us: 2.50x faster (-60%) | +----------------------------------------------------+-------------+------------------------------+ | select 1 | 209 us | 57.3 us: 3.65x faster (-73%) | +----------------------------------------------------+-------------+------------------------------+ | select 1 mysql user | 1.04 ms | 122 us: 8.52x faster (-88%) | +----------------------------------------------------+-------------+------------------------------+ | Select <10 cols of 100 chars> from_seq_1_to_100000 | 323 ms | 35.0 ms: 9.22x faster (-89%) | +----------------------------------------------------+-------------+------------------------------+``` mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/000077500000000000000000000000001456007477700242455ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/bulk.py000066400000000000000000000017561456007477700255650ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf import random chars = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "\\Z", "😎", "🌶", "🎤", "🥂" ] def randomString(length): result = ""; for value in range(length): result = result + chars[random.randint(0, (len(chars) - 1))] return result; def bulk(loops, conn, paramstyle): # conn.autocommit= False t0 = pyperf.perf_counter() s = randomString(100) vals = [(s,) for i in range(100)] range_it = range(loops) for value in range_it: cursor = conn.cursor() if paramstyle == 'qmark': cursor.executemany("INSERT INTO perfTestTextBatch(t0) VALUES (?)", vals) else: cursor.executemany("INSERT INTO perfTestTextBatch(t0) VALUES (%s)", vals) del cursor return pyperf.perf_counter() - t0 mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/do_1.py000066400000000000000000000004631456007477700254440ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf def do1(loops, conn, paramstyle): range_it = range(loops) t0 = pyperf.perf_counter() for value in range_it: cursor = conn.cursor() cursor.execute('do 1') del cursor return pyperf.perf_counter() - t0 mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/do_1000_param.py000066400000000000000000000126221456007477700270440ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf def do_1000_param(loops, conn, paramstyle): range_it = range(loops) params = [] for i in range(1, 1001): params.append(i) tupleParam = tuple(params) t0 = pyperf.perf_counter() for value in range_it: cursor = conn.cursor() if paramstyle == 'qmark': cursor.execute("DO ?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?", tupleParam) else: cursor.execute("DO %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", params) del cursor return pyperf.perf_counter() - t0 mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/select_1.py000066400000000000000000000006151456007477700263200ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf def select_1(loops, conn, paramstyle): cursor = conn.cursor() range_it = range(loops) t0 = pyperf.perf_counter() for value in range_it: cursor = conn.cursor() cursor.execute("select 1") rows = cursor.fetchall() del rows cursor.close() return pyperf.perf_counter() - t0 mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/select_1000_rows.py000066400000000000000000000006431456007477700276130ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf def select_1000_rows(loops, conn, paramstyle): range_it = range(loops) t0 = pyperf.perf_counter() for value in range_it: cursor = conn.cursor() cursor.execute("select seq, 'abcdefghijabcdefghijabcdefghijaa' from seq_1_to_1000") rows = cursor.fetchall() del cursor, rows return pyperf.perf_counter() - t0 mariadb-connector-python-1.1.10/testing/benchmarks/benchmark/select_100_cols.py000066400000000000000000000013171456007477700275000ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf def select_100_cols(loops, conn, paramstyle): range_it = range(loops) t0 = pyperf.perf_counter() for value in range_it: cursor = conn.cursor() cursor.execute("select * FROM test100") rows = cursor.fetchall() del cursor, rows return pyperf.perf_counter() - t0 def select_100_cols_execute(loops, conn, paramstyle): range_it = range(loops) t0 = pyperf.perf_counter() for value in range_it: cursor = conn.cursor(binary=True) cursor.execute("select * FROM test100 WHERE 1 = ?", (1,)) rows = cursor.fetchall() del cursor, rows return pyperf.perf_counter() - t0 mariadb-connector-python-1.1.10/testing/benchmarks/internal_bench.py000066400000000000000000000023171456007477700256430ustar00rootroot00000000000000#!/usr/bin/env python3 -O # -*- coding: utf-8 -*- import pyperf import os from benchmarks.benchmark.bulk import bulk from benchmarks.benchmark.do_1 import do1 from benchmarks.benchmark.select_1 import select_1 from benchmarks.benchmark.do_1000_param import do_1000_param from benchmarks.benchmark.select_100_cols import select_100_cols, select_100_cols_execute from benchmarks.benchmark.select_1000_rows import select_1000_rows def run_test(tests, conn, paramstyle): runner = pyperf.Runner(warmups=1000, processes=1, min_time=10) for test in tests: runner.bench_time_func(test['label'], test['method'], conn, paramstyle) def test_suite(paramstyle): ts = [ {'label': 'BULK Insert', 'method': bulk}, {'label': 'DO 1', 'method': do1}, {'label': 'DO 1000 params', 'method': do_1000_param}, {'label': 'select_100_cols', 'method': select_100_cols}, {'label': 'select 1', 'method': select_1}, {'label': 'select_1000_rows', 'method': select_1000_rows}, ] if paramstyle == 'qmark': ts.append({'label': 'select_100_cols_execute', 'method': select_100_cols_execute}) return ts mariadb-connector-python-1.1.10/testing/benchmarks/setup_db.py000066400000000000000000000057321456007477700245010ustar00rootroot00000000000000 def init_db(conn, paramstyle): my_string = "abcdefghi🌟" str1 = "".join([my_string]*10) str2 = "".join([my_string]*24) str3 = "".join([my_string]*1024) cursor = conn.cursor() cursor.execute("DROP TABLE IF EXISTS str_test") cursor.execute("CREATE TABLE str_test (" "col1 varchar(200), col2 TEXT, col3 TEXT)") vals = [(str1, str2, str3) for i in range(100)] if paramstyle == 'qmark': cursor.executemany("INSERT INTO str_test VALUES (?, ?, ?)", vals) else: cursor.executemany("INSERT INTO str_test VALUES (%s, %s, %s)", vals) del cursor cursor = conn.cursor() cursor.execute("DROP TABLE IF EXISTS num_test") cursor.execute("CREATE TABLE num_test(" "col1 smallint, col2 int, col3 smallint, " "col4 bigint, col5 float, col6 decimal(10,5) )") vals = [(i % 128, 0xFF+i, 0xFFF+i, 0xFFFF+i, 10000 + i + 0.3123, 20000 + i + 0.1234) for i in range(1000)] if paramstyle == 'qmark': cursor.executemany("INSERT INTO num_test VALUES (?,?,?,?,?,?)", vals) else: cursor.executemany("INSERT INTO num_test VALUES (%s,%s,%s,%s,%s,%s)", vals) cursor.execute("DROP TABLE IF EXISTS test100") cursor.execute("CREATE TABLE test100 (i1 int,i2 int,i3 int,i4 int,i5 int,i6 int,i7 int,i8 int,i9 int,i10 int,i11 int,i12 int,i13 int,i14 int,i15 int,i16 int,i17 int,i18 int,i19 int,i20 int,i21 int,i22 int,i23 int,i24 int,i25 int,i26 int,i27 int,i28 int,i29 int,i30 int,i31 int,i32 int,i33 int,i34 int,i35 int,i36 int,i37 int,i38 int,i39 int,i40 int,i41 int,i42 int,i43 int,i44 int,i45 int,i46 int,i47 int,i48 int,i49 int,i50 int,i51 int,i52 int,i53 int,i54 int,i55 int,i56 int,i57 int,i58 int,i59 int,i60 int,i61 int,i62 int,i63 int,i64 int,i65 int,i66 int,i67 int,i68 int,i69 int,i70 int,i71 int,i72 int,i73 int,i74 int,i75 int,i76 int,i77 int,i78 int,i79 int,i80 int,i81 int,i82 int,i83 int,i84 int,i85 int,i86 int,i87 int,i88 int,i89 int,i90 int,i91 int,i92 int,i93 int,i94 int,i95 int,i96 int,i97 int,i98 int,i99 int,i100 int)") cursor.execute("INSERT INTO test100 value (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100)") cursor.execute("DROP TABLE IF EXISTS perfTestTextBatch") try: cursor.execute("INSTALL SONAME 'ha_blackhole'") except Error: pass createTable = "CREATE TABLE perfTestTextBatch (id MEDIUMINT NOT NULL AUTO_INCREMENT,t0 text, PRIMARY KEY (id)) COLLATE='utf8mb4_unicode_ci'" try: cursor.execute(createTable + " ENGINE = BLACKHOLE") except Exception: cursor.execute(createTable) conn.commit() del cursor def end_db(conn): cursor = conn.cursor() cursor.execute("DROP TABLE IF EXISTS num_test") del cursor mariadb-connector-python-1.1.10/testing/test/000077500000000000000000000000001456007477700211555ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/test/__init__.py000066400000000000000000000000001456007477700232540ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/test/base_test.py000066400000000000000000000016461456007477700235070ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import os import mariadb from .conf_test import conf def is_skysql(): if conf()["host"][-13:] == "db.skysql.net": return True return False def is_maxscale(): return (os.environ.get('srv') == "maxscale" or os.environ.get('srv') == 'skysql-ha') def is_mysql(): mysql_server = 1 conn = create_connection() cursor = conn.cursor() cursor.execute("select version()") row = cursor.fetchone() if "MARIADB" in row[0].upper(): mysql_server = 0 del cursor, conn return mysql_server def create_connection(additional_conf=None): default_conf = conf() if additional_conf is None: c = {key: value for (key, value) in (default_conf.items())} else: c = {key: value for (key, value) in (list(default_conf.items()) + list( additional_conf.items()))} return mariadb.connect(**c) mariadb-connector-python-1.1.10/testing/test/conf_test.py000066400000000000000000000015021456007477700235110ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import os def glob(): dm = { "module": os.environ.get('TEST_MODULE', 'mariadb'), } return dm def conf(): d = { "user": os.environ.get('TEST_DB_USER', 'root'), "host": os.environ.get('TEST_DB_HOST', 'localhost'), "database": os.environ.get('TEST_DB_DATABASE', 'testp'), "port": int(os.environ.get('TEST_DB_PORT', '3306')), } if os.environ.get('TEST_REQUIRE_TLS'): if os.environ.get('TEST_REQUIRE_TLS') == "1": d["ssl"] = True if os.environ.get('TEST_RESET_SESSION'): reset = int(os.environ.get('TEST_RESET_SESSION', '1')) d["pool_reset_connection"] = reset if os.environ.get('TEST_DB_PASSWORD'): d["password"] = os.environ.get('TEST_DB_PASSWORD') return d mariadb-connector-python-1.1.10/testing/test/integration/000077500000000000000000000000001456007477700235005ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/test/integration/__init__.py000066400000000000000000000000001456007477700255770ustar00rootroot00000000000000mariadb-connector-python-1.1.10/testing/test/integration/test_connection.py000066400000000000000000000255671456007477700272670ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import os import unittest import mariadb from test.base_test import create_connection, is_skysql, is_maxscale from test.conf_test import conf from mariadb.constants import STATUS import platform from packaging.version import parse as parse_version class TestConnection(unittest.TestCase): def setUp(self): self.connection = create_connection() def tearDown(self): del self.connection def test_conpy36(self): if platform.system() == "Windows": self.skipTest("unix_socket not supported on Windows") default_conf = conf() try: mariadb.connect(user=default_conf["user"], unix_socket="/does_not_exist/x.sock", port=default_conf["port"], host=default_conf["host"]) except (mariadb.OperationalError,): pass def test_connection_default_file(self): if os.path.exists("client.cnf"): os.remove("client.cnf") default_conf = conf() f = open("client.cnf", "w+") f.write("[client]\n") f.write("host =%s\n" % default_conf["host"]) f.write("port =%i\n" % default_conf["port"]) f.write("user =%s\n" % default_conf["user"]) if "password" in default_conf: f.write("password =%s\n" % default_conf["password"]) f.write("database =%s\n" % default_conf["database"]) f.close() new_conn = mariadb.connect(user=default_conf["user"], ssl=True, default_file="./client.cnf") self.assertEqual(new_conn.database, default_conf["database"]) del new_conn os.remove("client.cnf") def test_autocommit(self): conn = self.connection conn.autocommit = False self.assertEqual(conn.autocommit, False) # revert conn.autocommit = True self.assertEqual(conn.autocommit, True) def test_local_infile(self): default_conf = conf() new_conn = mariadb.connect(**default_conf, local_infile=False) cursor = new_conn.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a int)") try: cursor.execute("LOAD DATA LOCAL INFILE 'x.x' INTO TABLE t1") except (mariadb.OperationalError,): pass del cursor del new_conn def test_tls_version(self): if is_maxscale(): self.skipTest("MAXSCALE test has no SSL on port by default") default_conf = conf() conn = mariadb.connect(**default_conf, tls_version="TLSv1.2") cursor = conn.cursor() cursor.execute("SHOW STATUS LIKE 'ssl_version'") row = cursor.fetchone() self.assertEqual(row[1], "TLSv1.2") cursor.close() conn.close() def test_init_command(self): default_conf = conf() new_conn = mariadb.connect(**default_conf, init_command="SET @a:=1") cursor = new_conn.cursor() cursor.execute("SELECT @a") row = cursor.fetchone() self.assertEqual(row[0], 1) del cursor del new_conn def test_compress(self): default_conf = conf() new_conn = mariadb.connect(**default_conf, compress=True) cursor = new_conn.cursor() cursor.execute("SHOW SESSION STATUS LIKE 'compression'") row = cursor.fetchone() if is_maxscale(): self.assertEqual(row[1], "OFF") else: self.assertEqual(row[1], "ON") del cursor del new_conn def test_schema(self): if self.connection.server_version < 100202: self.skipTest("session tracking not supported") if is_maxscale(): self.skipTest("MAXSCALE doesn't tell schema change for now") default_conf = conf() conn = self.connection self.assertEqual(conn.database, default_conf["database"]) cursor = conn.cursor() cursor.execute("DROP SCHEMA IF EXISTS test1") cursor.execute("CREATE SCHEMA test1") cursor.execute("USE test1") self.assertEqual(conn.database, "test1") conn.database = default_conf["database"] self.assertEqual(conn.database, default_conf["database"]) def test_ping(self): if is_maxscale(): self.skipTest("MAXSCALE wrong thread id") conn = self.connection cursor = conn.cursor() oldid = conn.connection_id try: cursor.execute("KILL {id}" . format(id=oldid)) except mariadb.Error: pass conn.auto_reconnect = True conn.ping() self.assertNotEqual(oldid, conn.connection_id) self.assertNotEqual(0, conn.connection_id) def test_ed25519(self): if is_skysql(): self.skipTest("Test fail on SkySQL") default_conf = conf() if is_maxscale(): self.skipTest("MAXSCALE doesn't support ed25519 for now") if self.connection.server_version < 100122: self.skipTest("ed25519 not supported") conn = self.connection curs = conn.cursor(buffered=True) if self.connection.server_name == "localhost": curs.execute("select * from information_schema.plugins where " "plugin_name ='unix_socket' and " "plugin_status ='ACTIVE'") if curs.rowcount > 0: del curs self.skipTest("unix_socket is active") cursor = conn.cursor() try: cursor.execute("INSTALL SONAME 'auth_ed25519'") except (mariadb.DatabaseError, mariadb.OperationalError): self.skipTest("Server couldn't load auth_ed25519") cursor.execute("DROP USER IF EXISTS eduser") if self.connection.server_version < 100400: cursor.execute("CREATE USER eduser@'%' IDENTIFIED VIA ed25519 " "USING " "'6aW9C7ENlasUfymtfMvMZZtnkCVlcb1ssxOLJ0kj/AA'") else: cursor.execute("CREATE USER eduser@'%' IDENTIFIED VIA ed25519 " "USING PASSWORD('MySup8%rPassw@ord')") cursor.execute("GRANT ALL on " + default_conf["database"] + ".* to eduser@'%'") conn2 = create_connection({"user": "eduser", "password": "MySup8%rPassw@ord"}) cursor.execute("DROP USER IF EXISTS eduser") try: create_connection({"user": "eduser", "password": "MySup8%rPassw@ord", "plugin_dir": "wrong_plugin_dir"}) self.fail("wrong plugin directory, must not have found " "authentication plugin") except (mariadb.OperationalError): pass cursor.execute("DROP USER IF EXISTS eduser") del cursor, conn2 def test_conpy46(self): with create_connection() as con: with con.cursor() as cursor: cursor.execute("SELECT 'foo'") row = cursor.fetchone() self.assertEqual(row[0], "foo") try: cursor.execute("SELECT 'bar'") except mariadb.ProgrammingError: pass try: cursor = con.cursor() except mariadb.ProgrammingError: pass def test_conpy101(self): default_conf = conf() c1 = mariadb.connect(**default_conf) self.assertEqual(c1.autocommit, False) c1 = mariadb.connect(**default_conf, autocommit=True) self.assertEqual(c1.autocommit, True) def test_db_attribute(self): con = create_connection() cursor = con.cursor() db = con.database try: cursor.execute("create schema test123") except mariadb.Error: pass con.database = "test123" cursor.execute("select database()", buffered=True) row = cursor.fetchone() self.assertEqual(row[0], "test123") con.database = db cursor.execute("select database()", buffered=True) row = cursor.fetchone() self.assertEqual(row[0], db) self.assertEqual(row[0], con.database) cursor.execute("drop schema test123") del cursor def test_server_status(self): con = create_connection() self.assertTrue(not con.server_status & STATUS.AUTOCOMMIT) con.autocommit = True self.assertTrue(con.server_status & STATUS.AUTOCOMMIT) con.autocommit = False self.assertTrue(not con.server_status & STATUS.AUTOCOMMIT) def test_conpy175(self): default_conf = conf() conn = mariadb.connect(**default_conf) str = "Bob's" cursor= conn.cursor() cursor.execute("SET session sql_mode='NO_BACKSLASH_ESCAPES'") newstr = conn.escape_string(str) self.assertEqual(newstr, "Bob''s") cursor.execute("SET session sql_mode=''") newstr = conn.escape_string(str) self.assertEqual(newstr, "Bob\\'s") conn.close() def test_closed(self): default_conf = conf() conn = mariadb.connect(**default_conf) conn.close() try: conn.cursor() except (mariadb.ProgrammingError): pass def test_multi_host(self): default_conf = conf() default_conf["host"] = "non_existant," + default_conf["host"] try: mariadb.connect(**default_conf) except mariadb.ProgrammingError: self.assertLess(parse_version(mariadb.mariadbapi_version), parse_version('3.3.0')) pass def test_conpy278(self): if is_maxscale(): self.skipTest("MAXSCALE bug MXS-4961") test_conf= conf() test_conf["reconnect"]= True conn= mariadb.connect(**test_conf) old_id= conn.connection_id try: conn.kill(conn.connection_id) except mariadb.OperationalError: conn.ping() self.assertNotEqual(old_id, conn.connection_id) conn.close() conn= mariadb.connect(**test_conf) old_id= conn.connection_id try: conn.kill(conn.connection_id) except mariadb.OperationalError: conn.ping() self.assertNotEqual(old_id, conn.connection_id) conn.close() conn= mariadb.connect(**test_conf) old_id= conn.connection_id try: conn.kill(conn.connection_id) except mariadb.OperationalError: pass cursor= conn.cursor() try: cursor.execute("set @a:=1") except mariadb.InterfaceError: pass cursor.execute("set @a:=1") self.assertNotEqual(old_id, conn.connection_id) old_id= conn.connection_id conn.reconnect() self.assertNotEqual(old_id, conn.connection_id) conn.close() if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_converter.py000066400000000000000000000032331456007477700271210ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import datetime import unittest from mariadb.constants import FIELD_TYPE from test.base_test import create_connection class foo(int): def bar(self): pass def timedelta_to_time(s): return (datetime.datetime.min + s).time() def long_minus(s): return s - 1 def none_to_string(s): if s is None: return "None" return s conversions = { **{FIELD_TYPE.TIME: timedelta_to_time}, **{FIELD_TYPE.LONG: long_minus}, **{FIELD_TYPE.NULL: none_to_string}, **{FIELD_TYPE.LONGLONG: long_minus}, } class TestConversion(unittest.TestCase): def setUp(self): self.connection = create_connection({"converter": conversions}) self.connection.autocommit = False def tearDown(self): del self.connection def test_convert_time(self): cursor = self.connection.cursor() a = datetime.time(12, 29, 21) cursor.execute("SELECT cast(? as time)", (a,)) row = cursor.fetchone() self.assertEqual(row[0], a) del cursor def test_convert_long(self): cursor = self.connection.cursor() a = 12345 cursor.execute("SELECT CAST(? AS SIGNED)", (12345,)) row = cursor.fetchone() self.assertEqual(row[0], a - 1) del cursor def test_convert_none(self): cursor = self.connection.cursor() cursor.execute("SELECT NULL") row = cursor.fetchone() self.assertEqual(row[0], "None") cursor.execute("SELECT ?", (None,)) row = cursor.fetchone() self.assertEqual(row[0], "None") del cursor if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_cursor.py000066400000000000000000001675051456007477700264440ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import datetime import unittest import os import decimal import json from decimal import Decimal import mariadb from mariadb.constants import FIELD_TYPE, EXT_FIELD_TYPE, ERR, CURSOR, INDICATOR, CLIENT from test.base_test import create_connection, is_maxscale, is_mysql server_indicator_version = 100206 class foo(int): def bar(self): pass class TestCursor(unittest.TestCase): def setUp(self): self.connection = create_connection() self.connection.autocommit = False def tearDown(self): del self.connection def test_conpy251(self): cursor = self.connection.cursor() x = cursor.nextset() self.assertEqual(x, None) cursor.close() def test_multiple_close(self): cursor = self.connection.cursor() cursor.close() del cursor def test_date(self): v = self.connection.server_version i = self.connection.server_info.lower() if (v) or ("mariadb" not in i and v < 50600): self.skipTest("microsecond not supported") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_date(" "c1 TIMESTAMP(6), c2 TIME(6), " "c3 DATETIME(6), c4 DATE)") t = datetime.datetime(2018, 6, 20, 12, 22, 31, 123456) c1 = t c2 = t.time() c3 = t c4 = t.date() cursor.execute("INSERT INTO test_date VALUES (?,?,?,?)", (c1, c2, c3, c4)) cursor.execute("SELECT c1,c2,c3,c4 FROM test_date") row = cursor.fetchone() self.assertEqual(row[0], c1) self.assertEqual(row[1], datetime.timedelta(seconds=44551, microseconds=123456)) self.assertEqual(row[2], c3) self.assertEqual(row[3], c4) cursor.close() def test_numbers(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_numbers (" "a tinyint unsigned, b smallint unsigned, " "c mediumint unsigned, d int unsigned, " "e bigint unsigned, f double)") c1 = 4 c2 = 200 c3 = 167557 c4 = 28688817 c5 = 7330133222578 c6 = 3.1415925 cursor.execute("insert into test_numbers values (?,?,?,?,?,?)", (c1, c2, c3, c4, c5, c6)) cursor.execute("select * from test_numbers") row = cursor.fetchone() self.assertEqual(row[0], c1) self.assertEqual(row[1], c2) self.assertEqual(row[2], c3) self.assertEqual(row[3], c4) self.assertEqual(row[4], c5) self.assertEqual(row[5], c6) del cursor def test_string(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_string (" "a char(5), b varchar(100), c tinytext, " "d mediumtext, e text, f longtext)") c1 = "12345" c2 = "The length of this text is < 100 characters" c3 = "This should also fit into tinytext which"\ " has a maximum of 255 characters" c4 = 'a' * 1000 c5 = 'b' * 6000 c6 = 'c' * 67000 cursor.execute("INSERT INTO test_string VALUES (?,?,?,?,?,?)", (c1, c2, c3, c4, c5, c6)) cursor.execute("SELECT * from test_string") row = cursor.fetchone() self.assertEqual(row[0], c1) self.assertEqual(row[1], c2) self.assertEqual(row[2], c3) self.assertEqual(row[3], c4) self.assertEqual(row[4], c5) self.assertEqual(row[5], c6) del cursor def test_blob(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_blob (" "a tinyblob, b mediumblob, c blob, " "d longblob)") c1 = b'a' * 100 c2 = b'b' * 1000 c3 = b'c' * 10000 c4 = b'd' * 100000 cursor.execute("INSERT INTO test_blob VALUES (?,?,?,?)", (c1, c2, c3, c4)) cursor.execute("SELECT * FROM test_blob") row = cursor.fetchone() self.assertEqual(row[0], c1) self.assertEqual(row[1], c2) self.assertEqual(row[2], c3) self.assertEqual(row[3], c4) del cursor def test_inserttuple(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_inserttuple (" "id int, name varchar(64), " "city varchar(64))") params = ((1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")) cursor.executemany("INSERT INTO test_inserttuple VALUES (?,?,?)", params) cursor.execute("SELECT name FROM test_inserttuple ORDER BY id DESC") row = cursor.fetchone() self.assertEqual("Andrey", row[0]) del cursor def test_fetchmany(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_fetchmany (" "id int, name varchar(64), " "city varchar(64))") params = [(1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")] cursor.executemany("INSERT INTO test_fetchmany VALUES (?,?,?)", params) # test Errors # a) if no select was executed self.assertRaises(mariadb.Error, cursor.fetchall) # b ) if cursor was not executed del cursor cursor = self.connection.cursor(buffered=False) self.assertRaises(mariadb.Error, cursor.fetchall) cursor.execute("SELECT id, name, city FROM test_fetchmany ORDER BY id") self.assertEqual(0, cursor.rowcount) row = cursor.fetchall() self.assertEqual(row, params) self.assertEqual(5, cursor.rowcount) cursor.execute("SELECT id, name, city FROM test_fetchmany ORDER BY id") self.assertEqual(0, cursor.rowcount) row = cursor.fetchmany(1) self.assertEqual(row, [params[0]]) self.assertEqual(1, cursor.rowcount) row = cursor.fetchmany(2) self.assertEqual(row, ([params[1], params[2]])) self.assertEqual(3, cursor.rowcount) cursor.arraysize = 1 row = cursor.fetchmany() self.assertEqual(row, [params[3]]) self.assertEqual(4, cursor.rowcount) cursor.arraysize = 2 row = cursor.fetchmany() self.assertEqual(row, [params[4]]) self.assertEqual(5, cursor.rowcount) del cursor def test1_multi_result(self): cursor = self.connection.cursor() cursor.execute("DROP PROCEDURE IF EXISTS p1") sql = """ CREATE PROCEDURE p1() BEGIN SELECT 1 FROM DUAL; SELECT 2 FROM DUAL; END """ cursor.execute(sql) cursor.execute("call p1()") row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.nextset() row = cursor.fetchone() self.assertEqual(row[0], 2) del cursor def test_buffered(self): cursor = self.connection.cursor(buffered=True) cursor.execute("SELECT 1 UNION SELECT 2 UNION SELECT 3") self.assertEqual(cursor.rowcount, 3) cursor.scroll(1) row = cursor.fetchone() self.assertEqual(row[0], 2) del cursor def test_ext_field_types(self): x = self.connection.server_version_info if x < (10, 10, 0) or is_mysql(): self.skipTest("Skip (MySQL and MariaDB < 10.10)") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a json, b uuid, c inet4, d inet6,"\ "e point)") cursor.execute("SELECT a,b,c,d,e FROM t1") metadata= cursor.metadata self.assertEqual(metadata["ext_type_or_format"][0], EXT_FIELD_TYPE.JSON) self.assertEqual(metadata["type"][0], FIELD_TYPE.BLOB) self.assertEqual(metadata["ext_type_or_format"][1], EXT_FIELD_TYPE.UUID) self.assertEqual(metadata["type"][1], FIELD_TYPE.STRING) self.assertEqual(metadata["ext_type_or_format"][2], EXT_FIELD_TYPE.INET4) self.assertEqual(metadata["type"][2], FIELD_TYPE.STRING) self.assertEqual(metadata["ext_type_or_format"][3], EXT_FIELD_TYPE.INET6) self.assertEqual(metadata["type"][3], FIELD_TYPE.STRING) self.assertEqual(metadata["ext_type_or_format"][4], EXT_FIELD_TYPE.POINT) self.assertEqual(metadata["type"][4], FIELD_TYPE.GEOMETRY) cursor.execute("SELECT a,b,c,d,e FROM t1 WHERE 1=?", (1,)) metadata= cursor.metadata self.assertEqual(metadata["ext_type_or_format"][0], EXT_FIELD_TYPE.JSON) self.assertEqual(metadata["type"][0], FIELD_TYPE.BLOB) self.assertEqual(metadata["ext_type_or_format"][1], EXT_FIELD_TYPE.UUID) self.assertEqual(metadata["type"][1], FIELD_TYPE.STRING) self.assertEqual(metadata["ext_type_or_format"][2], EXT_FIELD_TYPE.INET4) self.assertEqual(metadata["type"][2], FIELD_TYPE.STRING) self.assertEqual(metadata["ext_type_or_format"][3], EXT_FIELD_TYPE.INET6) self.assertEqual(metadata["type"][3], FIELD_TYPE.STRING) self.assertEqual(metadata["ext_type_or_format"][4], EXT_FIELD_TYPE.POINT) self.assertEqual(metadata["type"][4], FIELD_TYPE.GEOMETRY) cursor.close() def test_xfield_types(self): cursor = self.connection.cursor() cursor.execute("SELECT a,b,c,d,e FROM t1") description= cursor.description self.assertEqual(12, len(description[0])) self.assertEqual(description[0][11], EXT_FIELD_TYPE.JSON) cursor.execute("SELECT a,b,c,d,e FROM t1 WHERE 1=?", (1,)) description= cursor.description self.assertEqual(12, len(description[0])) self.assertEqual(description[0][11], EXT_FIELD_TYPE.JSON) cursor.close() connection.close() def test_xfield_types(self): cursor = self.connection.cursor() fieldinfo = mariadb.fieldinfo() cursor.execute("CREATE TEMPORARY TABLE test_xfield_types (" "a tinyint not null auto_increment primary " "key, b smallint, c int, d bigint, e float, " "f decimal, g double, h char(10), i varchar(255), " "j blob, k json, index(b))") info = cursor.description self.assertEqual(info, None) cursor.execute("SELECT * FROM test_xfield_types") info = cursor.description self.assertEqual(fieldinfo.type(info[0]), "TINY") self.assertEqual(fieldinfo.type(info[1]), "SHORT") self.assertEqual(fieldinfo.type(info[2]), "LONG") self.assertEqual(fieldinfo.type(info[3]), "LONGLONG") self.assertEqual(fieldinfo.type(info[4]), "FLOAT") self.assertEqual(fieldinfo.type(info[5]), "NEWDECIMAL") self.assertEqual(fieldinfo.type(info[6]), "DOUBLE") self.assertEqual(fieldinfo.type(info[7]), "STRING") self.assertEqual(fieldinfo.type(info[8]), "VAR_STRING") self.assertEqual(fieldinfo.type(info[9]), "BLOB") x = self.connection.server_version_info if not is_maxscale() and (x > (10, 5, 1) or is_mysql()): self.assertEqual(fieldinfo.type(info[10]), "JSON") else: self.assertEqual(fieldinfo.type(info[10]), "BLOB") self.assertEqual(fieldinfo.flag(info[0]), "NOT_NULL | PRIMARY_KEY | AUTO_INCREMENT | NUMERIC") self.assertEqual(fieldinfo.flag(info[1]), "PART_KEY | NUMERIC") self.assertEqual(fieldinfo.flag(info[9]), "BLOB | BINARY") del cursor def test_bulk_delete(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE bulk_delete (" "id int, name varchar(64), city varchar(64))") params = [(1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")] cursor.executemany("INSERT INTO bulk_delete VALUES (?,?,?)", params) self.assertEqual(cursor.rowcount, 5) params = [(1,), (2,)] cursor.executemany("DELETE FROM bulk_delete WHERE id=?", params) self.assertEqual(cursor.rowcount, 2) del cursor def test_pyformat(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE pyformat (" "id int, name varchar(64), city varchar(64))") params = [{"id": 1, "name": u"Jack", "city": u"Boston"}, {"id": 2, "name": u"Martin", "city": u"Ohio"}, {"id": 3, "name": u"James", "city": u"Washington"}, {"id": 4, "name": u"Rasmus", "city": u"Helsinki"}, {"id": 5, "name": u"Andrey", "city": u"Sofia"}] cursor.executemany("INSERT INTO pyformat VALUES " "(%(id)s,%(name)s,%(city)s)", params) self.assertEqual(cursor.rowcount, 5) cursor.execute("commit") cursor.execute("SELECT name FROM pyformat WHERE id=5") row = cursor.fetchone() self.assertEqual(row[0], "Andrey") def test_format(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE pyformat (" "id int, name varchar(64), city varchar(64))") params = [(1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")] cursor.executemany("INSERT INTO pyformat VALUES (%s,%s,%s)", params) self.assertEqual(cursor.rowcount, 5) cursor.execute("commit") cursor.execute("SELECT name FROM pyformat WHERE id=5") row = cursor.fetchone() self.assertEqual(row[0], "Andrey") def test_conpy214(self): cursor = self.connection.cursor(named_tuple=True) cursor.execute("SELECT 1 as foo") rows = cursor.fetchall() self.assertEqual(rows[0].foo, 1) del cursor cursor = self.connection.cursor(dictionary=True) cursor.execute("SELECT 1 as foo") rows = cursor.fetchall() self.assertEqual(rows[0]["foo"], 1) del cursor def test_named_tuple(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor(named_tuple=True) cursor.execute("CREATE TEMPORARY TABLE test_named_tuple (" "id int, name varchar(64), city varchar(64))") params = [(1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")] cursor.executemany("INSERT INTO test_named_tuple VALUES (?,?,?)", params) cursor.execute("SELECT * FROM test_named_tuple ORDER BY id") row = cursor.fetchone() self.assertEqual(cursor.statement, "SELECT * FROM test_named_tuple ORDER BY id") self.assertEqual(row.id, 1) self.assertEqual(row.name, "Jack") self.assertEqual(row.city, "Boston") del cursor def test_laststatement(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor(named_tuple=1) cursor.execute("CREATE TEMPORARY TABLE test_laststatement (" "id int, name varchar(64), " "city varchar(64))") self.assertEqual(cursor.statement, "CREATE TEMPORARY TABLE test_laststatement " "(id int, name varchar(64), city varchar(64))") params = [(1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")] cursor.executemany("INSERT INTO test_laststatement VALUES (?,?,?)", params) cursor.execute("SELECT * FROM test_laststatement ORDER BY id") self.assertEqual(cursor.statement, "SELECT * FROM test_laststatement ORDER BY id") del cursor def test_multi_cursor(self): cursor = self.connection.cursor() cursor1 = self.connection.cursor(cursor_type=CURSOR.READ_ONLY) cursor2 = self.connection.cursor(cursor_type=CURSOR.READ_ONLY) cursor.execute("CREATE TEMPORARY TABLE test_multi_cursor (a int)") cursor.execute("INSERT INTO test_multi_cursor VALUES " "(1),(2),(3),(4),(5),(6),(7),(8)") del cursor cursor1.execute("SELECT a FROM test_multi_cursor ORDER BY a") cursor2.execute("SELECT a FROM test_multi_cursor ORDER BY a DESC") for i in range(0, 8): self.assertEqual(cursor1.rownumber, i) row1 = cursor1.fetchone() row2 = cursor2.fetchone() self.assertEqual(cursor1.rownumber, cursor2.rownumber) self.assertEqual(row1[0] + row2[0], 9) del cursor1 del cursor2 def test_connection_attr(self): cursor = self.connection.cursor() self.assertEqual(cursor.connection, self.connection) del cursor def test_dbapi_type(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_dbapi_type (" "a int, b varchar(20), " "c blob, d datetime, e decimal)") cursor.execute("INSERT INTO test_dbapi_type VALUES " "(1, 'foo', 'blabla', now(), 10.2)") cursor.execute("SELECT * FROM test_dbapi_type ORDER BY a") expected_typecodes = [ mariadb.NUMBER, mariadb.STRING, mariadb.BINARY, mariadb.DATETIME, mariadb.NUMBER ] cursor.fetchone() typecodes = [row[1] for row in cursor.description] self.assertEqual(expected_typecodes, typecodes) del cursor def test_tuple(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE dyncol1 (a blob)") tpl = (1, 2, 3) try: cursor.execute("INSERT INTO dyncol1 VALUES (?)", tpl) except mariadb.ProgrammingError: pass del cursor def test_indicator(self): if is_mysql(): self.skipTest("Skip (MySQL)") if self.connection.server_version < server_indicator_version: self.skipTest("Requires server version >= 10.2.6") if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE ind1 (a int, " "b int default 2,c int)") vals = [(1, 4, 3), (INDICATOR.NULL, INDICATOR.DEFAULT, 3)] cursor.executemany("INSERT INTO ind1 VALUES (?,?,?)", vals) cursor.execute("SELECT a, b, c FROM ind1") row = cursor.fetchone() self.assertEqual(row[0], 1) self.assertEqual(row[1], 4) self.assertEqual(row[2], 3) row = cursor.fetchone() self.assertEqual(row[0], None) self.assertEqual(row[1], 2) self.assertEqual(row[2], 3) def test_reset(self): cursor = self.connection.cursor() cursor.execute("SELECT 1 UNION SELECT 2") cursor.execute("SELECT 1 UNION SELECT 2") del cursor def test_fake_pickle(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_fake_pickle (a blob)") k = bytes([0x80, 0x03, 0x00, 0x2E]) cursor.execute("insert into test_fake_pickle values (?)", (k,)) cursor.execute("select * from test_fake_pickle") row = cursor.fetchone() self.assertEqual(row[0], k) del cursor def test_no_result(self): cursor = self.connection.cursor() cursor.execute("set @a:=1") try: cursor.fetchone() except mariadb.ProgrammingError: pass del cursor def test_collate(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE `test_collate` (" "`test` varchar(500) COLLATE " "utf8mb4_unicode_ci NOT NULL) ENGINE=InnoDB " "DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci") cursor.execute("SET NAMES utf8mb4") cursor.execute("SELECT * FROM `test_collate` WHERE `test` LIKE 'jj' " "COLLATE utf8mb4_unicode_ci") del cursor def test_conpy_8(self): cursor = self.connection.cursor() cursor.execute("DROP PROCEDURE IF EXISTS p1") sql = """ CREATE PROCEDURE p1() BEGIN SELECT 1 FROM DUAL UNION SELECT 0 FROM DUAL; SELECT 2 FROM DUAL; END """ cursor.execute(sql) cursor.execute("call p1()") cursor.nextset() row = cursor.fetchone() self.assertEqual(row[0], 2) del cursor def test_conpy34(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a varchar(20)," "b varchar(20))") try: cursor.execute("INSERT INTO test.t1(fname, sname) VALUES (?, ?)", (("Walker", "Percy"), ("Flannery", "O'Connor"))) except (mariadb.ProgrammingError, mariadb.NotSupportedError): pass del cursor def test_scroll(self): cursor = self.connection.cursor(buffered=True) stmt = "SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4" cursor.execute(stmt) try: cursor.scroll(0) except mariadb.ProgrammingError: pass cursor.scroll(2, mode='relative') row = cursor.fetchone() self.assertEqual(row[0], 3) cursor.scroll(-3, mode='relative') row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.scroll(1) row = cursor.fetchone() self.assertEqual(row[0], 3) try: cursor.scroll(1) except mariadb.DatabaseError: pass cursor.scroll(0, mode='absolute') row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.scroll(2, mode='absolute') row = cursor.fetchone() self.assertEqual(row[0], 3) try: cursor.scroll(-2, mode='absolute') except mariadb.ProgrammingError: pass del cursor def test_conpy_9(self): cursor = self.connection.cursor() cursor.execute( "CREATE TEMPORARY TABLE test_compy_9 (" "a varchar(20), b double(5,2), c double)") cursor.execute("INSERT INTO test_compy_9 VALUES " "('€uro', -123.34, 12345.678)") cursor.execute("SELECT a,b,c FROM test_compy_9") cursor.fetchone() d = cursor.description self.assertEqual(d[0][2], 20) # 20 code points self.assertEqual(d[0][3], 80) # 80 characters self.assertEqual(d[1][2], 6) # length=precision + 1 self.assertEqual(d[1][4], 5) # precision self.assertEqual(d[1][5], 2) # scale del cursor def test_conpy_15(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_conpy_15 (" "a int not null auto_increment primary key," "b varchar(20))") self.assertEqual(cursor.lastrowid, None) cursor.execute("INSERT INTO test_conpy_15 VALUES (null, 'foo')") self.assertEqual(cursor.lastrowid, 1) cursor.execute("SELECT LAST_INSERT_ID()") row = cursor.fetchone() self.assertEqual(row[0], 1) vals = [(3, "bar"), (4, "this")] cursor.executemany("INSERT INTO test_conpy_15 VALUES (?,?)", vals) self.assertEqual(cursor.lastrowid, 4) # Bug MDEV-16847 # cursor.execute("SELECT LAST_INSERT_ID()") # row = cursor.fetchone() # self.assertEqual(row[0], 4) # Bug MDEV-16593 # vals= [(None, "bar"), (None, "foo")] # cursor.executemany("INSERT INTO t1 VALUES (?,?)", vals) # self.assertEqual(cursor.lastrowid, 6) del cursor def test_conpy_14(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() self.assertEqual(cursor.rowcount, -1) cursor.execute( "CREATE TEMPORARY TABLE test_conpy_14 (" "a int not null auto_increment primary key, b varchar(20))") self.assertEqual(cursor.rowcount, 0) cursor.execute("INSERT INTO test_conpy_14 VALUES (null, 'foo')") self.assertEqual(cursor.rowcount, 1) vals = [(3, "bar"), (4, "this")] cursor.executemany("INSERT INTO test_conpy_14 VALUES (?,?)", vals) self.assertEqual(cursor.rowcount, 2) del cursor def test_closed(self): cursor = self.connection.cursor() cursor.close() self.assertEqual(cursor.closed, True) try: cursor.execute("set @a:=1") except mariadb.ProgrammingError: pass del cursor def test_emptycursor(self): cursor = self.connection.cursor() try: cursor.execute("") except mariadb.ProgrammingError: pass del cursor def test_iterator(self): cursor = self.connection.cursor() cursor.execute("select 1 union select 2 union select 3 " "union select 4 union select 5") for i, row in enumerate(cursor): self.assertEqual(i + 1, cursor.rownumber) self.assertEqual(i + 1, row[0]) def test_update_bulk(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_update_bulk (" "a int primary key, b int)") vals = [(i,) for i in range(1000)] cursor.executemany("INSERT INTO test_update_bulk VALUES (?, NULL)", vals) self.assertEqual(cursor.rowcount, 1000) self.connection.autocommit = False cursor.executemany("UPDATE test_update_bulk SET b=2 WHERE a=?", vals) self.connection.commit() self.assertEqual(cursor.rowcount, 1000) self.connection.autocommit = True del cursor def test_multi_execute(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_multi_execute (" "a int auto_increment primary key, b int)") self.connection.autocommit = False for i in range(1, 1000): cursor.execute("INSERT INTO test_multi_execute VALUES (?,1)", (i,)) self.connection.autocommit = True del cursor def test_conpy21(self): conn = self.connection cursor = conn.cursor() self.assertFalse(cursor.closed) conn.close() self.assertTrue(cursor.closed) del cursor, conn def test_utf8(self): # F0 9F 98 8E 😎 unicode 6 smiling face with sunglasses # F0 9F 8C B6 🌶 unicode 7 hot pepper # F0 9F 8E A4 🎤 unicode 8 no microphones # F0 9F A5 82 🥂 unicode 9 champagne glass con = create_connection() cursor = con.cursor() cursor.execute( "CREATE TEMPORARY TABLE `test_utf8` (`test` blob)") cursor.execute("INSERT INTO test_utf8 VALUES (?)", ("😎🌶🎤🥂",)) cursor.execute("SELECT * FROM test_utf8") row = cursor.fetchone() e = b"\xf0\x9f\x98\x8e\xf0\x9f\x8c\xb6\xf0\x9f\x8e\xa4\xf0\x9f\xa5\x82" self.assertEqual(row[0], e) del cursor, con def test_conpy27(self): if is_mysql(): self.skipTest("Skip (MySQL)") con = create_connection() cursor = con.cursor(prepared=True, buffered=True) cursor.execute("SELECT ?", (1,)) row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.execute("SELECT ?, ?, ?", ('foo',)) row = cursor.fetchone() self.assertEqual(row[0], 'foo') del cursor, con def test_multiple_cursor(self): cursor = self.connection.cursor() cursor2 = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_multiple_cursor(" "col1 int, col2 varchar(100))") cursor.execute("INSERT INTO test_multiple_cursor " "VALUES (1, 'val1'), (2, 'val2')") cursor.execute("SELECT * FROM test_multiple_cursor LIMIT 1") cursor.fetchone() self.assertEqual(None, cursor.fetchone()) cursor2.execute("SELECT * FROM test_multiple_cursor LIMIT 1") cursor2.fetchone() del cursor, cursor2 def test_inaccurate_rownumber(self): cursor = self.connection.cursor(buffered=True) self.assertEqual(cursor.rownumber, None) stmt = "SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4" cursor.execute(stmt) self.assertEqual(cursor.rownumber, 0) cursor.scroll(2, mode='absolute') self.assertEqual(cursor.rownumber, 2) cursor.fetchone() self.assertEqual(cursor.rownumber, 3) cursor.execute("DO 1") self.assertEqual(cursor.rownumber, None) cursor.execute("DO ?", (2,)) self.assertEqual(cursor.rownumber, None) cursor.execute("SELECT 1") self.assertEqual(cursor.rownumber, 0) cursor.fetchone() self.assertEqual(cursor.rownumber, 1) cursor.fetchone() self.assertEqual(cursor.rownumber, 1) cursor.execute("SELECT ?", (1,)) self.assertEqual(cursor.rownumber, 0) cursor.fetchone() self.assertEqual(cursor.rownumber, 1) cursor.fetchone() self.assertEqual(cursor.rownumber, 1) del cursor def test_sp1(self): con = create_connection() cursor = con.cursor() cursor.execute("DROP PROCEDURE IF EXISTS p1") cursor.execute("CREATE PROCEDURE p1( )\nBEGIN\n SELECT 1;\nEND") cursor.callproc("p1") row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.execute("DROP PROCEDURE IF EXISTS p1") def test_sp2(self): con = create_connection() if con.server_version < 100301: self.skipTest("Not supported in versions < 10.3") cursor = con.cursor() cursor.execute("DROP PROCEDURE IF EXISTS p2") cursor.execute("CREATE PROCEDURE p2(IN s1 VARCHAR(20)," "IN s2 VARCHAR(20), OUT o1 VARCHAR(40) )\n" "BEGIN\n" "SET o1:=CAST(CONCAT(s1,s2) AS char " "CHARACTER SET utf8mb4);\nEND") cursor.callproc("p2", ("foo", "bar", 1)) self.assertEqual(cursor.sp_outparams, True) row = cursor.fetchone() self.assertEqual(row[0], "foobar") cursor.nextset() del cursor cursor = con.cursor() cursor.execute("CALL p2(?,?,?)", ("foo", "bar", 0)) self.assertEqual(cursor.sp_outparams, True) row = cursor.fetchone() self.assertEqual(row[0], "foobar") cursor.execute("DROP PROCEDURE IF EXISTS p2") del cursor, con def test_sp3(self): con = create_connection() if con.server_version < 100301: self.skipTest("Not supported in versions < 10.3") cursor = con.cursor() cursor.execute("DROP PROCEDURE IF EXISTS p3") cursor.execute("CREATE PROCEDURE p3(IN s1 VARCHAR(20)," "IN s2 VARCHAR(20), OUT o1 VARCHAR(40) )\n" "BEGIN\n" "SELECT '1';\n" "SET o1:=CAST(CONCAT(s1,s2) " "AS char CHARACTER SET utf8mb4);\n" "END") cursor.callproc("p3", ("foo", "bar", 1)) self.assertEqual(cursor.sp_outparams, False) row = cursor.fetchone() self.assertEqual(row[0], "1") cursor.nextset() self.assertEqual(cursor.sp_outparams, True) row = cursor.fetchone() self.assertEqual(row[0], "foobar") cursor.execute("DROP PROCEDURE IF EXISTS p3") del cursor, con def test_conpy42(self): if is_mysql(): self.skipTest("Skip (MySQL)") con = create_connection() cursor = con.cursor() cursor.execute("CREATE TEMPORARY TABLE conpy42(a GEOMETRY)") cursor.execute("INSERT INTO conpy42 VALUES " "(PointFromText('point(1 1)'))") cursor.execute("SELECT a FROM conpy42") row = cursor.fetchone() expected = b'' . join([b'\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00', b'\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00', b'\x00\xf0?']) self.assertEqual(row[0], expected) del cursor def test_conpy35(self): con = create_connection() cursor = con.cursor() cursor.execute("CREATE TEMPORARY table sample (" "id BIGINT AUTO_INCREMENT PRIMARY KEY," "name VARCHAR(64))") for name in ('foo', 'bar', 'baz'): cursor.execute("INSERT INTO sample SET name = ?", (name,)) self.assertEqual(cursor.lastrowid, 3) cursor = con.cursor(cursor_type=CURSOR.READ_ONLY) cursor.execute("SELECT * FROM sample ORDER BY id") i = 0 for row in cursor: i = i + 1 self.assertEqual(row[0], i) del cursor def test_conpy45(self): con = create_connection() cursor = con.cursor() cursor.execute("CREATE TEMPORARY table t1 (a time(3), b datetime(2))") cursor.execute("INSERT INTO t1 VALUES ('13:12:24.05111', " "'2020-10-10 14:12:24.123456')") cursor.execute("SELECT a,b FROM t1") row = cursor.fetchone() self.assertEqual(row[0], datetime.timedelta(seconds=47544, microseconds=51000)) self.assertEqual(row[1], datetime.datetime(2020, 10, 10, 14, 12, 24, 120000)) del cursor del con def test_conpy46(self): con = create_connection() with con.cursor() as cursor: cursor.execute("SELECT 'foo'") row = cursor.fetchone() self.assertEqual(row[0], "foo") try: cursor.execute("SELECT 'bar'") except mariadb.ProgrammingError: pass del con def test_conpy47(self): con = create_connection() cursor = con.cursor(buffered=True) cursor.execute("SELECT ?", (True, )) row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.execute("SELECT ?", (False,)) row = cursor.fetchone() self.assertEqual(row[0], 0) del con def test_conpy48(self): con = create_connection() cur = con.cursor() cur.execute("select %s", [True]) row = cur.fetchone() self.assertEqual(row[0], 1) cur.execute("create temporary table t1 (a int)") cur.executemany("insert into t1 values (%s)", [[1], (2,)]) cur.execute("select a from t1") row = cur.fetchone() self.assertEqual(row[0], 1) row = cur.fetchone() self.assertEqual(row[0], 2) del con def test_conpy51(self): con = create_connection() cur = con.cursor(buffered=True) cur.execute('create temporary table temp (a int unsigned)') cur.execute('insert into temp values (1), (2), (3)') cur.execute('select a from temp order by a') con.commit() row = cur.fetchall() self.assertEqual(row[0][0], 1) self.assertEqual(row[1][0], 2) self.assertEqual(row[2][0], 3) del con def test_conpy52(self): con = create_connection() cur = con.cursor(buffered=True) cur.execute('create temporary table temp (a int unsigned)') cur.execute('insert into temp values (1), (2), (3)') cur.execute('select a from temp order by a') con.commit() row = cur.fetchall() self.assertEqual(row[0][0], 1) self.assertEqual(row[1][0], 2) self.assertEqual(row[2][0], 3) cur.execute('select a from temp where a > ?', (0,)) con.commit() row = cur.fetchall() self.assertEqual(row[0][0], 1) self.assertEqual(row[1][0], 2) self.assertEqual(row[2][0], 3) cur.execute("drop table if exists temp") del con def test_conpy49(self): con = create_connection() cur = con.cursor() cur.execute("create temporary table t1 (a decimal(10,2))") cur.execute("insert into t1 values (?)", (Decimal('10.2'),)) cur.execute("select a from t1") row = cur.fetchone() self.assertEqual(row[0], Decimal('10.20')) del con def test_conpy56(self): con = create_connection() cur = con.cursor(dictionary=True) cur.execute("select 'foo' as bar, 'bar' as foo") row = cur.fetchone() self.assertEqual(row["foo"], "bar") self.assertEqual(row["bar"], "foo") del con def test_conpy53(self): con = create_connection() cur = con.cursor() cur.execute("select 1", ()) row = cur.fetchone() self.assertEqual(row[0], 1) cur.execute("select 1", []) row = cur.fetchone() self.assertEqual(row[0], 1) del con def test_conpy58(self): con = create_connection() cursor = con.cursor() cursor.execute("SELECT %(val)s", {"val": 3}) row = cursor.fetchone() self.assertEqual(row[0], 3) cursor.execute("CREATE TEMPORARY TABLE t1 (a int)") cursor.executemany("INSERT INTO t1 VALUES (%(val)s)", [{"val": 1}, {"val": 2}]) cursor.execute("SELECT a FROM t1 ORDER by a") row = cursor.fetchall() self.assertEqual(row[0][0], 1) self.assertEqual(row[1][0], 2) del con def test_conpy59(self): con = create_connection() cursor = con.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a date)") cursor.execute("INSERT INTO t1 VALUES('0000-01-01')") cursor.execute("SELECT a FROM t1") row = cursor.fetchone() self.assertEqual(row[0], None) del con def test_conpy61(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") if is_mysql(): self.skipTest("Skip (MySQL)") con = create_connection() if self.connection.server_version < server_indicator_version: self.skipTest("Requires server version >= 10.2.6") cursor = con.cursor() cursor.execute("CREATE TEMPORARY TABLE ind1 " "(a int, b int default 2,c int)") vals = [(1, 4, 3), (None, 2, 3)] cursor.executemany("INSERT INTO ind1 VALUES (?,?,?)", vals) cursor.execute("SELECT a, b, c FROM ind1") row = cursor.fetchone() self.assertEqual(row[0], 1) row = cursor.fetchone() self.assertEqual(row[0], None) cursor.execute("DELETE FROM ind1") vals = [(1, 4, 3), (INDICATOR.NULL, INDICATOR.DEFAULT, None)] cursor.executemany("INSERT INTO ind1 VALUES (?,?,?)", vals) cursor.execute("SELECT a, b, c FROM ind1") row = cursor.fetchone() self.assertEqual(row[0], 1) row = cursor.fetchone() self.assertEqual(row[0], None) self.assertEqual(row[1], 2) self.assertEqual(row[2], None) del cursor def test_conpy62(self): con = create_connection() cur = con.cursor() con = create_connection() query = "select round(.75 * (? / 3), 2) as val" cur.execute(query, [5]) row = cur.fetchone() self.assertEqual(row[0], Decimal(1.25)) def test_conpy67(self): con = create_connection() cur = con.cursor(buffered=False) cur.execute("SELECT 1") self.assertEqual(cur.rowcount, 0) cur = con.cursor() cur.execute("SELECT 1", buffered=False) self.assertEqual(cur.rowcount, 0) cur.close() cur = con.cursor() cur.execute("CREATE TEMPORARY TABLE test_conpy67 (a int)") cur.execute("SELECT * from test_conpy67") self.assertEqual(cur.rowcount, 0) cur.fetchall() self.assertEqual(cur.rowcount, 0) def test_negative_numbers(self): con = create_connection() cur = con.cursor() cur.execute("drop table if exists t1") cur.execute("create table t1(a tinyint, b int, c bigint)") cur.execute("insert into t1 values (?,?,?)", (-1, -300, -2147483649)) cur.execute("select a, b, c FROM t1") row = cur.fetchone() self.assertEqual(row[0], -1) self.assertEqual(row[1], -300) self.assertEqual(row[2], -2147483649) del cur con.close() def test_none_val(self): con = create_connection() cur = con.cursor() cur.execute("CREATE TEMPORARY TABLE t1 (a int)") vals = [(1,), (2,), (4,), (None,), (3,)] cur.executemany("INSERT INTO t1 VALUES (?)", vals) cur.execute("select a from t1 order by a") rows = cur.fetchall() self.assertEqual(rows[0][0], None) del cur def test_conpy81(self): con = create_connection() cur = con.cursor() cur.execute("CREATE TEMPORARY TABLE t1 (a int)") cur.execute("INSERT INTO t1 VALUES(1)") cur.execute("SELECT a FROM t1") row = cur.fetchone() self.assertEqual(row[0], 1) cur.execute("SELECT a FROM t1 WHERE 1=?", (1,)) row = cur.fetchone() self.assertEqual(row[0], 1) del cur def test_conpy94(self): con = create_connection() cur = con.cursor() a = foo(2) cur.execute("SELECT ?", (a,)) row = cur.fetchone() self.assertEqual(row[0], 2) del cur def test_conpy98(self): con = create_connection() cursor = con.cursor() cursor.execute("SELECT CAST('foo' AS BINARY) AS anon_1") row = cursor.fetchone() self.assertEqual(row[0], b'foo') del cursor def test_conpy68(self): con = create_connection() if con.server_version < 100207: self.skipTest("Not supported in versions < 10.2.7") cursor = con.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a JSON)") content = {'a': 'aaa', 'b': 'bbb', 'c': 123} cursor.execute("INSERT INTO t1 VALUES(?)", (json.dumps(content),)) cursor.execute("SELECT a FROM t1") row = cursor.fetchone() self.assertEqual(row[0], json.dumps(content)) del cursor def test_conpy123(self): con = create_connection({"client_flag": CLIENT.MULTI_STATEMENTS}) cursor1 = con.cursor() cursor1.execute("SELECT 1; SELECT 2") cursor1.close() cursor2 = con.cursor() cursor2.execute("SELECT 1") row = cursor2.fetchone() self.assertEqual(row[0], 1) cursor2.close() con.close() def test_conpy103(self): con = create_connection() cursor = con.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a decimal(10,2))") cursor.executemany("INSERT INTO t1 VALUES (?)", [[decimal.Decimal(1)]]) cursor.execute("SELECT a FROM t1") row = cursor.fetchone() self.assertEqual(row[0], decimal.Decimal(1)) def test_conpy129(self): conn = create_connection() server_version = conn.server_version major = int(server_version / 10000) minor = int((server_version % 10000) / 100) patch = server_version % 100 self.assertEqual(conn.server_version_info, (major, minor, patch)) self.assertEqual(conn.get_server_version(), (major, minor, patch)) def test_conpy167(self): conn = create_connection() cursor = conn.cursor() cursor.execute("CREATE TEMPORARY table t1 (" "a int not NULL auto_increment primary key, b int)") cursor.execute("INSERT INTO t1 VALUES (NULL, ?)", (1, )) self.assertEqual(cursor.rowcount, 1) cursor.executemany("INSERT INTO t1 VALUES (NULL, ?)", [(2, ), (3,)]) self.assertEqual(cursor.rowcount, 2) del cursor def test_conpy168(self): conn = create_connection() cursor = conn.cursor() x = os.urandom(32) cursor.execute("SELECT cast(? as binary)", (x,)) row = cursor.fetchone() self.assertEqual(row[0], x) del cursor def test_conpy133(self): if is_mysql(): self.skipTest("Skip (MySQL)") conn = create_connection() cursor = conn.cursor() cursor.execute("SELECT /*! ? */", (1,)) row = cursor.fetchone() self.assertEqual(row[0], 1) del cursor cursor = conn.cursor() cursor.execute("SELECT /*M! ? */", (1,)) row = cursor.fetchone() self.assertEqual(row[0], 1) del cursor cursor = conn.cursor() cursor.execute("SELECT /*M!50601 ? */", (1,)) row = cursor.fetchone() self.assertEqual(row[0], 1) del cursor cursor = conn.cursor() cursor.execute("SELECT /*!40301 ? */", (1,)) row = cursor.fetchone() self.assertEqual(row[0], 1) del cursor cursor = conn.cursor() try: cursor.execute("SELECT /*!50701 ? */", (1,)) except mariadb.ProgrammingError: pass del cursor cursor = conn.cursor() try: cursor.execute("SELECT /*!250701 ? */", (1,)) except mariadb.ProgrammingError: pass del cursor def check_closed(self): conn = create_connection() cursor1 = conn.cursor() cursor2 = conn.cursor() cursor1.close() try: cursor1.execute("select 1") except (mariadb.ProgrammingError): pass del cursor1 conn.close() try: cursor2.execute("select 1") except (mariadb.ProgrammingError): pass del cursor2, conn def test_conpy194(self): if is_mysql(): self.skipTest("Skip (MySQL)") if (self.connection.server_version < 105000): self.skipTest("Insert returning requires MariaDB >= 10.5") conn = create_connection() cursor = conn.cursor() cursor.execute("create temporary table t1 " "(a int not null auto_increment primary key," "b varchar(10))") data = [(1, ), (2, ), (3, )] cursor.executemany("insert into t1 values (?, 'foo') returning a", data) rows = cursor.fetchall() self.assertEqual(rows, data) cursor.executemany("delete from t1 where a=? returning a", data) rows = cursor.fetchall() self.assertEqual(rows, data) cursor.execute("select a from t1") rows = cursor.fetchall() self.assertEqual(rows, []) data = [(1, "foo"), (2, "bar"), (3, "hello")] cursor.executemany("insert into t1 values (?,?) returning a,b", data) rows = cursor.fetchall() self.assertEqual(rows, data) cursor.executemany("replace into t1 values (?,?) returning a,b", [(1, "xyz")]) rows = cursor.fetchall() self.assertEqual(rows, [(1, "xyz")]) del cursor, conn def test_conpy178(self): conn = create_connection() cursor = conn.cursor() cursor.execute("DROP PROCEDURE IF EXISTS p2") cursor.execute("CREATE PROCEDURE p2(IN s1 VARCHAR(20), " "IN s2 VARCHAR(20), OUT o1 VARCHAR(40) )\n" "BEGIN\nSET o1:=CAST(CONCAT(s1,s2) AS " "char CHARACTER SET utf8mb4);\nEND") for i in range(0, 500): cursor.callproc("p2", ("foo", "bar", 1)) row = cursor.fetchone() self.assertEqual(row[0], b"foobar" if is_mysql() else "foobar") conn.close() def test_conpy205(self): conn = create_connection() cursor = conn.cursor() cursor.execute("select %(name)s", {"name": "Marc"}) row = cursor.fetchone() self.assertEqual(row[0], "Marc") cursor.execute("select %(name)s", {"name": "Marc", "noname": "unknown"}) row = cursor.fetchone() self.assertEqual(row[0], "Marc") try: cursor.execute("select ?", {"noname": "unknown"}) except (mariadb.ProgrammingError): pass try: cursor.execute("select %(name)s", (1,)) except (mariadb.ProgrammingError): pass try: cursor.execute("select %(name)s", {"noname": "unknown"}) except (mariadb.ProgrammingError): pass try: cursor.execute("select ?") except (mariadb.ProgrammingError): pass try: cursor.execute("select ?,?,?", (1, 2)) except (mariadb.ProgrammingError): pass try: cursor.execute("select ?,?,?", (1, 2, 3, 4)) except (mariadb.ProgrammingError): pass cursor.close() def test_conpy203(self): conn = create_connection() cursor = conn.cursor() try: cursor.execute("SELECT") except mariadb.ProgrammingError as err: self.assertEqual(err.errno, ERR.ER_PARSE_ERROR) def test_unicode_parsing(self): conn = create_connection() cursor = conn.cursor() cursor.execute("create temporary table Unitéble2 ( 測試 int, méil int)") cursor.execute("insert into Unitéble2 values (%(測試)s, %(méil)s)", {"測試": 1, "méil": 2}) self.assertEqual(cursor.rowcount, 1) cursor.execute("SELECT `Unitéble2`.`測試` AS `Unitéble2_測試`," " `Unitéble2`.`méil` AS `Unitéble2_méil` FROM " "`Unitéble2` WHERE ? = `Unitéble2`.`測試`", (1, )) cursor.fetchall() self.assertEqual(cursor.rowcount, 1) del cursor def test_conpy209(self): conn = create_connection() cursor = conn.cursor() data = ("col_Unitéble_id_seq", "foobar") sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE "\ "TABLE_TYPE='SEQUENCE' and TABLE_NAME=? and TABLE_SCHEMA=?" transformed = b"" . join([b'SELECT TABLE_NAME FROM ', b'INFORMATION_SCHEMA.TABLES ', b'WHERE TABLE_TYPE=\'SEQUENCE\'', b' and TABLE_NAME=', b'\'col_Unit\xc3\xa9ble_id_seq\'', b' and TABLE_SCHEMA=\'foobar\'']) cursor.execute(sql, data) self.assertEqual(transformed, cursor._transformed_statement) del cursor def test_conpy277(self): conn = create_connection() cursor = conn.cursor() cursor.execute("SET session sql_mode='TRADITIONAL,ANSI_QUOTES,ONLY_FULL_GROUP_BY,PIPES_AS_CONCAT'") cursor.execute('select ? as x', ('hi',)) row= cursor.fetchone() self.assertEqual(row[0], 'hi') cursor.close() conn.close() def test_conpy213(self): conversions = {**{FIELD_TYPE.NEWDECIMAL: float}} connection = create_connection({"converter": conversions}) cursor = connection.cursor() cursor.execute("SELECT 1.1") rows = cursor.fetchall() self.assertEqual(rows[0][0], 1.1) cursor.execute("SELECT 1.1") row = cursor.fetchone() self.assertEqual(row[0], 1.1) del cursor del connection def test_conpy218(self): conn = create_connection() cursor = conn.cursor() cursor.execute("SELECT 1", None) row = cursor.fetchone() self.assertEqual(row[0], 1) cursor.execute("SELECT 2", ()) row = cursor.fetchone() self.assertEqual(row[0], 2) cursor.execute("SELECT 3", []) row = cursor.fetchone() self.assertEqual(row[0], 3) cursor.execute("SELECT 4", {}) row = cursor.fetchone() self.assertEqual(row[0], 4) del cursor del conn def test_conpy222(self): conn = create_connection() cursor = conn.cursor() cursor.close() del cursor cursor = conn.cursor() del cursor try: cursor.close() # noqa: F821 except Exception: pass def test_conpy_224(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_inserttuple (" "id int, name varchar(64), " "city varchar(64))") params = ((1, u"Jack", u"Boston"), (2, u"Martin", u"Ohio"), (3, u"James", u"Washington"), (4, u"Rasmus", u"Helsinki"), (5, u"Andrey", u"Sofia")) cursor.executemany("INSERT INTO test_inserttuple VALUES (?,?,?)", params) cursor.executemany("INSERT INTO test_inserttuple VALUES (?,?,?)", params) cursor = self.connection.cursor() cursor.execute("SELECT name FROM test_inserttuple ORDER BY id DESC") row = cursor.fetchone() self.assertEqual("Andrey", row[0]) del cursor def test_conpy225(self): conn = create_connection() cursor = conn.cursor() cursor.execute("CREATE TEMPORARY TABLE x01 (a int, b int)") params = ((1, 2), (2, 3), (3, 4), (4, 5)) cursor.executemany("INSERT INTO x01 VALUES (?,?)", params) self.assertEqual(cursor.rowcount, 4) if (not is_mysql()): self.assertEqual(cursor.affected_rows, 4) cursor.execute("UPDATE x01 SET a=1 WHERE a=1") self.assertEqual(cursor.rowcount, 0) self.assertEqual(cursor.affected_rows, 0) cursor.execute("UPDATE x01 SET a=1 WHERE a=4") self.assertEqual(cursor.affected_rows, 1) self.assertEqual(cursor.rowcount, 1) def test_conpy270(self): connection = create_connection() x = connection.server_version_info if x < (10, 7, 0) or is_mysql(): self.skipTest("Skip (MySQL and MariaDB < 10.7)") cursor = connection.cursor() cursor.execute("drop table if exists t1") cursor.execute("create table t1 (a uuid)") cursor.execute("insert into t1 values (uuid())") # text protocol cursor.execute("select a from t1") self.assertEqual(cursor.description[0][1], mariadb.STRING); cursor.fetchall() # binary protcol cursor.execute("select a from t1 WHERE 1=?", (1,)) self.assertEqual(cursor.description[0][1], mariadb.STRING); cursor.fetchall() cursor.close() connection.close() def test_conpy269(self): if is_mysql(): self.skipTest("Skip (MySQL)") connection = create_connection() cursor = connection.cursor() cursor.execute("SELECT 1 UNION SELECT 2") self.assertEqual(cursor.rowcount, 2) cursor.close() self.assertEqual(cursor.rowcount, -1) connection.close() def test_conpy258(self): connection = create_connection() cursor = connection.cursor() cursor.execute("CREATE TEMPORARY TABLE t1 (a INT(9) ZEROFILL)") cursor.execute("INSERT INTO t1 VALUES(123)") cursor.execute("SELECT a FROM t1") row = cursor.fetchone() self.assertEqual(row[0], 123) cursor.close() cursor = connection.cursor(binary=True) cursor.execute("SELECT a FROM t1") row = cursor.fetchone() self.assertEqual(row[0], 123) cursor.close() connection.close() def test_conpy276(self): connection = create_connection() cursor = connection.cursor() cursor.execute("SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4") connection.close() row= cursor.fetchone() self.assertEqual(row[0], 1) rows= cursor.fetchall() self.assertEqual(rows, [(2,),(3,),(4,)]) cursor._seek(0) row= cursor.fetchone() self.assertEqual(row[0], 1) self.assertEqual(cursor.rowcount, 4) cursor.close() def test_conpy91(self): with create_connection() as connection: with connection.cursor() as cursor: for parameter_type in (int, decimal.Decimal): with self.subTest(parameter_type=parameter_type): with self.subTest(parameter_count=1): with self.subTest(parameter_style='?'): cursor.execute('select ?', [parameter_type(1)]) [[value]] = cursor.fetchall() self.assertEqual(value, 1) with self.subTest(parameter_style='%s'): cursor.execute('select %s', [parameter_type(1)]) [[value]] = cursor.fetchall() self.assertEqual(value, 1) with self.subTest(parameter_style='%(name)s'): cursor.execute('select %(value)s', dict(value=parameter_type(1))) [[value]] = cursor.fetchall() self.assertEqual(value, 1) with self.subTest(parameter_count=2): with self.subTest(parameter_style='?'): cursor.execute('select ?, ?', [parameter_type(1), 1]) [[value, _]] = cursor.fetchall() self.assertEqual(value, 1) with self.subTest(parameter_style='%s'): cursor.execute('select %s, %s', [parameter_type(1), 1]) [[value, _]] = cursor.fetchall() self.assertEqual(value, 1) with self.subTest(parameter_style='%(name)s'): cursor.execute('select %(value)s, %(dummy)s', dict(value=parameter_type(1), dummy=1)) [[value, _]] = cursor.fetchall() self.assertEqual(value, 1) if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_cursor_mariadb.py000066400000000000000000000072641456007477700301160ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import datetime import unittest from test.base_test import create_connection class CursorMariaDBTest(unittest.TestCase): def setUp(self): self.connection = create_connection() def tearDown(self): del self.connection def test_insert_parameter(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_insert_parameter(" "a int not null auto_increment primary key," "b int, c int, d varchar(20),e date)") list_in = [] for i in range(1, 300001): row = (i, i, i, "bar", datetime.date(2019, 1, 1)) list_in.append(row) cursor.executemany("INSERT INTO test_insert_parameter VALUES " "(?,?,?,?,?)", list_in) self.assertEqual(len(list_in), cursor.rowcount) self.connection.commit() cursor.execute("SELECT * FROM test_insert_parameter order by a") list_out = cursor.fetchall() self.assertEqual(len(list_in), cursor.rowcount) self.assertEqual(list_in, list_out) cursor.close() def test_update_parameter(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_update_parameter(" "a int not null auto_increment primary key," "b int, c int, d varchar(20),e date)") cursor.execute("set @@autocommit=0") list_in = [] for i in range(1, 300001): row = (i, i, i, "bar", datetime.date(2019, 1, 1)) list_in.append(row) cursor.executemany("INSERT INTO test_update_parameter VALUES " "(?,?,?,?,?)", list_in) self.assertEqual(len(list_in), cursor.rowcount) self.connection.commit() cursor.close() list_update = [] cursor = self.connection.cursor() cursor.execute("set @@autocommit=0") for i in range(1, 300001): row = (i + 1, i) list_update.append(row) cursor.executemany("UPDATE test_update_parameter SET b=? " "WHERE a=?", list_update) self.assertEqual(cursor.rowcount, 300000) self.connection.commit() cursor.close() def test_delete_parameter(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_delete_parameter(" "a int not null auto_increment primary key," "b int, c int, d varchar(20),e date)") cursor.execute("set @@autocommit=0") list_in = [] for i in range(1, 300001): row = (i, i, i, "bar", datetime.date(2019, 1, 1)) list_in.append(row) cursor.executemany("INSERT INTO test_delete_parameter VALUES " "(?,?,?,?,?)", list_in) self.assertEqual(len(list_in), cursor.rowcount) self.connection.commit() cursor.close() list_delete = [] cursor = self.connection.cursor() cursor.execute("set @@autocommit=0") for i in range(1, 300001): list_delete.append((i,)) cursor.executemany("DELETE FROM test_delete_parameter WHERE " "a=?", list_delete) self.assertEqual(cursor.rowcount, 300000) self.connection.commit() cursor.close() if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_cursor_mysql.py000066400000000000000000000023171456007477700276560ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import datetime import unittest from test.base_test import create_connection, is_maxscale class CursorMySQLTest(unittest.TestCase): def setUp(self): self.connection = create_connection() def tearDown(self): del self.connection def test_parameter(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_parameter(" "a int auto_increment primary key not " "null, b int, c int, d varchar(20),e date)") cursor.execute("SET @@autocommit=0") list_in = [] for i in range(1, 30000): row = (i, i, i, "bar", datetime.date(2019, 1, 1)) list_in.append(row) cursor.executemany("INSERT INTO test_parameter VALUES " "(%s,%s,%s,%s,%s)", list_in) self.connection.commit() cursor.execute("SELECT * FROM test_parameter order by a") list_out = cursor.fetchall() self.assertEqual(list_in, list_out) cursor.close() if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_dbapi20.py000066400000000000000000000765071456007477700263510ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- ''' Python DB API 2.0 driver compliance unit test suite. This software is Public Domain and may be used without restrictions. "Now we have booze and barflies entering the discussion, plus rumours of DBAs on drugs... and I won't tell you what flashes through my mind each time I read the subject line with 'Anal Compliance' in it. All around this is turning out to be a thoroughly unwholesome unit test." -- Ian Bicking ''' __rcs_id__ = '$Id$' __version__ = '$Revision$'[11:-2] __author__ = 'Stuart Bishop ' import time import unittest import mariadb import datetime # $Log$ # Revision 1.1.2.1 2006/02/25 03:44:32 adustman # Generic DB-API unit test module # # Revision 1.10 2003/10/09 03:14:14 zenzen # Add test for DB API 2.0 optional extension, where database exceptions # are exposed as attributes on the Connection object. # # Revision 1.9 2003/08/13 01:16:36 zenzen # Minor tweak from Stefan Fleiter # # Revision 1.8 2003/04/10 00:13:25 zenzen # Changes, as per suggestions by M.-A. Lemburg # - Add a table prefix, to ensure namespace collisions can always be avoided # # Revision 1.7 2003/02/26 23:33:37 zenzen # Break out DDL into helper functions, as per request by David Rushby # # Revision 1.6 2003/02/21 03:04:33 zenzen # Stuff from Henrik Ekelund: # added test_None # added test_nextset & hooks # # Revision 1.5 2003/02/17 22:08:43 zenzen # Implement suggestions and code from Henrik Eklund - test that # cursor.arraysize # defaults to 1 & generic cursor.callproc test added # # Revision 1.4 2003/02/15 00:16:33 zenzen # Changes, as per suggestions and bug reports by M.-A. Lemburg, # Matthew T. Kromer, Federico Di Gregorio and Daniel Dittmar # - Class renamed # - Now a subclass of TestCase, to avoid requiring the driver stub # to use multiple inheritance # - Reversed the polarity of buggy test in test_description # - Test exception hierarchy correctly # - self.populate is now self._populate(), so if a driver stub # overrides self.ddl1 this change propagates # - VARCHAR columns now have a width, which will hopefully make the # DDL even more portable (this will be reversed if it causes more problems) # - cursor.rowcount being checked after various execute and fetchXXX methods # - Check for fetchall and fetchmany returning empty lists after results # are exhausted (already checking for empty lists if select retrieved # nothing # - Fix bugs in test_setoutputsize_basic and test_setinputsizes # from test.conf_test import conf from test.base_test import is_maxscale class DatabaseAPI20Test(unittest.TestCase): ''' Test a database self.driver for DB API 2.0 compatibility. This implementation tests Gadfly, but the TestCase is structured so that other self.drivers can subclass this test case to ensure compliance with the DB-API. It is expected that this TestCase may be expanded in the future if ambiguities or edge conditions are discovered. The 'Optional Extensions' are not yet being tested. self.drivers should subclass this test, overriding setUp, tearDown, self.driver, connect_args and connect_kw_args. Class specification should be as follows: import dbapi20 class mytest(dbapi20.DatabaseAPI20Test): [...] Don't 'import DatabaseAPI20Test from dbapi20', or you will confuse the unit tester - just 'import dbapi20'. ''' # The self.driver module. This should be the module where the 'connect' # method is to be found driver = mariadb connect_args = () # List of arguments to pass to connect connect_kw_args = conf() # Keyword arguments for connect table_prefix = 'dbapi20test_' # If you need to specify a prefix for tables ddl1 = 'create table %sbooze (name varchar(20))' % table_prefix ddl2 = 'create table %sbarflys (name varchar(20))' % table_prefix xddl1 = 'drop table %sbooze' % table_prefix xddl2 = 'drop table %sbarflys' % table_prefix # Name of stored procedure to convert string->lowercase lowerfunc = 'lower' # Some drivers may need to override these helpers, for example adding # a 'commit' after the execute. def executeDDL1(self, cursor): cursor.execute(self.ddl1) def executeDDL2(self, cursor): cursor.execute(self.ddl2) def setUp(self): ''' self.drivers should override this method to perform required setup if any is necessary, such as creating the database. ''' pass def tearDown(self): ''' self.drivers should override this method to perform required cleanup if any is necessary, such as deleting the test database. The default drops the tables that may be created. ''' con = self._connect() try: cur = con.cursor() for ddl in (self.xddl1, self.xddl2): try: cur.execute(ddl) con.commit() except self.driver.Error: # Assume table didn't exist. Other tests will check if # execute is busted. pass finally: cur.close() con.close() def _connect(self): try: return self.driver.connect(**self.connect_kw_args) except AttributeError: self.fail("No connect method found in self.driver module") def test_connect(self): con = self._connect() con.close() def test_apilevel(self): try: # Must exist apilevel = self.driver.apilevel # Must equal 2.0 self.assertEqual(apilevel, '2.0') except AttributeError: self.fail("Driver doesn't define apilevel") def test_threadsafety(self): try: # Must exist threadsafety = self.driver.threadsafety # Must be a valid value self.assertTrue(threadsafety in (0, 1, 2, 3)) except AttributeError: self.fail("Driver doesn't define threadsafety") def test_paramstyle(self): try: # Must exist paramstyle = self.driver.paramstyle # Must be a valid value self.assertTrue(paramstyle in ( 'qmark', 'numeric', 'named', 'format', 'pyformat' )) except AttributeError: self.fail("Driver doesn't define paramstyle") def test_Exceptions(self): # Make sure required exceptions exist, and are in the # defined hierarchy. self.assertTrue(issubclass(self.driver.Warning, Exception)) self.assertTrue(issubclass(self.driver.Error, Exception)) self.assertTrue( issubclass(self.driver.InterfaceError, self.driver.Error) ) self.assertTrue( issubclass(self.driver.DatabaseError, self.driver.Error) ) self.assertTrue( issubclass(self.driver.OperationalError, self.driver.Error) ) self.assertTrue( issubclass(self.driver.IntegrityError, self.driver.Error) ) self.assertTrue( issubclass(self.driver.InternalError, self.driver.Error) ) self.assertTrue( issubclass(self.driver.ProgrammingError, self.driver.Error) ) self.assertTrue( issubclass(self.driver.NotSupportedError, self.driver.Error) ) def test_ExceptionsAsConnectionAttributes(self): # OPTIONAL EXTENSION # Test for the optional DB API 2.0 extension, where the exceptions # are exposed as attributes on the Connection object # I figure this optional extension will be implemented by any # driver author who is using this test suite, so it is enabled # by default. con = self._connect() drv = self.driver self.assertTrue(con.Warning is drv.Warning) self.assertTrue(con.Error is drv.Error) self.assertTrue(con.InterfaceError is drv.InterfaceError) self.assertTrue(con.DatabaseError is drv.DatabaseError) self.assertTrue(con.OperationalError is drv.OperationalError) self.assertTrue(con.IntegrityError is drv.IntegrityError) self.assertTrue(con.InternalError is drv.InternalError) self.assertTrue(con.ProgrammingError is drv.ProgrammingError) self.assertTrue(con.NotSupportedError is drv.NotSupportedError) def test_commit(self): con = self._connect() try: # Commit must work, even if it doesn't do anything con.commit() finally: con.close() def test_rollback(self): con = self._connect() # If rollback is defined, it should either work or throw # the documented exception if hasattr(con, 'rollback'): try: con.rollback() except self.driver.NotSupportedError: pass def test_cursor(self): con = self._connect() try: cur = con.cursor() finally: cur.close() con.close() def test_cursor_isolation(self): con = self._connect() try: # Make sure cursors created from the same connection have # the documented transaction isolation level cur1 = con.cursor() cur2 = con.cursor() self.executeDDL1(cur1) cur1.execute("insert into %sbooze values ('Victoria Bitter')" % ( self.table_prefix )) cur2.execute("select name from %sbooze" % self.table_prefix) booze = cur2.fetchall() self.assertEqual(len(booze), 1) self.assertEqual(len(booze[0]), 1) self.assertEqual(booze[0][0], 'Victoria Bitter') finally: cur1.close() cur2.close() con.close() def test_description(self): con = self._connect() try: cur = con.cursor() self.executeDDL1(cur) self.assertEqual(cur.description, None, 'cursor.description should be none after ' 'executing a statement that can return no ' 'rows (such as DDL)' ) cur.execute('select name from %sbooze' % self.table_prefix) self.assertEqual(len(cur.description), 1, 'cursor.description describes too many columns' ) self.assertEqual(len(cur.description[0]), 11, 'cursor.description[x] tuples must have ' '11 elements' ) self.assertEqual(cur.description[0][0].lower(), 'name', 'cursor.description[x][0] must return column ' 'name' ) # table name self.assertEqual(cur.description[0][8].lower(), 'dbapi20test_booze', 'cursor.description[x][0] must return column ' 'name' ) # original column name self.assertEqual(cur.description[0][9].lower(), 'name', 'cursor.description[x][0] must return column ' 'name' ) # original table name self.assertEqual(cur.description[0][10].lower(), 'dbapi20test_booze', 'cursor.description[x][0] must return i' 'column name' ) self.assertEqual(cur.description[0][1], self.driver.STRING, 'cursor.description[x][1] must return column ' 'type. Got %r' % cur.description[0][1] ) # Make sure self.description gets reset self.executeDDL2(cur) self.assertEqual(cur.description, None, 'cursor.description not being set to None when ' 'executing no-result statements (eg. DDL)' ) finally: cur.close() con.close() def test_rowcount(self): con = self._connect() try: cur = con.cursor(buffered=True) self.executeDDL1(cur) self.assertEqual(cur.rowcount, 0, 'cursor.rowcount should be 0 after executing ' 'no-result statements' ) cur.execute("insert into %sbooze values ('Victoria Bitter')" % ( self.table_prefix )) self.assertEqual(cur.rowcount, 1) cur.execute("select name from %sbooze" % self.table_prefix) self.assertEqual(cur.rowcount, 1) self.executeDDL2(cur) self.assertEqual(cur.rowcount, 0, 'cursor.rowcount not being reset to 0 after ' 'executing no-result statements' ) finally: cur.close() con.close() lower_func = 'lower' def test_close(self): con = self._connect() try: cur = con.cursor() finally: con.close() # cursor.execute should raise an Error if called after connection # closed self.assertRaises(self.driver.Error, self.executeDDL1, cur) # connection.commit should raise an Error if called after connection' # closed.' self.assertRaises(self.driver.Error, con.commit) # connection.close should raise an Error if called more than once self.assertRaises(self.driver.Error, con.close) def test_execute(self): con = self._connect() try: cur = con.cursor() self._paraminsert(cur) finally: con.close() def _paraminsert(self, cur): self.executeDDL1(cur) cur.execute("insert into %sbooze values ('Victoria Bitter')" % ( self.table_prefix )) self.assertTrue(cur.rowcount in (-1, 1)) if self.driver.paramstyle == 'qmark': cur.execute( 'insert into %sbooze values (?)' % self.table_prefix, ("Cooper's",) ) elif self.driver.paramstyle == 'numeric': cur.execute( 'insert into %sbooze values (:1)' % self.table_prefix, ("Cooper's",) ) elif self.driver.paramstyle == 'named': cur.execute( 'insert into %sbooze values (:beer)' % self.table_prefix, {'beer': "Cooper's"} ) elif self.driver.paramstyle == 'format': cur.execute( 'insert into %sbooze values (%%s)' % self.table_prefix, ("Cooper's",) ) elif self.driver.paramstyle == 'pyformat': cur.execute( 'insert into %sbooze values (%%(beer)s)' % self.table_prefix, {'beer': "Cooper's"} ) else: self.fail('Invalid paramstyle') self.assertTrue(cur.rowcount in (-1, 1)) cur.execute('select name from %sbooze' % self.table_prefix) res = cur.fetchall() self.assertEqual(len(res), 2, 'cursor.fetchall returned too few rows') beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual(beers[0], "Cooper's", 'cursor.fetchall retrieved incorrect data, or data ' 'inserted incorrectly' ) self.assertEqual(beers[1], "Victoria Bitter", 'cursor.fetchall retrieved incorrect data, ' 'or data inserted incorrectly' ) def test_executemany(self): if is_maxscale(): self.skipTest("MAXSCALE doesn't support BULK yet") con = self._connect() try: cur = con.cursor() self.executeDDL1(cur) largs = [("Cooper's",), ("Boag's",)] margs = [{'beer': "Cooper's"}, {'beer': "Boag's"}] if self.driver.paramstyle == 'qmark': cur.executemany( 'insert into %sbooze values (?)' % self.table_prefix, largs ) elif self.driver.paramstyle == 'numeric': cur.executemany( 'insert into %sbooze values (:1)' % self.table_prefix, largs ) elif self.driver.paramstyle == 'named': cur.executemany( 'insert into %sbooze values (:beer)' % self.table_prefix, margs ) elif self.driver.paramstyle == 'format': cur.executemany( 'insert into %sbooze values (%%s)' % self.table_prefix, largs ) elif self.driver.paramstyle == 'pyformat': cur.executemany( 'insert into %sbooze values (%%(beer)s)' % ( self.table_prefix ), margs ) else: self.fail('Unknown paramstyle') self.assertTrue(cur.rowcount in (-1, 2), 'insert using cursor.executemany set ' 'cursor.rowcount to ' 'incorrect value %r' % cur.rowcount ) cur.execute('select name from %sbooze' % self.table_prefix) res = cur.fetchall() self.assertEqual(len(res), 2, 'cursor.fetchall retrieved incorrect number ' 'of rows' ) beers = [res[0][0], res[1][0]] beers.sort() self.assertEqual(beers[0], "Boag's", 'incorrect data retrieved') self.assertEqual(beers[1], "Cooper's", 'incorrect data retrieved') finally: con.close() def test_fetchone(self): con = self._connect() try: cur = con.cursor(buffered=True) # cursor.fetchone should raise an Error if called before # executing a select-type query self.assertRaises(self.driver.Error, cur.fetchone) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows self.executeDDL1(cur) self.assertRaises(self.driver.Error, cur.fetchone) cur.execute('select name from %sbooze' % self.table_prefix) self.assertEqual(cur.fetchone(), None, 'cursor.fetchone should return None if a query ' 'retrieves no rows' ) self.assertTrue(cur.rowcount in (-1, 0)) # cursor.fetchone should raise an Error if called after # executing a query that cannot return rows cur.execute("insert into %sbooze values ('Victoria Bitter')" % ( self.table_prefix )) self.assertRaises(self.driver.Error, cur.fetchone) cur.execute('select name from %sbooze' % self.table_prefix) r = cur.fetchone() self.assertEqual(len(r), 1, 'cursor.fetchone should have retrieved a single ' ' row' ) self.assertEqual(r[0], 'Victoria Bitter', 'cursor.fetchone retrieved incorrect data' ) self.assertEqual(cur.fetchone(), None, 'cursor.fetchone should return None ' 'if no more rows available' ) self.assertTrue(cur.rowcount in (-1, 1)) finally: con.close() samples = [ 'Carlton Cold', 'Carlton Draft', 'Mountain Goat', 'Redback', 'Victoria Bitter', 'XXXX' ] def _populate(self): ''' Return a list of sql commands to setup the DB for the fetch tests. ''' populate = [ "insert into %sbooze values ('%s')" % (self.table_prefix, s) for s in self.samples ] return populate def test_fetchmany(self): con = self._connect() try: cur = con.cursor() # cursor.fetchmany should raise an Error if called without # issuing a query self.assertRaises(self.driver.Error, cur.fetchmany, 4) self.executeDDL1(cur) for sql in self._populate(): cur.execute(sql) cur.execute('select name from %sbooze' % self.table_prefix) r = cur.fetchmany() self.assertEqual(len(r), 1, 'cursor.fetchmany retrieved incorrect number of ' 'rows, default of arraysize is one.' ) cur.arraysize = 10 r = cur.fetchmany(3) # Should get 3 rows self.assertEqual(len(r), 3, 'cursor.fetchmany retrieved incorrect number ' 'of rows' ) r = cur.fetchmany(4) # Should get 2 more self.assertEqual(len(r), 2, 'cursor.fetchmany retrieved incorrect number ' 'of rows' ) r = cur.fetchmany(4) # Should be an empty sequence self.assertEqual(len(r), 0, 'cursor.fetchmany should return an empty ' 'sequence after ' 'results are exhausted' ) self.assertTrue(cur.rowcount in (-1, 6)) # Same as above, using cursor.arraysize cur.arraysize = 4 cur.execute('select name from %sbooze' % self.table_prefix) r = cur.fetchmany() # Should get 4 rows self.assertEqual(len(r), 4, 'cursor.arraysize not being honoured by fetchmany' ) r = cur.fetchmany() # Should get 2 more self.assertEqual(len(r), 2) r = cur.fetchmany() # Should be an empty sequence self.assertEqual(len(r), 0) self.assertTrue(cur.rowcount in (-1, 6)) cur.arraysize = 6 cur.execute('select name from %sbooze' % self.table_prefix) rows = cur.fetchmany() # Should get all rows self.assertTrue(cur.rowcount in (-1, 6)) self.assertEqual(len(rows), 6) self.assertEqual(len(rows), 6) rows = [r[0] for r in rows] rows.sort() # Make sure we get the right data back out for i in range(0, 6): self.assertEqual(rows[i], self.samples[i], 'incorrect data retrieved by cursor.fetchmany' ) rows = cur.fetchmany() # Should return an empty list self.assertEqual(len(rows), 0, 'cursor.fetchmany should return an empty ' 'sequence if called after the whole result ' 'set has been fetched' ) self.assertTrue(cur.rowcount in (-1, 6)) self.executeDDL2(cur) cur.execute('select name from %sbarflys' % self.table_prefix) r = cur.fetchmany() # Should get empty sequence self.assertEqual(len(r), 0, "cursor.fetchmany should return an " "empty sequence if query retrieved " "no rows" ) self.assertTrue(cur.rowcount in (-1, 0)) finally: con.close() def test_fetchall(self): con = self._connect() try: cur = con.cursor() # cursor.fetchall should raise an Error if called # without executing a query that may return rows (such # as a select) self.assertRaises(self.driver.Error, cur.fetchall) self.executeDDL1(cur) for sql in self._populate(): cur.execute(sql) # cursor.fetchall should raise an Error if called # after executing a a statement that cannot return rows self.assertRaises(self.driver.Error, cur.fetchall) cur.execute('select name from %sbooze' % self.table_prefix) rows = cur.fetchall() self.assertTrue(cur.rowcount in (-1, len(self.samples))) self.assertEqual(len(rows), len(self.samples), 'cursor.fetchall did not retrieve all rows' ) rows = [r[0] for r in rows] rows.sort() for i in range(0, len(self.samples)): self.assertEqual(rows[i], self.samples[i], 'cursor.fetchall retrieved incorrect rows' ) rows = cur.fetchall() self.assertEqual( len(rows), 0, 'cursor.fetchall should return an empty list if called ' 'after the whole result set has been fetched' ) self.assertTrue(cur.rowcount in (-1, len(self.samples))) self.executeDDL2(cur) cur.execute('select name from %sbarflys' % self.table_prefix) rows = cur.fetchall() self.assertTrue(cur.rowcount in (-1, 0)) self.assertEqual(len(rows), 0, 'cursor.fetchall should return an empty list if ' 'a select query returns no rows' ) finally: con.close() def test_mixedfetch(self): con = self._connect() try: cur = con.cursor() self.executeDDL1(cur) for sql in self._populate(): cur.execute(sql) cur.execute('select name from %sbooze' % self.table_prefix) rows1 = cur.fetchone() rows23 = cur.fetchmany(2) rows4 = cur.fetchone() rows56 = cur.fetchall() self.assertTrue(cur.rowcount in (-1, 6)) self.assertEqual(len(rows23), 2, 'fetchmany returned incorrect number of rows' ) self.assertEqual(len(rows56), 2, 'fetchall returned incorrect number of rows' ) rows = [rows1[0]] rows.extend([rows23[0][0], rows23[1][0]]) rows.append(rows4[0]) rows.extend([rows56[0][0], rows56[1][0]]) rows.sort() for i in range(0, len(self.samples)): self.assertEqual(rows[i], self.samples[i], 'incorrect data retrieved or inserted' ) finally: con.close() def help_nextset_setUp(self, cur): ''' Should create a procedure called deleteme that returns two result sets, first the number of rows in booze then "name from booze" ''' raise NotImplementedError('Helper not implemented') # sql=""" # create procedure deleteme as # begin # select count(*) from booze # select name from booze # end # """ # cur.execute(sql) def help_nextset_tearDown(self, cur): 'If cleaning up is needed after nextSetTest' raise NotImplementedError('Helper not implemented') # cur.execute("drop procedure deleteme") def test_arraysize(self): # Not much here - rest of the tests for this are in test_fetchmany con = self._connect() try: cur = con.cursor() self.assertTrue(hasattr(cur, 'arraysize'), 'cursor.arraysize must be defined' ) finally: con.close() def test_setinputsizes(self): con = self._connect() try: cur = con.cursor() cur.setinputsizes((25,)) self._paraminsert(cur) # Make sure cursor still works finally: con.close() def test_None(self): con = self._connect() try: cur = con.cursor() self.executeDDL1(cur) cur.execute('insert into %sbooze values (NULL)' % self.table_prefix) cur.execute('select name from %sbooze' % self.table_prefix) r = cur.fetchall() self.assertEqual(len(r), 1) self.assertEqual(len(r[0]), 1) self.assertEqual(r[0][0], None, 'NULL value not returned as None') finally: con.close() def test_Date(self): d1 = self.driver.Date(2002, 12, 25) self.assertTrue(isinstance(d1, datetime.date)) d2 = self.driver.DateFromTicks(time.mktime((2002, 12, 25, 0, 0, 0, 0, 0, 0))) # Can we assume this? API doesn't specify, but it seems implied self.assertEqual(str(d1), str(d2)) def test_Time(self): t1 = self.driver.Time(13, 45, 30) self.assertTrue(isinstance(t1, datetime.time)) t2 = self.driver.TimeFromTicks(time.mktime((2001, 1, 1, 13, 45, 30, 0, 0, 0))) # Can we assume this? API doesn't specify, but it seems implied self.assertTrue(isinstance(t2, datetime.time)) self.assertEqual(str(t1), str(t2)) def test_Timestamp(self): t1 = self.driver.Timestamp(2002, 12, 25, 13, 45, 30) self.assertTrue(isinstance(t1, datetime.datetime)) t2 = self.driver.TimestampFromTicks( time.mktime((2002, 12, 25, 13, 45, 30, 0, 0, 0)) ) self.assertTrue(isinstance(t2, datetime.datetime)) # Can we assume this? API doesn't specify, but it seems implied # self.assertEqual(str(t1),str(t2)) def test_Binary(self): b = self.driver.Binary(b'Something') self.assertTrue(isinstance(b, bytes)) b = self.driver.Binary(b'') self.assertTrue(isinstance(b, bytes)) def test_STRING(self): self.assertTrue(hasattr(self.driver, 'STRING'), 'module.STRING must be defined' ) def test_BINARY(self): self.assertTrue(hasattr(self.driver, 'BINARY'), 'module.BINARY must be defined.' ) def test_NUMBER(self): self.assertTrue(hasattr(self.driver, 'NUMBER'), 'module.NUMBER must be defined.' ) def test_DATETIME(self): self.assertTrue(hasattr(self.driver, 'DATETIME'), 'module.DATETIME must be defined.' ) def test_ROWID(self): self.assertTrue(hasattr(self.driver, 'ROWID'), 'module.ROWID must be defined.' ) if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_exception.py000066400000000000000000000033151456007477700271110ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import unittest from datetime import datetime import mariadb from test.base_test import create_connection class TestException(unittest.TestCase): def setUp(self): self.connection = create_connection() def tearDown(self): del self.connection def test_exception(self): cursor = self.connection.cursor() try: cursor.execute("WRONG QUERY") except mariadb.ProgrammingError as err: self.assertEqual(err.sqlstate, "42000") self.assertEqual(err.errno, 1064) self.assertTrue(err.errmsg.find("You have an error " "in your SQL syntax") > -1) pass del cursor def test_db_unknown_exception(self): try: create_connection({"database": "unknown"}) except mariadb.ProgrammingError as err: self.assertEqual(err.sqlstate, "42000") self.assertEqual(err.errno, 1049) self.assertTrue(err.errmsg.find("Unknown database 'unknown'") > -1) pass def test_conn_timeout_exception(self): start = datetime.today() try: create_connection({"connect_timeout": 1, "host": "8.8.8.8"}) except mariadb.OperationalError as err: self.assertEqual(err.sqlstate, "HY000") self.assertEqual(err.errno, 2002) self.assertTrue(err.errmsg.find("server on '8.8.8.8'") > -1) end = datetime.today() difference = end - start self.assertEqual(difference.days, 0) self.assertTrue(difference.seconds, 1) pass if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_module.py000066400000000000000000000015211456007477700263750ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import unittest import mariadb from test.base_test import create_connection class TestConnection(unittest.TestCase): def setUp(self): self.connection = create_connection() def tearDown(self): del self.connection def test_conpy_63(self): version = mariadb.__version__ version_info = mariadb.__version_info__ str_version = list(map(str, version.split('.'))) self.assertEqual(int(str_version[0]), version_info[0]) self.assertEqual(int(str_version[1]), version_info[1]) # patch might contain letters try: self.assertEqual(int(str_version[2]), version_info[2]) except Exception: self.assertEqual(str_version[2], version_info[2]) if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_nondbapi.py000066400000000000000000000112661456007477700267110ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import unittest import mariadb from test.base_test import create_connection, is_skysql, is_maxscale, is_mysql from test.conf_test import conf class CursorTest(unittest.TestCase): def setUp(self): self.connection = create_connection() def tearDown(self): del self.connection def test_ping(self): if is_maxscale(): self.skipTest("MAXSCALE return wrong thread id") new_conn = create_connection() id = new_conn.connection_id self.connection.kill(id) try: new_conn.ping() except mariadb.InterfaceError: pass del new_conn new_conn = create_connection() new_conn.auto_reconnect = True id = new_conn.connection_id self.connection.kill(id) new_conn.ping() new_id = new_conn.connection_id self.assertTrue(id != new_id) del new_conn def test_change_user(self): if is_skysql(): self.skipTest("SkySQL failure") if is_maxscale(): self.skipTest("MAXSCALE doesn't get new user immediately") if self.connection.server_name == "localhost": curs = self.connection.cursor(buffered=True) curs.execute("select * from information_schema.plugins " "where plugin_name='unix_socket' " "and plugin_status='ACTIVE'") if curs.rowcount > 0: del curs self.skipTest("unix_socket is active") del curs default_conf = conf() cursor = self.connection.cursor() cursor.execute("drop user if exists foo") if is_mysql() and self.connection.server_version < 80000: cursor.execute("create user foo@'%'") cursor.execute("GRANT ALL on `" + default_conf["database"] + "`.* TO foo@'%' IDENTIFIED BY " "'heyPassw-!µ20§rd'") else: cursor.execute("create user foo@'%' IDENTIFIED " "BY 'heyPassw-!µ20§rd'") cursor.execute("GRANT ALL on `" + default_conf["database"] + "`.* TO foo@'%'") new_conn = create_connection() new_conn.change_user("foo", "heyPassw-!µ20§rd", "") self.assertEqual("foo", new_conn.user) cursor.execute("drop user foo") del new_conn del cursor def test_reconnect(self): if is_maxscale(): self.skipTest("MAXSCALE wrong thread id") new_conn = create_connection() conn1_id = new_conn.connection_id self.connection.kill(conn1_id) new_conn.reconnect() conn2_id = new_conn.connection_id self.assertFalse(conn1_id == conn2_id) del new_conn def test_reset(self): if self.connection.server_version < 100204: self.skipTest("RESET not supported") cursor = self.connection.cursor() cursor.execute("SELECT 1 UNION SELECT 2") try: self.connection.ping() except mariadb.InterfaceError: pass self.connection.reset() self.connection.ping() del cursor def test_warnings(self): conn = self.connection cursor = conn.cursor() cursor.execute("SET session sql_mode=''") cursor.execute("CREATE TEMPORARY TABLE test_warnings (a tinyint)") cursor.execute("INSERT INTO test_warnings VALUES (300)") self.assertEqual(conn.warnings, 1) self.assertEqual(conn.warnings, cursor.warnings) del cursor def test_server_infos(self): self.assertTrue(self.connection.server_info) self.assertTrue(self.connection.server_version > 0) def test_escape(self): cursor = self.connection.cursor() cursor.execute("CREATE TEMPORARY TABLE test_escape (a varchar(100))") str = 'This is a \ and a \"' # noqa: W605 cmd = "INSERT INTO test_escape VALUES('%s')" % str try: cursor.execute(cmd) except mariadb.DatabaseError: pass str = self.connection.escape_string(str) cmd = "INSERT INTO test_escape VALUES('%s')" % str cursor.execute(cmd) del cursor def test_conpy279(self): conn = self.connection default_conf = conf() if "password" not in default_conf: default_conf["password"] = None try: conn.change_user(None, None, None) except TypeError: pass conn.change_user(default_conf["user"], default_conf["password"], None) conn.close() if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_pooling.py000066400000000000000000000276031456007477700265700ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import unittest import mariadb import platform from test.base_test import create_connection, conf, is_skysql, is_maxscale @unittest.skipIf(platform.python_implementation() == "PyPy", "skip pooling tests for PyPy") class TestPooling(unittest.TestCase): def setUp(self): pass # self.connection = create_connection() # self.connection.autocommit = False def tearDown(self): pass # del self.connection def test_connection_pools(self): pool = mariadb.ConnectionPool(pool_name="test_connection") self.assertEqual(mariadb._CONNECTION_POOLS["test_connection"], pool) pool.close() self.assertEqual(mariadb._CONNECTION_POOLS, {}) def test_conpy39(self): try: mariadb.ConnectionPool() except mariadb.ProgrammingError: pass def test_conpy246(self): # test if a pooled connection will be roll backed default_conf = conf() pool = mariadb.ConnectionPool(pool_name="CONPY246", pool_size=1, pool_reset_connection=False, **default_conf) conn = pool.get_connection() cursor = conn.cursor() cursor.execute("DROP TABLE IF EXISTS conpy246") cursor.execute("CREATE TABLE conpy246(a int)") cursor.execute("INSERT INTO conpy246 VALUES (1)") cursor.close() conn.close() conn = pool.get_connection() cursor = conn.cursor() cursor.execute("SELECT * FROM conpy246") self.assertEqual(cursor.rowcount, 0) cursor.execute("DROP TABLE conpy246") cursor.close() conn.close() pool.close() def test_conpy250(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="CONPY250", pool_size=16, pool_reset_connection=False, pool_validation_interval=0, **default_conf) self.assertEqual(pool.connection_count, 16) pool.close() self.assertEqual(pool.connection_count, 0) def test_conpy247_1(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="CONPY247_1", pool_size=1, pool_reset_connection=False, pool_validation_interval=0, **default_conf) # service connection conn = create_connection() cursor = conn.cursor() pconn = pool.get_connection() old_id = pconn.connection_id cursor.execute("KILL %s" % (old_id,)) pconn.close() pconn = pool.get_connection() self.assertNotEqual(old_id, pconn.connection_id) conn.close() pool.close() def test_conpy247_2(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="CONPY247_2", pool_size=1, pool_reset_connection=True, pool_validation_interval=0, **default_conf) # service connection conn = create_connection() cursor = conn.cursor() pconn = pool.get_connection() old_id = pconn.connection_id cursor.execute("KILL %s" % (old_id,)) pconn.close() pconn = pool.get_connection() self.assertNotEqual(old_id, pconn.connection_id) conn.close() pool.close() def test_conpy247_3(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="CONPY247_3", pool_size=10, pool_reset_connection=True, pool_validation_interval=0, **default_conf) # service connection conn = create_connection() cursor = conn.cursor() ids = [] cursor.execute("DROP PROCEDURE IF EXISTS p1") sql = """CREATE PROCEDURE p1() BEGIN SELECT 1; SELECT 2; END""" cursor.execute(sql) for i in range(0, 10): pconn = pool.get_connection() ids.append(pconn.connection_id) cursor.execute("KILL %s" % (pconn.connection_id,)) pconn.close() new_ids = [] for i in range(0, 10): pconn = pool.get_connection() new_ids.append(pconn.connection_id) self.assertEqual(pconn.connection_id in ids, False) cursor = pconn.cursor() cursor.callproc("p1") cursor.close() pconn.close() for i in range(0, 10): pconn = pool.get_connection() self.assertEqual(pconn.connection_id in new_ids, True) pconn.close() conn.close() pool.close() def test_conpy245(self): # we can't test performance here, but we can check if LRU works. # All connections must have been used the same number of times. default_conf = conf() pool_size = 64 iterations = 100 pool = mariadb.ConnectionPool(pool_name="CONPY245", pool_size=pool_size, **default_conf) for i in range(0, iterations): for j in range(0, pool_size): conn = pool.get_connection() conn.close() for i in range(0, pool_size): conn = pool.get_connection() self.assertEqual(conn._used, iterations + 1) conn.close() pool.close() def test_connection_pool_conf(self): pool = mariadb.ConnectionPool(pool_name="test_conf") default_conf = conf() conn = create_connection() try: pool.add_connection(conn) except mariadb.PoolError: pass try: pool.set_config(**default_conf) except mariadb.Error: pool.close() raise pool.add_connection(conn) c = pool.get_connection() self.assertEqual(c, conn) pool.close() def test_connection_pool_maxconn(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="test_max_size", pool_size=6, **default_conf) connections = [] for i in range(0, 6): connections.append(pool.get_connection()) self.assertRaises(mariadb.PoolError, lambda:pool.get_connection()) for c in connections: c.close() pool.close() def test_connection_pool_add(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="test_connection_pool_add") try: pool.set_config(**default_conf) except mariadb.Error: pool.close() raise for i in range(1, 6): pool.add_connection() try: pool.add_connection() except mariadb.PoolError: pass pool.close() def test_conpy69(self): if is_skysql(): self.skipTest("skipping on SkySQL") if is_maxscale(): self.skipTest("skipping on maxscale, bug") conn = create_connection() conn.autocommit = True cursor1 = conn.cursor() cursor1.execute("CREATE SCHEMA IF NOT EXISTS 中文考试") cursor1.execute("COMMIT") default_conf = conf() default_conf["database"] = "中文考试" pool = mariadb.ConnectionPool(pool_name="test_conpy69") try: pool.set_config(**default_conf) except mariadb.Error: pool.close() raise try: for i in range(1, 6): pool.add_connection() conn = mariadb.connect(pool_name="test_conpy69") conn.autocommit = True cursor = conn.cursor() cursor.execute("select database()") row = cursor.fetchone() self.assertEqual(row[0], "中文考试") cursor.execute("CREATE TEMPORARY TABLE t1 " "(a varchar(255)) character set utf8mb4") cursor.execute("insert into t1 values (?)", ("123.45 中文考试",)) cursor.execute("select a from t1", buffered=True) row = cursor.fetchone() self.assertEqual(row[0], "123.45 中文考试") cursor1.execute("DROP SCHEMA 中文考试") finally: pool.close() def test__CONNECTION_POOLS(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name="test_use", **default_conf) conn = mariadb.connect(pool_name="test_use") cursor = conn.cursor() cursor.execute("SELECT 1") row = cursor.fetchone() self.assertEqual(row[0], 1) del cursor pool.close() def test_create_pool_from_conn(self): default_conf = conf() key = "t1" conn = mariadb.connect(pool_name=key, **default_conf) cursor = conn.cursor() del mariadb._CONNECTION_POOLS["t1"] self.assertEqual(mariadb._CONNECTION_POOLS, {}) try: cursor.execute("SELECT 1") except mariadb.ProgrammingError: pass def test_pool_getter(self): default_conf = conf() mariadb.connect(pool_name="getter_test", pool_size=4, **default_conf) p = mariadb._CONNECTION_POOLS["getter_test"] self.assertEqual(p.pool_name, "getter_test") self.assertEqual(p.pool_size, 4) if "pool_reset_connection" in default_conf: self.assertEqual(p.pool_reset_connection, default_conf["pool_reset_connection"]) else: self.assertEqual(p.pool_reset_connection, True) self.assertEqual(p.max_size, 64) mariadb._CONNECTION_POOLS["getter_test"].close() def test_pool_connection_reset(self): default_conf = conf() conn = mariadb.connect(pool_name="reset_test", pool_size=1, **default_conf) cursor = conn.cursor() cursor.execute("SELECT 1") cursor.close() conn.close() conn = mariadb.connect(pool_name="reset_test") cursor = conn.cursor() cursor.execute("SELECT 2") row = cursor.fetchone() self.assertEqual(row[0], 2) mariadb._CONNECTION_POOLS["reset_test"].close() def test_conpy40(self): default_conf = conf() pool = mariadb.ConnectionPool(pool_name='test_conpy40') try: pool.set_config(pool_size=3) except mariadb.PoolError: pass try: pool.set_config(**default_conf) except mariadb.Error: pool.close() raise for j in range(3): c = mariadb.connect(**default_conf) pool.add_connection(c) pool.close() def test_pool_add(self): pool = mariadb.ConnectionPool(pool_name="test_pool_add") try: mariadb.ConnectionPool(pool_name="test_pool_add") except mariadb.ProgrammingError: pass pool.close() self.assertEqual(mariadb._CONNECTION_POOLS, {}) def test_conpy256(self): size = 10 connections = [] default_conf = conf() pool = mariadb.ConnectionPool(pool_name="test_conpy256", pool_size=size, **default_conf) for i in range(size): c= pool.get_connection() self.assertNotEqual(c in connections, True) connections.append(c) pool.close() if __name__ == '__main__': unittest.main() mariadb-connector-python-1.1.10/testing/test/integration/test_xa.py000066400000000000000000000072641456007477700255320ustar00rootroot00000000000000#!/usr/bin/env python -O # -*- coding: utf-8 -*- import unittest import mariadb from test.base_test import create_connection class TestCA(unittest.TestCase): def setUp(self): self.connection = create_connection() self.connection.autocommit = False def tearDown(self): del self.connection def test_xid(self): con = create_connection() xid = con.xid(1, "foo", "bar") self.assertEqual(xid, (1, "foo", "bar")) # default for format_id is 1 xid = con.xid(0, "foo", "bar") self.assertEqual(xid, (1, "foo", "bar")) # parameter too long: try: xid = con.xid(0, "a" * 65, "bar") except mariadb.ProgrammingError: pass try: xid = con.xid(0, "foo", "b" * 65) except mariadb.ProgrammingError: pass def test_tpc_begin(self): con = create_connection() xid = con.xid(0, "1234567890", "2345") try: con.tpc_begin(xid) except mariadb.NotSupportedError: pass def test_tpc_commit(self): con = create_connection() xid = con.xid(0, "1234567891", "2345") cursor = con.cursor() cursor.execute("DROP TABLE IF EXISTS t1") cursor.execute("CREATE TABLE t1 (a int)") try: con.tpc_begin(xid) cursor.execute("INSERT INTO t1 VALUES (1),(2)") cursor.close() con.tpc_commit() finally: con.close() def test_tpc_rollback_without_prepare(self): con = create_connection() try: xid = con.xid(0, "1234567892", "2345") con.tpc_begin(xid) cursor = con.cursor() cursor.execute("SELECT 1") cursor.close() con.tpc_rollback() finally: con.close() def test_tpc_commit_with_prepare(self): con = create_connection() try: xid = con.xid(0, "1234567893", "2345") con.tpc_begin(xid) cursor = con.cursor() cursor.execute("SELECT 1") cursor.close() con.tpc_prepare() con.tpc_commit() finally: con.close() def test_tpc_rollback_with_prepare(self): con = create_connection() try: xid = con.xid(0, "1234567894", "2345") con.tpc_begin(xid) cursor = con.cursor() cursor.execute("SELECT 1") cursor.close() con.tpc_prepare() con.tpc_rollback() finally: con.close() def test_tpc_begin_in_transaction_fails(self): con = create_connection() try: xid = con.xid(0, "1234567895", "2345") cursor = con.cursor() cursor.execute("BEGIN") cursor.execute("SELECT 1") cursor.close() self.assertRaises(mariadb.IntegrityError, con.tpc_begin, xid) finally: con.close() def test_commit_in_tpc_fails(self): con = create_connection() try: xid = con.xid(0, "1234567897", "2345") con.tpc_begin(xid) self.assertRaises(mariadb.ProgrammingError, con.commit) finally: con.close() def test_rollback_in_tpc_fails(self): # calling rollback() within a TPC transaction fails with # ProgrammingError. con = create_connection() try: xid = con.xid(0, "1234567898", "2345") con.tpc_begin(xid) self.assertRaises(mariadb.ProgrammingError, con.rollback) finally: con.close() if __name__ == '__main__': unittest.main()