pax_global_header00006660000000000000000000000064134061555520014520gustar00rootroot0000000000000052 comment=8eee26692726e7c923186322a68e15e4d98c138e PyMySQL-0.9.3/000077500000000000000000000000001340615555200130075ustar00rootroot00000000000000PyMySQL-0.9.3/.coveragerc000066400000000000000000000004541340615555200151330ustar00rootroot00000000000000[run] branch = True source = pymysql omit = pymysql/tests/* pymysql/tests/thirdparty/test_MySQLdb/* [report] exclude_lines = pragma: no cover except ImportError: if DEBUG: def __repr__ def __str__ raise NotImplementedError def __getattr__ raise ValueError PyMySQL-0.9.3/.github/000077500000000000000000000000001340615555200143475ustar00rootroot00000000000000PyMySQL-0.9.3/.github/ISSUE_TEMPLATE.md000066400000000000000000000012051340615555200170520ustar00rootroot00000000000000This project is maintained one busy person with a frail wife and an infant daughter. My time and energy is a very limited resource. I'm not a teacher or free tech support. Don't ask a question here. Don't file an issue until you believe it's a not a problem with your code. Search for friendly volunteers who can teach you or review your code on ML or Q&A sites. See also: https://medium.com/@methane/why-you-must-not-ask-questions-on-github-issues-51d741d83fde If you're sure it's PyMySQL's issue, report the complete steps to reproduce, from creating database. I don't have time to investigate your issue from an incomplete code snippet. PyMySQL-0.9.3/.gitignore000066400000000000000000000002361340615555200150000ustar00rootroot00000000000000*.pyc *.pyo /.cache /.coverage /.idea /.tox /.venv /.vscode /PyMySQL.egg-info /build /dist /docs/build /pymysql/tests/databases.json __pycache__ Pipfile.lock PyMySQL-0.9.3/.travis.yml000066400000000000000000000030041340615555200151150ustar00rootroot00000000000000# vim: sw=2 ts=2 sts=2 expandtab sudo: required language: python services: - docker cache: pip matrix: include: - env: - DB=mariadb:5.5 python: "3.5" - env: - DB=mariadb:10.0 python: "3.6" - env: - DB=mariadb:10.1 python: "pypy" - env: - DB=mariadb:10.2 python: "2.7" - env: - DB=mariadb:10.3 python: "3.7-dev" - env: - DB=mysql:5.5 python: "3.5" - env: - DB=mysql:5.6 python: "3.6" - env: - DB=mysql:5.7 python: "3.4" - env: - DB=mysql:8.0 - TEST_AUTH=yes python: "3.7-dev" - env: - DB=mysql:8.0 - TEST_AUTH=yes python: "2.7" # different py version from 5.6 and 5.7 as cache seems to be based on py version # http://dev.mysql.com/downloads/mysql/5.7.html has latest development release version # really only need libaio1 for DB builds however libaio-dev is whitelisted for container builds and liaio1 isn't install: - pip install -U coveralls unittest2 coverage cryptography pytest pytest-cov before_script: - ./.travis/initializedb.sh - python -VV - rm -f ~/.my.cnf # set in .travis.initialize.db.sh for the above commands - we should be using database.json however - export COVERALLS_PARALLEL=true script: - coverage run ./runtests.py - if [ "${TEST_AUTH}" = "yes" ]; then pytest -v --cov-config .coveragerc tests; fi - if [ ! -z "${DB}" ]; then docker logs mysqld; fi after_success: - coveralls PyMySQL-0.9.3/.travis/000077500000000000000000000000001340615555200143755ustar00rootroot00000000000000PyMySQL-0.9.3/.travis/database.json000066400000000000000000000004141340615555200170330ustar00rootroot00000000000000[ {"host": "localhost", "unix_socket": "/var/run/mysqld/mysqld.sock", "user": "root", "passwd": "", "db": "test1", "use_unicode": true, "local_infile": true}, {"host": "127.0.0.1", "port": 3306, "user": "test2", "password": "some password", "db": "test2" } ] PyMySQL-0.9.3/.travis/docker.json000066400000000000000000000003551340615555200165420ustar00rootroot00000000000000[ {"host": "127.0.0.1", "port": 3306, "user": "root", "passwd": "", "db": "test1", "use_unicode": true, "local_infile": true}, {"host": "127.0.0.1", "port": 3306, "user": "test2", "password": "some password", "db": "test2" } ] PyMySQL-0.9.3/.travis/initializedb.sh000077500000000000000000000050541340615555200174070ustar00rootroot00000000000000#!/bin/bash #debug set -x #verbose set -v if [ ! -z "${DB}" ]; then # disable existing database server in case of accidential connection sudo service mysql stop docker pull ${DB} docker run -it --name=mysqld -d -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -p 3306:3306 ${DB} sleep 10 mysql() { docker exec mysqld mysql "${@}" } while : do sleep 5 mysql -e 'select version()' if [ $? = 0 ]; then break fi echo "server logs" docker logs --tail 5 mysqld done mysql -e 'select VERSION()' if [ $DB == 'mysql:8.0' ]; then WITH_PLUGIN='with mysql_native_password' mysql -e 'SET GLOBAL local_infile=on' docker cp mysqld:/var/lib/mysql/public_key.pem "${HOME}" docker cp mysqld:/var/lib/mysql/ca.pem "${HOME}" docker cp mysqld:/var/lib/mysql/server-cert.pem "${HOME}" docker cp mysqld:/var/lib/mysql/client-key.pem "${HOME}" docker cp mysqld:/var/lib/mysql/client-cert.pem "${HOME}" # Test user for auth test mysql -e ' CREATE USER user_sha256 IDENTIFIED WITH "sha256_password" BY "pass_sha256", nopass_sha256 IDENTIFIED WITH "sha256_password", user_caching_sha2 IDENTIFIED WITH "caching_sha2_password" BY "pass_caching_sha2", nopass_caching_sha2 IDENTIFIED WITH "caching_sha2_password" PASSWORD EXPIRE NEVER;' mysql -e 'GRANT RELOAD ON *.* TO user_caching_sha2;' else WITH_PLUGIN='' fi mysql -uroot -e 'create database test1 DEFAULT CHARACTER SET utf8mb4' mysql -uroot -e 'create database test2 DEFAULT CHARACTER SET utf8mb4' mysql -u root -e "create user test2 identified ${WITH_PLUGIN} by 'some password'; grant all on test2.* to test2;" mysql -u root -e "create user test2@localhost identified ${WITH_PLUGIN} by 'some password'; grant all on test2.* to test2@localhost;" cp .travis/docker.json pymysql/tests/databases.json else cat ~/.my.cnf mysql -e 'select VERSION()' mysql -e 'create database test1 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;' mysql -e 'create database test2 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;' mysql -u root -e "create user test2 identified by 'some password'; grant all on test2.* to test2;" mysql -u root -e "create user test2@localhost identified by 'some password'; grant all on test2.* to test2@localhost;" cp .travis/database.json pymysql/tests/databases.json fi PyMySQL-0.9.3/CHANGELOG000066400000000000000000000214331340615555200142240ustar00rootroot00000000000000# Changes ## 0.9.3 Release date: 2018-12-18 * cryptography dependency is optional now. * Fix old_password (used before MySQL 4.1) support. * Deprecate old_password. * Stop sending ``sys.argv[0]`` for connection attribute "program_name". * Close connection when unknown error is happened. * Deprecate context manager API of Connection object. ## 0.9.2 Release date: 2018-07-04 * Disalbled unintentinally enabled debug log * Removed unintentionally installed tests ## 0.9.1 Release date: 2018-07-03 * Fixed caching_sha2_password and sha256_password raise TypeError on PY2 (#700, #702) ## 0.9.0 Release date: 2018-06-27 * Change default charset from latin1 to utf8mb4. (because MySQL 8 changed) (#692) * Support sha256_password and caching_sha2_password auth method (#682) * Add cryptography dependency, because it's needed for new auth methods. * Remove deprecated `no_delay` option (#694) * Support connection attributes (#679) * Map LOCK_DEADLOCK to OperationalError (#693) ## 0.8.1 Release date: 2018-05-07 * Reduce `cursor.callproc()` roundtrip time. (#636) * Fixed `cursor.query()` is hunged after multi statement failed. (#647) * WRONG_DB_NAME and WRONG_COLUMN_NAME is ProgrammingError for now. (#629) * Many test suite improvements, especially adding MySQL 8.0 and using Docker. Thanks to Daniel Black. * Droppped support for old Python and MySQL whih is not tested long time. ## 0.8 Release date: 2017-12-20 * **BACKWARD INCOMPATIBLE** ``binary_prefix`` option is added and off by default because of compatibility with mysqlclient. When you need PyMySQL 0.7 behavior, you have to pass ``binary_prefix=True``. (#549) * **BACKWARD INCOMPATIBLE** ``MULTI_STATEMENTS`` client flag is no longer set by default, while it was on PyMySQL 0.7. You need to pass ``client_flag=CLIENT.MULTI_STATEMENTS`` when you connect to explicitly enable multi-statement mode. (#590) * Fixed AuthSwitch packet handling. * Raise OperationalError for MariaDB's constraint error. (#607) * executemany() accepts query without space between ``VALUES`` and ``(``. (#597) * Support config file containing option without value. (#588) * Fixed Connection.ping() returned unintended value. ## 0.7.11 Release date: 2017-04-06 * Fixed Connection.close() failed when failed to send COM_CLOSE packet. * Cursor.executemany() accepts query ends with semicolon. * ssl parameters can be read from my.cnf. ## 0.7.10 Release date: 2017-02-14 * **SECURITY FIX**: Raise RuntimeError when received LOAD_LOCAL packet while ``loacal_infile=False``. (Thanks to Bryan Helmig) * Raise SERVER_LOST error for MariaDB's shutdown packet (#540) * Change default connect_timeout to 10. * Add bind_address option (#529) ## 0.7.9 Release date: 2016-09-03 * Fix PyMySQL stop reading rows when first column is empty string (#513) Reverts DEPRECATE_EOF introduced in 0.7.7. ## 0.7.8 Release date: 2016-09-01 * Revert error message change in 0.7.7. (SQLAlchemy parses error message, #507) ## 0.7.7 Release date: 2016-08-30 * Add new unicode collation (#498) * Fix conv option is not used for encoding objects. * Experimental support for DEPRECATE_EOF protocol. ## 0.7.6 Release date: 2016-07-29 * Fix SELECT JSON type cause UnicodeError * Avoid float convertion while parsing microseconds * Warning has number * SSCursor supports warnings ## 0.7.5 Release date: 2016-06-28 * Fix exception raised while importing when getpwuid() fails (#472) * SSCursor supports LOAD DATA LOCAL INFILE (#473) * Fix encoding error happen for JSON type (#477) * Fix test fail on Python 2.7 and MySQL 5.7 (#478) ## 0.7.4 Release date: 2016-05-26 * Fix AttributeError may happen while Connection.__del__ (#463) * Fix SyntaxError in test_cursor. (#464) * frozenset support for query value. (#461) * Start using readthedocs.io ## 0.7.3 Release date: 2016-05-19 * Add read_timeout and write_timeout option. * Support serialization customization by `conv` option. * Unknown type is converted by `str()`, for MySQLdb compatibility. * Support '%%' in `Cursor.executemany()` * Support REPLACE statement in `Cursor.executemany()` * Fix handling incomplete row caused by 'SHOW SLAVE HOSTS'. * Fix decode error when use_unicode=False on PY3 * Fix port option in my.cnf file is ignored. ## 0.7.2 Release date: 2016-02-24 * Fix misuse of `max_allowed_packet` parameter. (#426, #407 and #397) * Add %(name)s plceholder support to `Cursor.executemany()`. (#427, thanks to @WorldException) ## 0.7.1 Release date: 2016-01-14 * Fix auth fail with MySQL 5.1 * Fix escaping unicode fails on Python 2 ## 0.7 Release date: 2016-01-10 * Faster binary escaping * Add `"_binary" prefix` to string literal for binary types. binary types are: `bytearray` on Python 2, `bytes` and `bytearray` on Python 3. This is because recent MySQL show warnings when string literal is invalid for connection encoding. * `pymysql.Binary()` returns `bytearray` on Python 2. This is required to distinguish binary and string. * Auth plugin support. * no_delay option is ignored. It will be removed in PyMySQL 0.8. ## 0.6.7 Release date: 2015-09-30 * Allow self signed certificate * Add max_allowed_packet option * Fix error when bytes in executemany * Support geometry type * Add coveralls badge to README * Fix some bugs relating to warnings * Add Cursor.mogrify() method * no_delay option is deprecated and True by default * Fix options from my.cnf overrides options from arguments * Allow socket like object. (It's not feature for end users) * Strip quotes while reading options from my.cnf file * Fix encoding issue in executemany() ## 0.6.6 * Add context manager to cursor * Fix can't encode blob that is not utf-8 on PY3. (regression of 0.6.4, Thanks to @wiggzz) ## 0.6.5 Skipped ## 0.6.4 * Support "LOAD LOCAL INFILE". Thanks @wraziens * Show MySQL warnings after execute query. * Fix MySQLError may be wrapped with OperationalError while connectiong. (#274) * SSCursor no longer attempts to expire un-collected rows within __del__, delaying termination of an interrupted program; cleanup of uncollected rows is left to the Connection on next execute, which emits a warning at that time. (#287) * Support datetime and time with microsecond. (#303) * Use surrogateescape to format bytes on Python 3. * OperationalError raised from connect() have information about original exception. (#304) * `init_command` now support multi statement. * `Connection.escape()` method now accepts second argument compatible to MySQL-Python. ## 0.6.3 * Fixed multiple result sets with SSCursor. * Fixed connection timeout. * Fixed literal set syntax to work on Py2.6. * Allow for mysql negative values with 0 hour timedelta. * Added Connection.begin(). ## 0.6.2 * Fixed old password on Python 3. * Added support for bulk insert in Cursor.executemany(). * Added support for microseconds in datetimes and dates before 1900. * Several other bug fixes. ## 0.6.1 * Added cursor._last_executed for MySQLdb compatibility * Cursor.fetchall() and .fetchmany now return list, not tuple * Allow "length of auth-plugin-data" = 0 * Cursor.connection references connection object without weakref ## 0.6 * Improved Py3k support * Improved PyPy support * Added IPv6 support * Added Thing2Literal for Django/MySQLdb compatibility * Removed errorhandler * Fixed GC errors * Improved test suite * Many bug fixes * Many performance improvements ## 0.4 * Miscellaneous bug fixes * Implementation of SSL support * Implementation of kill() * Cleaned up charset functionality * Fixed BIT type handling * Connections raise exceptions after they are close()'d * Full Py3k and unicode support ## 0.3 * Implemented most of the extended DBAPI 2.0 spec including callproc() * Fixed error handling to include the message from the server and support multiple protocol versions. * Implemented ping() * Implemented unicode support (probably needs better testing) * Removed DeprecationWarnings * Ran against the MySQLdb unit tests to check for bugs * Added support for client_flag, charset, sql_mode, read_default_file, use_unicode, cursorclass, init_command, and connect_timeout. * Refactoring for some more compatibility with MySQLdb including a fake pymysql.version_info attribute. * Now runs with no warnings with the -3 command-line switch * Added test cases for all outstanding tickets and closed most of them. * Basic Jython support added. * Fixed empty result sets bug. * Integrated new unit tests and refactored the example into one. * Fixed bug with decimal conversion. * Fixed string encoding bug. Now unicode and binary data work! * Added very basic docstrings. ## 0.2 * Changed connection parameter name 'password' to 'passwd' to make it more plugin replaceable for the other mysql clients. * Changed pack()/unpack() calls so it runs on 64 bit OSes too. * Added support for unix_socket. * Added support for no password. * Renamed decorders to decoders. * Better handling of non-existing decoder. PyMySQL-0.9.3/LICENSE000066400000000000000000000020561340615555200140170ustar00rootroot00000000000000Copyright (c) 2010, 2013 PyMySQL contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PyMySQL-0.9.3/MANIFEST.in000066400000000000000000000000451340615555200145440ustar00rootroot00000000000000include README.rst LICENSE CHANGELOG PyMySQL-0.9.3/Pipfile000066400000000000000000000002601340615555200143200ustar00rootroot00000000000000[[source]] url = "https://pypi.org/simple" verify_ssl = true name = "pypi" [packages] cryptography = "*" [dev-packages] pytest = "*" unittest2 = "*" twine = "*" flake8 = "*" PyMySQL-0.9.3/README.rst000066400000000000000000000100101340615555200144660ustar00rootroot00000000000000.. image:: https://readthedocs.org/projects/pymysql/badge/?version=latest :target: https://pymysql.readthedocs.io/ :alt: Documentation Status .. image:: https://badge.fury.io/py/PyMySQL.svg :target: https://badge.fury.io/py/PyMySQL .. image:: https://travis-ci.org/PyMySQL/PyMySQL.svg?branch=master :target: https://travis-ci.org/PyMySQL/PyMySQL .. image:: https://coveralls.io/repos/PyMySQL/PyMySQL/badge.svg?branch=master&service=github :target: https://coveralls.io/github/PyMySQL/PyMySQL?branch=master .. image:: https://img.shields.io/badge/license-MIT-blue.svg :target: https://github.com/PyMySQL/PyMySQL/blob/master/LICENSE PyMySQL ======= .. contents:: Table of Contents :local: This package contains a pure-Python MySQL client library, based on `PEP 249`_. Most public APIs are compatible with mysqlclient and MySQLdb. NOTE: PyMySQL doesn't support low level APIs `_mysql` provides like `data_seek`, `store_result`, and `use_result`. You should use high level APIs defined in `PEP 249`_. But some APIs like `autocommit` and `ping` are supported because `PEP 249`_ doesn't cover their usecase. .. _`PEP 249`: https://www.python.org/dev/peps/pep-0249/ Requirements ------------- * Python -- one of the following: - CPython_ : 2.7 and >= 3.4 - PyPy_ : Latest version * MySQL Server -- one of the following: - MySQL_ >= 5.5 - MariaDB_ >= 5.5 .. _CPython: https://www.python.org/ .. _PyPy: https://pypy.org/ .. _MySQL: https://www.mysql.com/ .. _MariaDB: https://mariadb.org/ Installation ------------ Package is uploaded on `PyPI `_. You can install it with pip:: $ python3 -m pip install PyMySQL To use "sha256_password" or "caching_sha2_password" for authenticate, you need to install additional dependency:: $ python3 -m pip install PyMySQL[rsa] Documentation ------------- Documentation is available online: https://pymysql.readthedocs.io/ For support, please refer to the `StackOverflow `_. Example ------- The following examples make use of a simple table .. code:: sql CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) COLLATE utf8_bin NOT NULL, `password` varchar(255) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ; .. code:: python import pymysql.cursors # Connect to the database connection = pymysql.connect(host='localhost', user='user', password='passwd', db='db', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) try: with connection.cursor() as cursor: # Create a new record sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)" cursor.execute(sql, ('webmaster@python.org', 'very-secret')) # connection is not autocommit by default. So you must commit to save # your changes. connection.commit() with connection.cursor() as cursor: # Read a single record sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s" cursor.execute(sql, ('webmaster@python.org',)) result = cursor.fetchone() print(result) finally: connection.close() This example will print: .. code:: python {'password': 'very-secret', 'id': 1} Resources --------- * DB-API 2.0: https://www.python.org/dev/peps/pep-0249/ * MySQL Reference Manuals: https://dev.mysql.com/doc/ * MySQL client/server protocol: https://dev.mysql.com/doc/internals/en/client-server-protocol.html * "Connector" channel in MySQL Community Slack: https://lefred.be/mysql-community-on-slack/ * PyMySQL mailing list: https://groups.google.com/forum/#!forum/pymysql-users License ------- PyMySQL is released under the MIT License. See LICENSE for more information. PyMySQL-0.9.3/docs/000077500000000000000000000000001340615555200137375ustar00rootroot00000000000000PyMySQL-0.9.3/docs/Makefile000066400000000000000000000151671340615555200154110ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyMySQL.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyMySQL.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/PyMySQL" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyMySQL" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." PyMySQL-0.9.3/docs/make.bat000066400000000000000000000150701340615555200153470ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyMySQL.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyMySQL.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end PyMySQL-0.9.3/docs/source/000077500000000000000000000000001340615555200152375ustar00rootroot00000000000000PyMySQL-0.9.3/docs/source/conf.py000066400000000000000000000203731340615555200165430ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # PyMySQL documentation build configuration file, created by # sphinx-quickstart on Tue May 17 12:01:11 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../../')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'PyMySQL' copyright = u'2016, Yutaka Matsubara and GitHub contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.7' # The full version, including alpha/beta/rc tags. release = '0.7.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'PyMySQLdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'PyMySQL.tex', u'PyMySQL Documentation', u'Yutaka Matsubara and GitHub contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pymysql', u'PyMySQL Documentation', [u'Yutaka Matsubara and GitHub contributors'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'PyMySQL', u'PyMySQL Documentation', u'Yutaka Matsubara and GitHub contributors', 'PyMySQL', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} PyMySQL-0.9.3/docs/source/index.rst000066400000000000000000000003411340615555200170760ustar00rootroot00000000000000Welcome to PyMySQL's documentation! =================================== .. toctree:: :maxdepth: 2 user/index modules/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` PyMySQL-0.9.3/docs/source/modules/000077500000000000000000000000001340615555200167075ustar00rootroot00000000000000PyMySQL-0.9.3/docs/source/modules/connections.rst000066400000000000000000000005571340615555200217720ustar00rootroot00000000000000Connection Object ================= .. module:: pymysql.connections .. autoclass:: Connection :members: :exclude-members: DataError, DatabaseError, Error, InterfaceError, IntegrityError, InternalError, NotSupportedError, OperationalError, ProgrammingError, Warning, escape, literal, write_packet PyMySQL-0.9.3/docs/source/modules/cursors.rst000066400000000000000000000006451340615555200211460ustar00rootroot00000000000000Cursor Objects ============== .. module:: pymysql.cursors .. autoclass:: Cursor :members: :exclude-members: DataError, DatabaseError, Error, InterfaceError, IntegrityError, InternalError, NotSupportedError, OperationalError, ProgrammingError, Warning .. autoclass:: SSCursor :members: .. autoclass:: DictCursor :members: .. autoclass:: SSDictCursor :members: PyMySQL-0.9.3/docs/source/modules/index.rst000066400000000000000000000005021340615555200205450ustar00rootroot00000000000000API Reference ------------- If you are looking for information on a specific function, class or method, this part of the documentation is for you. For more information, please read the `Python Database API specification `_. .. toctree:: :maxdepth: 2 connections cursors PyMySQL-0.9.3/docs/source/user/000077500000000000000000000000001340615555200162155ustar00rootroot00000000000000PyMySQL-0.9.3/docs/source/user/development.rst000066400000000000000000000020311340615555200212650ustar00rootroot00000000000000.. _development: =========== Development =========== You can help developing PyMySQL by `contributing on GitHub`_. .. _contributing on GitHub: https://github.com/PyMySQL/PyMySQL Building the documentation -------------------------- Go to the ``docs`` directory and run ``make html``. Test Suite ----------- If you would like to run the test suite, create a database for testing like this:: mysql -e 'create database test_pymysql DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;' mysql -e 'create database test_pymysql2 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;' Then, copy the file ``.travis/database.json`` to ``pymysql/tests/databases.json`` and edit the new file to match your MySQL configuration:: $ cp .travis/database.json pymysql/tests/databases.json $ $EDITOR pymysql/tests/databases.json To run all the tests, execute the script ``runtests.py``:: $ python runtests.py A ``tox.ini`` file is also provided for conveniently running tests on multiple Python versions:: $ tox PyMySQL-0.9.3/docs/source/user/examples.rst000066400000000000000000000030641340615555200205700ustar00rootroot00000000000000.. _examples: ======== Examples ======== .. _CRUD: CRUD ---- The following examples make use of a simple table .. code:: sql CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `email` varchar(255) COLLATE utf8_bin NOT NULL, `password` varchar(255) COLLATE utf8_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ; .. code:: python import pymysql.cursors # Connect to the database connection = pymysql.connect(host='localhost', user='user', password='passwd', db='db', charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) try: with connection.cursor() as cursor: # Create a new record sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)" cursor.execute(sql, ('webmaster@python.org', 'very-secret')) # connection is not autocommit by default. So you must commit to save # your changes. connection.commit() with connection.cursor() as cursor: # Read a single record sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s" cursor.execute(sql, ('webmaster@python.org',)) result = cursor.fetchone() print(result) finally: connection.close() This example will print: .. code:: python {'password': 'very-secret', 'id': 1} PyMySQL-0.9.3/docs/source/user/index.rst000066400000000000000000000003301340615555200200520ustar00rootroot00000000000000User Guide ------------ The PyMySQL user guide explains how to install PyMySQL and how to contribute to the library as a developer. .. toctree:: :maxdepth: 1 installation examples resources development PyMySQL-0.9.3/docs/source/user/installation.rst000066400000000000000000000012211340615555200214440ustar00rootroot00000000000000.. _installation: ============ Installation ============ The last stable release is available on PyPI and can be installed with ``pip``:: $ python3 -m pip install PyMySQL To use "sha256_password" or "caching_sha2_password" for authenticate, you need to install additional dependency:: $ python3 -m pip install PyMySQL[rsa] Requirements ------------- * Python -- one of the following: - CPython_ >= 2.7 or >= 3.4 - Latest PyPy_ * MySQL Server -- one of the following: - MySQL_ >= 5.5 - MariaDB_ >= 5.5 .. _CPython: http://www.python.org/ .. _PyPy: http://pypy.org/ .. _MySQL: http://www.mysql.com/ .. _MariaDB: https://mariadb.org/ PyMySQL-0.9.3/docs/source/user/resources.rst000066400000000000000000000005131340615555200207600ustar00rootroot00000000000000.. _resources: ============ Resources ============ DB-API 2.0: http://www.python.org/dev/peps/pep-0249 MySQL Reference Manuals: http://dev.mysql.com/doc/ MySQL client/server protocol: http://dev.mysql.com/doc/internals/en/client-server-protocol.html PyMySQL mailing list: https://groups.google.com/forum/#!forum/pymysql-users PyMySQL-0.9.3/example.py000066400000000000000000000005001340615555200150070ustar00rootroot00000000000000#!/usr/bin/env python from __future__ import print_function import pymysql conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='', db='mysql') cur = conn.cursor() cur.execute("SELECT Host,User FROM user") print(cur.description) print() for row in cur: print(row) cur.close() conn.close() PyMySQL-0.9.3/pymysql/000077500000000000000000000000001340615555200145255ustar00rootroot00000000000000PyMySQL-0.9.3/pymysql/__init__.py000066400000000000000000000111741340615555200166420ustar00rootroot00000000000000""" PyMySQL: A pure-Python MySQL client library. Copyright (c) 2010-2016 PyMySQL contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import sys from ._compat import PY2 from .constants import FIELD_TYPE from .converters import escape_dict, escape_sequence, escape_string from .err import ( Warning, Error, InterfaceError, DataError, DatabaseError, OperationalError, IntegrityError, InternalError, NotSupportedError, ProgrammingError, MySQLError) from .times import ( Date, Time, Timestamp, DateFromTicks, TimeFromTicks, TimestampFromTicks) VERSION = (0, 9, 3, None) if VERSION[3] is not None: VERSION_STRING = "%d.%d.%d_%s" % VERSION else: VERSION_STRING = "%d.%d.%d" % VERSION[:3] threadsafety = 1 apilevel = "2.0" paramstyle = "pyformat" class DBAPISet(frozenset): def __ne__(self, other): if isinstance(other, set): return frozenset.__ne__(self, other) else: return other not in self def __eq__(self, other): if isinstance(other, frozenset): return frozenset.__eq__(self, other) else: return other in self def __hash__(self): return frozenset.__hash__(self) STRING = DBAPISet([FIELD_TYPE.ENUM, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING]) BINARY = DBAPISet([FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB, FIELD_TYPE.TINY_BLOB]) NUMBER = DBAPISet([FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT, FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG, FIELD_TYPE.TINY, FIELD_TYPE.YEAR]) DATE = DBAPISet([FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE]) TIME = DBAPISet([FIELD_TYPE.TIME]) TIMESTAMP = DBAPISet([FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME]) DATETIME = TIMESTAMP ROWID = DBAPISet() def Binary(x): """Return x as a binary type.""" if PY2: return bytearray(x) else: return bytes(x) def Connect(*args, **kwargs): """ Connect to the database; see connections.Connection.__init__() for more information. """ from .connections import Connection return Connection(*args, **kwargs) from . import connections as _orig_conn if _orig_conn.Connection.__init__.__doc__ is not None: Connect.__doc__ = _orig_conn.Connection.__init__.__doc__ del _orig_conn def get_client_info(): # for MySQLdb compatibility version = VERSION if VERSION[3] is None: version = VERSION[:3] return '.'.join(map(str, version)) connect = Connection = Connect # we include a doctored version_info here for MySQLdb compatibility version_info = (1, 3, 12, "final", 0) NULL = "NULL" __version__ = get_client_info() def thread_safe(): return True # match MySQLdb.thread_safe() def install_as_MySQLdb(): """ After this function is called, any application that imports MySQLdb or _mysql will unwittingly actually use pymysql. """ sys.modules["MySQLdb"] = sys.modules["_mysql"] = sys.modules["pymysql"] __all__ = [ 'BINARY', 'Binary', 'Connect', 'Connection', 'DATE', 'Date', 'Time', 'Timestamp', 'DateFromTicks', 'TimeFromTicks', 'TimestampFromTicks', 'DataError', 'DatabaseError', 'Error', 'FIELD_TYPE', 'IntegrityError', 'InterfaceError', 'InternalError', 'MySQLError', 'NULL', 'NUMBER', 'NotSupportedError', 'DBAPISet', 'OperationalError', 'ProgrammingError', 'ROWID', 'STRING', 'TIME', 'TIMESTAMP', 'Warning', 'apilevel', 'connect', 'connections', 'constants', 'converters', 'cursors', 'escape_dict', 'escape_sequence', 'escape_string', 'get_client_info', 'paramstyle', 'threadsafety', 'version_info', "install_as_MySQLdb", "NULL", "__version__", ] PyMySQL-0.9.3/pymysql/_auth.py000066400000000000000000000170501340615555200162020ustar00rootroot00000000000000""" Implements auth methods """ from ._compat import text_type, PY2 from .constants import CLIENT from .err import OperationalError from .util import byte2int, int2byte try: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.asymmetric import padding _have_cryptography = True except ImportError: _have_cryptography = False from functools import partial import hashlib import io import struct import warnings DEBUG = False SCRAMBLE_LENGTH = 20 sha1_new = partial(hashlib.new, 'sha1') # mysql_native_password # https://dev.mysql.com/doc/internals/en/secure-password-authentication.html#packet-Authentication::Native41 def scramble_native_password(password, message): """Scramble used for mysql_native_password""" if not password: return b'' stage1 = sha1_new(password).digest() stage2 = sha1_new(stage1).digest() s = sha1_new() s.update(message[:SCRAMBLE_LENGTH]) s.update(stage2) result = s.digest() return _my_crypt(result, stage1) def _my_crypt(message1, message2): result = bytearray(message1) if PY2: message2 = bytearray(message2) for i in range(len(result)): result[i] ^= message2[i] return bytes(result) # old_passwords support ported from libmysql/password.c # https://dev.mysql.com/doc/internals/en/old-password-authentication.html SCRAMBLE_LENGTH_323 = 8 class RandStruct_323(object): def __init__(self, seed1, seed2): self.max_value = 0x3FFFFFFF self.seed1 = seed1 % self.max_value self.seed2 = seed2 % self.max_value def my_rnd(self): self.seed1 = (self.seed1 * 3 + self.seed2) % self.max_value self.seed2 = (self.seed1 + self.seed2 + 33) % self.max_value return float(self.seed1) / float(self.max_value) def scramble_old_password(password, message): """Scramble for old_password""" warnings.warn("old password (for MySQL <4.1) is used. Upgrade your password with newer auth method.\n" "old password support will be removed in future PyMySQL version") hash_pass = _hash_password_323(password) hash_message = _hash_password_323(message[:SCRAMBLE_LENGTH_323]) hash_pass_n = struct.unpack(">LL", hash_pass) hash_message_n = struct.unpack(">LL", hash_message) rand_st = RandStruct_323( hash_pass_n[0] ^ hash_message_n[0], hash_pass_n[1] ^ hash_message_n[1] ) outbuf = io.BytesIO() for _ in range(min(SCRAMBLE_LENGTH_323, len(message))): outbuf.write(int2byte(int(rand_st.my_rnd() * 31) + 64)) extra = int2byte(int(rand_st.my_rnd() * 31)) out = outbuf.getvalue() outbuf = io.BytesIO() for c in out: outbuf.write(int2byte(byte2int(c) ^ byte2int(extra))) return outbuf.getvalue() def _hash_password_323(password): nr = 1345345333 add = 7 nr2 = 0x12345671 # x in py3 is numbers, p27 is chars for c in [byte2int(x) for x in password if x not in (' ', '\t', 32, 9)]: nr ^= (((nr & 63) + add) * c) + (nr << 8) & 0xFFFFFFFF nr2 = (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFF add = (add + c) & 0xFFFFFFFF r1 = nr & ((1 << 31) - 1) # kill sign bits r2 = nr2 & ((1 << 31) - 1) return struct.pack(">LL", r1, r2) # sha256_password def _roundtrip(conn, send_data): conn.write_packet(send_data) pkt = conn._read_packet() pkt.check_error() return pkt def _xor_password(password, salt): password_bytes = bytearray(password) salt = bytearray(salt) # for PY2 compat. salt_len = len(salt) for i in range(len(password_bytes)): password_bytes[i] ^= salt[i % salt_len] return bytes(password_bytes) def sha2_rsa_encrypt(password, salt, public_key): """Encrypt password with salt and public_key. Used for sha256_password and caching_sha2_password. """ if not _have_cryptography: raise RuntimeError("cryptography is required for sha256_password or caching_sha2_password") message = _xor_password(password + b'\0', salt) rsa_key = serialization.load_pem_public_key(public_key, default_backend()) return rsa_key.encrypt( message, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA1()), algorithm=hashes.SHA1(), label=None, ), ) def sha256_password_auth(conn, pkt): if conn._secure: if DEBUG: print("sha256: Sending plain password") data = conn.password + b'\0' return _roundtrip(conn, data) if pkt.is_auth_switch_request(): conn.salt = pkt.read_all() if not conn.server_public_key and conn.password: # Request server public key if DEBUG: print("sha256: Requesting server public key") pkt = _roundtrip(conn, b'\1') if pkt.is_extra_auth_data(): conn.server_public_key = pkt._data[1:] if DEBUG: print("Received public key:\n", conn.server_public_key.decode('ascii')) if conn.password: if not conn.server_public_key: raise OperationalError("Couldn't receive server's public key") data = sha2_rsa_encrypt(conn.password, conn.salt, conn.server_public_key) else: data = b'' return _roundtrip(conn, data) def scramble_caching_sha2(password, nonce): # (bytes, bytes) -> bytes """Scramble algorithm used in cached_sha2_password fast path. XOR(SHA256(password), SHA256(SHA256(SHA256(password)), nonce)) """ if not password: return b'' p1 = hashlib.sha256(password).digest() p2 = hashlib.sha256(p1).digest() p3 = hashlib.sha256(p2 + nonce).digest() res = bytearray(p1) if PY2: p3 = bytearray(p3) for i in range(len(p3)): res[i] ^= p3[i] return bytes(res) def caching_sha2_password_auth(conn, pkt): # No password fast path if not conn.password: return _roundtrip(conn, b'') if pkt.is_auth_switch_request(): # Try from fast auth if DEBUG: print("caching sha2: Trying fast path") conn.salt = pkt.read_all() scrambled = scramble_caching_sha2(conn.password, conn.salt) pkt = _roundtrip(conn, scrambled) # else: fast auth is tried in initial handshake if not pkt.is_extra_auth_data(): raise OperationalError( "caching sha2: Unknown packet for fast auth: %s" % pkt._data[:1] ) # magic numbers: # 2 - request public key # 3 - fast auth succeeded # 4 - need full auth pkt.advance(1) n = pkt.read_uint8() if n == 3: if DEBUG: print("caching sha2: succeeded by fast path.") pkt = conn._read_packet() pkt.check_error() # pkt must be OK packet return pkt if n != 4: raise OperationalError("caching sha2: Unknwon result for fast auth: %s" % n) if DEBUG: print("caching sha2: Trying full auth...") if conn._secure: if DEBUG: print("caching sha2: Sending plain password via secure connection") return _roundtrip(conn, conn.password + b'\0') if not conn.server_public_key: pkt = _roundtrip(conn, b'\x02') # Request public key if not pkt.is_extra_auth_data(): raise OperationalError( "caching sha2: Unknown packet for public key: %s" % pkt._data[:1] ) conn.server_public_key = pkt._data[1:] if DEBUG: print(conn.server_public_key.decode('ascii')) data = sha2_rsa_encrypt(conn.password, conn.salt, conn.server_public_key) pkt = _roundtrip(conn, data) PyMySQL-0.9.3/pymysql/_compat.py000066400000000000000000000007411340615555200165230ustar00rootroot00000000000000import sys PY2 = sys.version_info[0] == 2 PYPY = hasattr(sys, 'pypy_translation_info') JYTHON = sys.platform.startswith('java') IRONPYTHON = sys.platform == 'cli' CPYTHON = not PYPY and not JYTHON and not IRONPYTHON if PY2: import __builtin__ range_type = xrange text_type = unicode long_type = long str_type = basestring unichr = __builtin__.unichr else: range_type = range text_type = str long_type = int str_type = str unichr = chr PyMySQL-0.9.3/pymysql/_socketio.py000066400000000000000000000077211340615555200170650ustar00rootroot00000000000000""" SocketIO imported from socket module in Python 3. Copyright (c) 2001-2013 Python Software Foundation; All Rights Reserved. """ from socket import * import io import errno __all__ = ['SocketIO'] EINTR = errno.EINTR _blocking_errnos = (errno.EAGAIN, errno.EWOULDBLOCK) class SocketIO(io.RawIOBase): """Raw I/O implementation for stream sockets. This class supports the makefile() method on sockets. It provides the raw I/O interface on top of a socket object. """ # One might wonder why not let FileIO do the job instead. There are two # main reasons why FileIO is not adapted: # - it wouldn't work under Windows (where you can't used read() and # write() on a socket handle) # - it wouldn't work with socket timeouts (FileIO would ignore the # timeout and consider the socket non-blocking) # XXX More docs def __init__(self, sock, mode): if mode not in ("r", "w", "rw", "rb", "wb", "rwb"): raise ValueError("invalid mode: %r" % mode) io.RawIOBase.__init__(self) self._sock = sock if "b" not in mode: mode += "b" self._mode = mode self._reading = "r" in mode self._writing = "w" in mode self._timeout_occurred = False def readinto(self, b): """Read up to len(b) bytes into the writable buffer *b* and return the number of bytes read. If the socket is non-blocking and no bytes are available, None is returned. If *b* is non-empty, a 0 return value indicates that the connection was shutdown at the other end. """ self._checkClosed() self._checkReadable() if self._timeout_occurred: raise IOError("cannot read from timed out object") while True: try: return self._sock.recv_into(b) except timeout: self._timeout_occurred = True raise except error as e: n = e.args[0] if n == EINTR: continue if n in _blocking_errnos: return None raise def write(self, b): """Write the given bytes or bytearray object *b* to the socket and return the number of bytes written. This can be less than len(b) if not all data could be written. If the socket is non-blocking and no bytes could be written None is returned. """ self._checkClosed() self._checkWritable() try: return self._sock.send(b) except error as e: # XXX what about EINTR? if e.args[0] in _blocking_errnos: return None raise def readable(self): """True if the SocketIO is open for reading. """ if self.closed: raise ValueError("I/O operation on closed socket.") return self._reading def writable(self): """True if the SocketIO is open for writing. """ if self.closed: raise ValueError("I/O operation on closed socket.") return self._writing def seekable(self): """True if the SocketIO is open for seeking. """ if self.closed: raise ValueError("I/O operation on closed socket.") return super().seekable() def fileno(self): """Return the file descriptor of the underlying socket. """ self._checkClosed() return self._sock.fileno() @property def name(self): if not self.closed: return self.fileno() else: return -1 @property def mode(self): return self._mode def close(self): """Close the SocketIO object. This doesn't close the underlying socket, except if all references to it have disappeared. """ if self.closed: return io.RawIOBase.close(self) self._sock._decref_socketios() self._sock = None PyMySQL-0.9.3/pymysql/charset.py000066400000000000000000000241321340615555200165320ustar00rootroot00000000000000MBLENGTH = { 8:1, 33:3, 88:2, 91:2 } class Charset(object): def __init__(self, id, name, collation, is_default): self.id, self.name, self.collation = id, name, collation self.is_default = is_default == 'Yes' def __repr__(self): return "Charset(id=%s, name=%r, collation=%r)" % ( self.id, self.name, self.collation) @property def encoding(self): name = self.name if name in ('utf8mb4', 'utf8mb3'): return 'utf8' return name @property def is_binary(self): return self.id == 63 class Charsets: def __init__(self): self._by_id = {} self._by_name = {} def add(self, c): self._by_id[c.id] = c if c.is_default: self._by_name[c.name] = c def by_id(self, id): return self._by_id[id] def by_name(self, name): return self._by_name.get(name.lower()) _charsets = Charsets() """ Generated with: mysql -N -s -e "select id, character_set_name, collation_name, is_default from information_schema.collations order by id;" | python -c "import sys for l in sys.stdin.readlines(): id, name, collation, is_default = l.split(chr(9)) print '_charsets.add(Charset(%s, \'%s\', \'%s\', \'%s\'))' \ % (id, name, collation, is_default.strip()) " """ _charsets.add(Charset(1, 'big5', 'big5_chinese_ci', 'Yes')) _charsets.add(Charset(2, 'latin2', 'latin2_czech_cs', '')) _charsets.add(Charset(3, 'dec8', 'dec8_swedish_ci', 'Yes')) _charsets.add(Charset(4, 'cp850', 'cp850_general_ci', 'Yes')) _charsets.add(Charset(5, 'latin1', 'latin1_german1_ci', '')) _charsets.add(Charset(6, 'hp8', 'hp8_english_ci', 'Yes')) _charsets.add(Charset(7, 'koi8r', 'koi8r_general_ci', 'Yes')) _charsets.add(Charset(8, 'latin1', 'latin1_swedish_ci', 'Yes')) _charsets.add(Charset(9, 'latin2', 'latin2_general_ci', 'Yes')) _charsets.add(Charset(10, 'swe7', 'swe7_swedish_ci', 'Yes')) _charsets.add(Charset(11, 'ascii', 'ascii_general_ci', 'Yes')) _charsets.add(Charset(12, 'ujis', 'ujis_japanese_ci', 'Yes')) _charsets.add(Charset(13, 'sjis', 'sjis_japanese_ci', 'Yes')) _charsets.add(Charset(14, 'cp1251', 'cp1251_bulgarian_ci', '')) _charsets.add(Charset(15, 'latin1', 'latin1_danish_ci', '')) _charsets.add(Charset(16, 'hebrew', 'hebrew_general_ci', 'Yes')) _charsets.add(Charset(18, 'tis620', 'tis620_thai_ci', 'Yes')) _charsets.add(Charset(19, 'euckr', 'euckr_korean_ci', 'Yes')) _charsets.add(Charset(20, 'latin7', 'latin7_estonian_cs', '')) _charsets.add(Charset(21, 'latin2', 'latin2_hungarian_ci', '')) _charsets.add(Charset(22, 'koi8u', 'koi8u_general_ci', 'Yes')) _charsets.add(Charset(23, 'cp1251', 'cp1251_ukrainian_ci', '')) _charsets.add(Charset(24, 'gb2312', 'gb2312_chinese_ci', 'Yes')) _charsets.add(Charset(25, 'greek', 'greek_general_ci', 'Yes')) _charsets.add(Charset(26, 'cp1250', 'cp1250_general_ci', 'Yes')) _charsets.add(Charset(27, 'latin2', 'latin2_croatian_ci', '')) _charsets.add(Charset(28, 'gbk', 'gbk_chinese_ci', 'Yes')) _charsets.add(Charset(29, 'cp1257', 'cp1257_lithuanian_ci', '')) _charsets.add(Charset(30, 'latin5', 'latin5_turkish_ci', 'Yes')) _charsets.add(Charset(31, 'latin1', 'latin1_german2_ci', '')) _charsets.add(Charset(32, 'armscii8', 'armscii8_general_ci', 'Yes')) _charsets.add(Charset(33, 'utf8', 'utf8_general_ci', 'Yes')) _charsets.add(Charset(34, 'cp1250', 'cp1250_czech_cs', '')) _charsets.add(Charset(36, 'cp866', 'cp866_general_ci', 'Yes')) _charsets.add(Charset(37, 'keybcs2', 'keybcs2_general_ci', 'Yes')) _charsets.add(Charset(38, 'macce', 'macce_general_ci', 'Yes')) _charsets.add(Charset(39, 'macroman', 'macroman_general_ci', 'Yes')) _charsets.add(Charset(40, 'cp852', 'cp852_general_ci', 'Yes')) _charsets.add(Charset(41, 'latin7', 'latin7_general_ci', 'Yes')) _charsets.add(Charset(42, 'latin7', 'latin7_general_cs', '')) _charsets.add(Charset(43, 'macce', 'macce_bin', '')) _charsets.add(Charset(44, 'cp1250', 'cp1250_croatian_ci', '')) _charsets.add(Charset(45, 'utf8mb4', 'utf8mb4_general_ci', 'Yes')) _charsets.add(Charset(46, 'utf8mb4', 'utf8mb4_bin', '')) _charsets.add(Charset(47, 'latin1', 'latin1_bin', '')) _charsets.add(Charset(48, 'latin1', 'latin1_general_ci', '')) _charsets.add(Charset(49, 'latin1', 'latin1_general_cs', '')) _charsets.add(Charset(50, 'cp1251', 'cp1251_bin', '')) _charsets.add(Charset(51, 'cp1251', 'cp1251_general_ci', 'Yes')) _charsets.add(Charset(52, 'cp1251', 'cp1251_general_cs', '')) _charsets.add(Charset(53, 'macroman', 'macroman_bin', '')) _charsets.add(Charset(57, 'cp1256', 'cp1256_general_ci', 'Yes')) _charsets.add(Charset(58, 'cp1257', 'cp1257_bin', '')) _charsets.add(Charset(59, 'cp1257', 'cp1257_general_ci', 'Yes')) _charsets.add(Charset(63, 'binary', 'binary', 'Yes')) _charsets.add(Charset(64, 'armscii8', 'armscii8_bin', '')) _charsets.add(Charset(65, 'ascii', 'ascii_bin', '')) _charsets.add(Charset(66, 'cp1250', 'cp1250_bin', '')) _charsets.add(Charset(67, 'cp1256', 'cp1256_bin', '')) _charsets.add(Charset(68, 'cp866', 'cp866_bin', '')) _charsets.add(Charset(69, 'dec8', 'dec8_bin', '')) _charsets.add(Charset(70, 'greek', 'greek_bin', '')) _charsets.add(Charset(71, 'hebrew', 'hebrew_bin', '')) _charsets.add(Charset(72, 'hp8', 'hp8_bin', '')) _charsets.add(Charset(73, 'keybcs2', 'keybcs2_bin', '')) _charsets.add(Charset(74, 'koi8r', 'koi8r_bin', '')) _charsets.add(Charset(75, 'koi8u', 'koi8u_bin', '')) _charsets.add(Charset(76, 'utf8', 'utf8_tolower_ci', '')) _charsets.add(Charset(77, 'latin2', 'latin2_bin', '')) _charsets.add(Charset(78, 'latin5', 'latin5_bin', '')) _charsets.add(Charset(79, 'latin7', 'latin7_bin', '')) _charsets.add(Charset(80, 'cp850', 'cp850_bin', '')) _charsets.add(Charset(81, 'cp852', 'cp852_bin', '')) _charsets.add(Charset(82, 'swe7', 'swe7_bin', '')) _charsets.add(Charset(83, 'utf8', 'utf8_bin', '')) _charsets.add(Charset(84, 'big5', 'big5_bin', '')) _charsets.add(Charset(85, 'euckr', 'euckr_bin', '')) _charsets.add(Charset(86, 'gb2312', 'gb2312_bin', '')) _charsets.add(Charset(87, 'gbk', 'gbk_bin', '')) _charsets.add(Charset(88, 'sjis', 'sjis_bin', '')) _charsets.add(Charset(89, 'tis620', 'tis620_bin', '')) _charsets.add(Charset(91, 'ujis', 'ujis_bin', '')) _charsets.add(Charset(92, 'geostd8', 'geostd8_general_ci', 'Yes')) _charsets.add(Charset(93, 'geostd8', 'geostd8_bin', '')) _charsets.add(Charset(94, 'latin1', 'latin1_spanish_ci', '')) _charsets.add(Charset(95, 'cp932', 'cp932_japanese_ci', 'Yes')) _charsets.add(Charset(96, 'cp932', 'cp932_bin', '')) _charsets.add(Charset(97, 'eucjpms', 'eucjpms_japanese_ci', 'Yes')) _charsets.add(Charset(98, 'eucjpms', 'eucjpms_bin', '')) _charsets.add(Charset(99, 'cp1250', 'cp1250_polish_ci', '')) _charsets.add(Charset(192, 'utf8', 'utf8_unicode_ci', '')) _charsets.add(Charset(193, 'utf8', 'utf8_icelandic_ci', '')) _charsets.add(Charset(194, 'utf8', 'utf8_latvian_ci', '')) _charsets.add(Charset(195, 'utf8', 'utf8_romanian_ci', '')) _charsets.add(Charset(196, 'utf8', 'utf8_slovenian_ci', '')) _charsets.add(Charset(197, 'utf8', 'utf8_polish_ci', '')) _charsets.add(Charset(198, 'utf8', 'utf8_estonian_ci', '')) _charsets.add(Charset(199, 'utf8', 'utf8_spanish_ci', '')) _charsets.add(Charset(200, 'utf8', 'utf8_swedish_ci', '')) _charsets.add(Charset(201, 'utf8', 'utf8_turkish_ci', '')) _charsets.add(Charset(202, 'utf8', 'utf8_czech_ci', '')) _charsets.add(Charset(203, 'utf8', 'utf8_danish_ci', '')) _charsets.add(Charset(204, 'utf8', 'utf8_lithuanian_ci', '')) _charsets.add(Charset(205, 'utf8', 'utf8_slovak_ci', '')) _charsets.add(Charset(206, 'utf8', 'utf8_spanish2_ci', '')) _charsets.add(Charset(207, 'utf8', 'utf8_roman_ci', '')) _charsets.add(Charset(208, 'utf8', 'utf8_persian_ci', '')) _charsets.add(Charset(209, 'utf8', 'utf8_esperanto_ci', '')) _charsets.add(Charset(210, 'utf8', 'utf8_hungarian_ci', '')) _charsets.add(Charset(211, 'utf8', 'utf8_sinhala_ci', '')) _charsets.add(Charset(212, 'utf8', 'utf8_german2_ci', '')) _charsets.add(Charset(213, 'utf8', 'utf8_croatian_ci', '')) _charsets.add(Charset(214, 'utf8', 'utf8_unicode_520_ci', '')) _charsets.add(Charset(215, 'utf8', 'utf8_vietnamese_ci', '')) _charsets.add(Charset(223, 'utf8', 'utf8_general_mysql500_ci', '')) _charsets.add(Charset(224, 'utf8mb4', 'utf8mb4_unicode_ci', '')) _charsets.add(Charset(225, 'utf8mb4', 'utf8mb4_icelandic_ci', '')) _charsets.add(Charset(226, 'utf8mb4', 'utf8mb4_latvian_ci', '')) _charsets.add(Charset(227, 'utf8mb4', 'utf8mb4_romanian_ci', '')) _charsets.add(Charset(228, 'utf8mb4', 'utf8mb4_slovenian_ci', '')) _charsets.add(Charset(229, 'utf8mb4', 'utf8mb4_polish_ci', '')) _charsets.add(Charset(230, 'utf8mb4', 'utf8mb4_estonian_ci', '')) _charsets.add(Charset(231, 'utf8mb4', 'utf8mb4_spanish_ci', '')) _charsets.add(Charset(232, 'utf8mb4', 'utf8mb4_swedish_ci', '')) _charsets.add(Charset(233, 'utf8mb4', 'utf8mb4_turkish_ci', '')) _charsets.add(Charset(234, 'utf8mb4', 'utf8mb4_czech_ci', '')) _charsets.add(Charset(235, 'utf8mb4', 'utf8mb4_danish_ci', '')) _charsets.add(Charset(236, 'utf8mb4', 'utf8mb4_lithuanian_ci', '')) _charsets.add(Charset(237, 'utf8mb4', 'utf8mb4_slovak_ci', '')) _charsets.add(Charset(238, 'utf8mb4', 'utf8mb4_spanish2_ci', '')) _charsets.add(Charset(239, 'utf8mb4', 'utf8mb4_roman_ci', '')) _charsets.add(Charset(240, 'utf8mb4', 'utf8mb4_persian_ci', '')) _charsets.add(Charset(241, 'utf8mb4', 'utf8mb4_esperanto_ci', '')) _charsets.add(Charset(242, 'utf8mb4', 'utf8mb4_hungarian_ci', '')) _charsets.add(Charset(243, 'utf8mb4', 'utf8mb4_sinhala_ci', '')) _charsets.add(Charset(244, 'utf8mb4', 'utf8mb4_german2_ci', '')) _charsets.add(Charset(245, 'utf8mb4', 'utf8mb4_croatian_ci', '')) _charsets.add(Charset(246, 'utf8mb4', 'utf8mb4_unicode_520_ci', '')) _charsets.add(Charset(247, 'utf8mb4', 'utf8mb4_vietnamese_ci', '')) _charsets.add(Charset(248, 'gb18030', 'gb18030_chinese_ci', 'Yes')) _charsets.add(Charset(249, 'gb18030', 'gb18030_bin', '')) _charsets.add(Charset(250, 'gb18030', 'gb18030_unicode_520_ci', '')) _charsets.add(Charset(255, 'utf8mb4', 'utf8mb4_0900_ai_ci', '')) charset_by_name = _charsets.by_name charset_by_id = _charsets.by_id #TODO: remove this def charset_to_encoding(name): """Convert MySQL's charset name to Python's codec name""" if name in ('utf8mb4', 'utf8mb3'): return 'utf8' return name PyMySQL-0.9.3/pymysql/connections.py000066400000000000000000001376211340615555200174330ustar00rootroot00000000000000# Python implementation of the MySQL client-server protocol # http://dev.mysql.com/doc/internals/en/client-server-protocol.html # Error codes: # http://dev.mysql.com/doc/refman/5.5/en/error-messages-client.html from __future__ import print_function from ._compat import PY2, range_type, text_type, str_type, JYTHON, IRONPYTHON import errno import io import os import socket import struct import sys import traceback import warnings from . import _auth from .charset import charset_by_name, charset_by_id from .constants import CLIENT, COMMAND, CR, FIELD_TYPE, SERVER_STATUS from . import converters from .cursors import Cursor from .optionfile import Parser from .protocol import ( dump_packet, MysqlPacket, FieldDescriptorPacket, OKPacketWrapper, EOFPacketWrapper, LoadLocalPacketWrapper ) from .util import byte2int, int2byte from . import err, VERSION_STRING try: import ssl SSL_ENABLED = True except ImportError: ssl = None SSL_ENABLED = False try: import getpass DEFAULT_USER = getpass.getuser() del getpass except (ImportError, KeyError): # KeyError occurs when there's no entry in OS database for a current user. DEFAULT_USER = None DEBUG = False _py_version = sys.version_info[:2] if PY2: pass elif _py_version < (3, 6): # See http://bugs.python.org/issue24870 _surrogateescape_table = [chr(i) if i < 0x80 else chr(i + 0xdc00) for i in range(256)] def _fast_surrogateescape(s): return s.decode('latin1').translate(_surrogateescape_table) else: def _fast_surrogateescape(s): return s.decode('ascii', 'surrogateescape') # socket.makefile() in Python 2 is not usable because very inefficient and # bad behavior about timeout. # XXX: ._socketio doesn't work under IronPython. if PY2 and not IRONPYTHON: # read method of file-like returned by sock.makefile() is very slow. # So we copy io-based one from Python 3. from ._socketio import SocketIO def _makefile(sock, mode): return io.BufferedReader(SocketIO(sock, mode)) else: # socket.makefile in Python 3 is nice. def _makefile(sock, mode): return sock.makefile(mode) TEXT_TYPES = { FIELD_TYPE.BIT, FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB, FIELD_TYPE.STRING, FIELD_TYPE.TINY_BLOB, FIELD_TYPE.VAR_STRING, FIELD_TYPE.VARCHAR, FIELD_TYPE.GEOMETRY, } DEFAULT_CHARSET = 'utf8mb4' MAX_PACKET_LEN = 2**24-1 def pack_int24(n): return struct.pack('`_ in the specification. """ _sock = None _auth_plugin_name = '' _closed = False _secure = False def __init__(self, host=None, user=None, password="", database=None, port=0, unix_socket=None, charset='', sql_mode=None, read_default_file=None, conv=None, use_unicode=None, client_flag=0, cursorclass=Cursor, init_command=None, connect_timeout=10, ssl=None, read_default_group=None, compress=None, named_pipe=None, autocommit=False, db=None, passwd=None, local_infile=False, max_allowed_packet=16*1024*1024, defer_connect=False, auth_plugin_map=None, read_timeout=None, write_timeout=None, bind_address=None, binary_prefix=False, program_name=None, server_public_key=None): if use_unicode is None and sys.version_info[0] > 2: use_unicode = True if db is not None and database is None: database = db if passwd is not None and not password: password = passwd if compress or named_pipe: raise NotImplementedError("compress and named_pipe arguments are not supported") self._local_infile = bool(local_infile) if self._local_infile: client_flag |= CLIENT.LOCAL_FILES if read_default_group and not read_default_file: if sys.platform.startswith("win"): read_default_file = "c:\\my.ini" else: read_default_file = "/etc/my.cnf" if read_default_file: if not read_default_group: read_default_group = "client" cfg = Parser() cfg.read(os.path.expanduser(read_default_file)) def _config(key, arg): if arg: return arg try: return cfg.get(read_default_group, key) except Exception: return arg user = _config("user", user) password = _config("password", password) host = _config("host", host) database = _config("database", database) unix_socket = _config("socket", unix_socket) port = int(_config("port", port)) bind_address = _config("bind-address", bind_address) charset = _config("default-character-set", charset) if not ssl: ssl = {} if isinstance(ssl, dict): for key in ["ca", "capath", "cert", "key", "cipher"]: value = _config("ssl-" + key, ssl.get(key)) if value: ssl[key] = value self.ssl = False if ssl: if not SSL_ENABLED: raise NotImplementedError("ssl module not found") self.ssl = True client_flag |= CLIENT.SSL self.ctx = self._create_ssl_ctx(ssl) self.host = host or "localhost" self.port = port or 3306 self.user = user or DEFAULT_USER self.password = password or b"" if isinstance(self.password, text_type): self.password = self.password.encode('latin1') self.db = database self.unix_socket = unix_socket self.bind_address = bind_address if not (0 < connect_timeout <= 31536000): raise ValueError("connect_timeout should be >0 and <=31536000") self.connect_timeout = connect_timeout or None if read_timeout is not None and read_timeout <= 0: raise ValueError("read_timeout should be >= 0") self._read_timeout = read_timeout if write_timeout is not None and write_timeout <= 0: raise ValueError("write_timeout should be >= 0") self._write_timeout = write_timeout if charset: self.charset = charset self.use_unicode = True else: self.charset = DEFAULT_CHARSET self.use_unicode = False if use_unicode is not None: self.use_unicode = use_unicode self.encoding = charset_by_name(self.charset).encoding client_flag |= CLIENT.CAPABILITIES if self.db: client_flag |= CLIENT.CONNECT_WITH_DB self.client_flag = client_flag self.cursorclass = cursorclass self._result = None self._affected_rows = 0 self.host_info = "Not connected" # specified autocommit mode. None means use server default. self.autocommit_mode = autocommit if conv is None: conv = converters.conversions # Need for MySQLdb compatibility. self.encoders = {k: v for (k, v) in conv.items() if type(k) is not int} self.decoders = {k: v for (k, v) in conv.items() if type(k) is int} self.sql_mode = sql_mode self.init_command = init_command self.max_allowed_packet = max_allowed_packet self._auth_plugin_map = auth_plugin_map or {} self._binary_prefix = binary_prefix self.server_public_key = server_public_key self._connect_attrs = { '_client_name': 'pymysql', '_pid': str(os.getpid()), '_client_version': VERSION_STRING, } if program_name: self._connect_attrs["program_name"] = program_name if defer_connect: self._sock = None else: self.connect() def _create_ssl_ctx(self, sslp): if isinstance(sslp, ssl.SSLContext): return sslp ca = sslp.get('ca') capath = sslp.get('capath') hasnoca = ca is None and capath is None ctx = ssl.create_default_context(cafile=ca, capath=capath) ctx.check_hostname = not hasnoca and sslp.get('check_hostname', True) ctx.verify_mode = ssl.CERT_NONE if hasnoca else ssl.CERT_REQUIRED if 'cert' in sslp: ctx.load_cert_chain(sslp['cert'], keyfile=sslp.get('key')) if 'cipher' in sslp: ctx.set_ciphers(sslp['cipher']) ctx.options |= ssl.OP_NO_SSLv2 ctx.options |= ssl.OP_NO_SSLv3 return ctx def close(self): """ Send the quit message and close the socket. See `Connection.close() `_ in the specification. :raise Error: If the connection is already closed. """ if self._closed: raise err.Error("Already closed") self._closed = True if self._sock is None: return send_data = struct.pack('`_ in the specification. """ self._execute_command(COMMAND.COM_QUERY, "COMMIT") self._read_ok_packet() def rollback(self): """ Roll back the current transaction. See `Connection.rollback() `_ in the specification. """ self._execute_command(COMMAND.COM_QUERY, "ROLLBACK") self._read_ok_packet() def show_warnings(self): """Send the "SHOW WARNINGS" SQL command.""" self._execute_command(COMMAND.COM_QUERY, "SHOW WARNINGS") result = MySQLResult(self) result.read() return result.rows def select_db(self, db): """ Set current db. :param db: The name of the db. """ self._execute_command(COMMAND.COM_INIT_DB, db) self._read_ok_packet() def escape(self, obj, mapping=None): """Escape whatever value you pass to it. Non-standard, for internal use; do not use this in your applications. """ if isinstance(obj, str_type): return "'" + self.escape_string(obj) + "'" if isinstance(obj, (bytes, bytearray)): ret = self._quote_bytes(obj) if self._binary_prefix: ret = "_binary" + ret return ret return converters.escape_item(obj, self.charset, mapping=mapping) def literal(self, obj): """Alias for escape() Non-standard, for internal use; do not use this in your applications. """ return self.escape(obj, self.encoders) def escape_string(self, s): if (self.server_status & SERVER_STATUS.SERVER_STATUS_NO_BACKSLASH_ESCAPES): return s.replace("'", "''") return converters.escape_string(s) def _quote_bytes(self, s): if (self.server_status & SERVER_STATUS.SERVER_STATUS_NO_BACKSLASH_ESCAPES): return "'%s'" % (_fast_surrogateescape(s.replace(b"'", b"''")),) return converters.escape_bytes(s) def cursor(self, cursor=None): """ Create a new cursor to execute queries with. :param cursor: The type of cursor to create; one of :py:class:`Cursor`, :py:class:`SSCursor`, :py:class:`DictCursor`, or :py:class:`SSDictCursor`. None means use Cursor. """ if cursor: return cursor(self) return self.cursorclass(self) def __enter__(self): """Context manager that returns a Cursor""" warnings.warn( "Context manager API of Connection object is deprecated; Use conn.begin()", DeprecationWarning) return self.cursor() def __exit__(self, exc, value, traceback): """On successful exit, commit. On exception, rollback""" if exc: self.rollback() else: self.commit() # The following methods are INTERNAL USE ONLY (called from Cursor) def query(self, sql, unbuffered=False): # if DEBUG: # print("DEBUG: sending query:", sql) if isinstance(sql, text_type) and not (JYTHON or IRONPYTHON): if PY2: sql = sql.encode(self.encoding) else: sql = sql.encode(self.encoding, 'surrogateescape') self._execute_command(COMMAND.COM_QUERY, sql) self._affected_rows = self._read_query_result(unbuffered=unbuffered) return self._affected_rows def next_result(self, unbuffered=False): self._affected_rows = self._read_query_result(unbuffered=unbuffered) return self._affected_rows def affected_rows(self): return self._affected_rows def kill(self, thread_id): arg = struct.pack('= 5: self.client_flag |= CLIENT.MULTI_RESULTS if self.user is None: raise ValueError("Did not specify a username") charset_id = charset_by_name(self.charset).id if isinstance(self.user, text_type): self.user = self.user.encode(self.encoding) data_init = struct.pack('=5.0) data += authresp + b'\0' if self.db and self.server_capabilities & CLIENT.CONNECT_WITH_DB: if isinstance(self.db, text_type): self.db = self.db.encode(self.encoding) data += self.db + b'\0' if self.server_capabilities & CLIENT.PLUGIN_AUTH: data += (plugin_name or b'') + b'\0' if self.server_capabilities & CLIENT.CONNECT_ATTRS: connect_attrs = b'' for k, v in self._connect_attrs.items(): k = k.encode('utf-8') connect_attrs += struct.pack('B', len(k)) + k v = v.encode('utf-8') connect_attrs += struct.pack('B', len(v)) + v data += struct.pack('B', len(connect_attrs)) + connect_attrs self.write_packet(data) auth_packet = self._read_packet() # if authentication method isn't accepted the first byte # will have the octet 254 if auth_packet.is_auth_switch_request(): if DEBUG: print("received auth switch") # https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest auth_packet.read_uint8() # 0xfe packet identifier plugin_name = auth_packet.read_string() if self.server_capabilities & CLIENT.PLUGIN_AUTH and plugin_name is not None: auth_packet = self._process_auth(plugin_name, auth_packet) else: # send legacy handshake data = _auth.scramble_old_password(self.password, self.salt) + b'\0' self.write_packet(data) auth_packet = self._read_packet() elif auth_packet.is_extra_auth_data(): if DEBUG: print("received extra data") # https://dev.mysql.com/doc/internals/en/successful-authentication.html if self._auth_plugin_name == "caching_sha2_password": auth_packet = _auth.caching_sha2_password_auth(self, auth_packet) elif self._auth_plugin_name == "sha256_password": auth_packet = _auth.sha256_password_auth(self, auth_packet) else: raise err.OperationalError("Received extra packet for auth method %r", self._auth_plugin_name) if DEBUG: print("Succeed to auth") def _process_auth(self, plugin_name, auth_packet): handler = self._get_auth_plugin_handler(plugin_name) if handler: try: return handler.authenticate(auth_packet) except AttributeError: if plugin_name != b'dialog': raise err.OperationalError(2059, "Authentication plugin '%s'" " not loaded: - %r missing authenticate method" % (plugin_name, type(handler))) if plugin_name == b"caching_sha2_password": return _auth.caching_sha2_password_auth(self, auth_packet) elif plugin_name == b"sha256_password": return _auth.sha256_password_auth(self, auth_packet) elif plugin_name == b"mysql_native_password": data = _auth.scramble_native_password(self.password, auth_packet.read_all()) elif plugin_name == b"mysql_old_password": data = _auth.scramble_old_password(self.password, auth_packet.read_all()) + b'\0' elif plugin_name == b"mysql_clear_password": # https://dev.mysql.com/doc/internals/en/clear-text-authentication.html data = self.password + b'\0' elif plugin_name == b"dialog": pkt = auth_packet while True: flag = pkt.read_uint8() echo = (flag & 0x06) == 0x02 last = (flag & 0x01) == 0x01 prompt = pkt.read_all() if prompt == b"Password: ": self.write_packet(self.password + b'\0') elif handler: resp = 'no response - TypeError within plugin.prompt method' try: resp = handler.prompt(echo, prompt) self.write_packet(resp + b'\0') except AttributeError: raise err.OperationalError(2059, "Authentication plugin '%s'" \ " not loaded: - %r missing prompt method" % (plugin_name, handler)) except TypeError: raise err.OperationalError(2061, "Authentication plugin '%s'" \ " %r didn't respond with string. Returned '%r' to prompt %r" % (plugin_name, handler, resp, prompt)) else: raise err.OperationalError(2059, "Authentication plugin '%s' (%r) not configured" % (plugin_name, handler)) pkt = self._read_packet() pkt.check_error() if pkt.is_ok_packet() or last: break return pkt else: raise err.OperationalError(2059, "Authentication plugin '%s' not configured" % plugin_name) self.write_packet(data) pkt = self._read_packet() pkt.check_error() return pkt def _get_auth_plugin_handler(self, plugin_name): plugin_class = self._auth_plugin_map.get(plugin_name) if not plugin_class and isinstance(plugin_name, bytes): plugin_class = self._auth_plugin_map.get(plugin_name.decode('ascii')) if plugin_class: try: handler = plugin_class(self) except TypeError: raise err.OperationalError(2059, "Authentication plugin '%s'" " not loaded: - %r cannot be constructed with connection object" % (plugin_name, plugin_class)) else: handler = None return handler # _mysql support def thread_id(self): return self.server_thread_id[0] def character_set_name(self): return self.charset def get_host_info(self): return self.host_info def get_proto_info(self): return self.protocol_version def _get_server_information(self): i = 0 packet = self._read_packet() data = packet.get_all_data() self.protocol_version = byte2int(data[i:i+1]) i += 1 server_end = data.find(b'\0', i) self.server_version = data[i:server_end].decode('latin1') i = server_end + 1 self.server_thread_id = struct.unpack('= i + 6: lang, stat, cap_h, salt_len = struct.unpack('= i + salt_len: # salt_len includes auth_plugin_data_part_1 and filler self.salt += data[i:i+salt_len] i += salt_len i+=1 # AUTH PLUGIN NAME may appear here. if self.server_capabilities & CLIENT.PLUGIN_AUTH and len(data) >= i: # Due to Bug#59453 the auth-plugin-name is missing the terminating # NUL-char in versions prior to 5.5.10 and 5.6.2. # ref: https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake # didn't use version checks as mariadb is corrected and reports # earlier than those two. server_end = data.find(b'\0', i) if server_end < 0: # pragma: no cover - very specific upstream bug # not found \0 and last field so take it all self._auth_plugin_name = data[i:].decode('utf-8') else: self._auth_plugin_name = data[i:server_end].decode('utf-8') def get_server_info(self): return self.server_version Warning = err.Warning Error = err.Error InterfaceError = err.InterfaceError DatabaseError = err.DatabaseError DataError = err.DataError OperationalError = err.OperationalError IntegrityError = err.IntegrityError InternalError = err.InternalError ProgrammingError = err.ProgrammingError NotSupportedError = err.NotSupportedError class MySQLResult(object): def __init__(self, connection): """ :type connection: Connection """ self.connection = connection self.affected_rows = None self.insert_id = None self.server_status = None self.warning_count = 0 self.message = None self.field_count = 0 self.description = None self.rows = None self.has_next = None self.unbuffered_active = False def __del__(self): if self.unbuffered_active: self._finish_unbuffered_query() def read(self): try: first_packet = self.connection._read_packet() if first_packet.is_ok_packet(): self._read_ok_packet(first_packet) elif first_packet.is_load_local_packet(): self._read_load_local_packet(first_packet) else: self._read_result_packet(first_packet) finally: self.connection = None def init_unbuffered_query(self): """ :raise OperationalError: If the connection to the MySQL server is lost. :raise InternalError: """ self.unbuffered_active = True first_packet = self.connection._read_packet() if first_packet.is_ok_packet(): self._read_ok_packet(first_packet) self.unbuffered_active = False self.connection = None elif first_packet.is_load_local_packet(): self._read_load_local_packet(first_packet) self.unbuffered_active = False self.connection = None else: self.field_count = first_packet.read_length_encoded_integer() self._get_descriptions() # Apparently, MySQLdb picks this number because it's the maximum # value of a 64bit unsigned integer. Since we're emulating MySQLdb, # we set it to this instead of None, which would be preferred. self.affected_rows = 18446744073709551615 def _read_ok_packet(self, first_packet): ok_packet = OKPacketWrapper(first_packet) self.affected_rows = ok_packet.affected_rows self.insert_id = ok_packet.insert_id self.server_status = ok_packet.server_status self.warning_count = ok_packet.warning_count self.message = ok_packet.message self.has_next = ok_packet.has_next def _read_load_local_packet(self, first_packet): if not self.connection._local_infile: raise RuntimeError( "**WARN**: Received LOAD_LOCAL packet but local_infile option is false.") load_packet = LoadLocalPacketWrapper(first_packet) sender = LoadLocalFile(load_packet.filename, self.connection) try: sender.send_data() except: self.connection._read_packet() # skip ok packet raise ok_packet = self.connection._read_packet() if not ok_packet.is_ok_packet(): # pragma: no cover - upstream induced protocol error raise err.OperationalError(2014, "Commands Out of Sync") self._read_ok_packet(ok_packet) def _check_packet_is_eof(self, packet): if not packet.is_eof_packet(): return False #TODO: Support CLIENT.DEPRECATE_EOF # 1) Add DEPRECATE_EOF to CAPABILITIES # 2) Mask CAPABILITIES with server_capabilities # 3) if server_capabilities & CLIENT.DEPRECATE_EOF: use OKPacketWrapper instead of EOFPacketWrapper wp = EOFPacketWrapper(packet) self.warning_count = wp.warning_count self.has_next = wp.has_next return True def _read_result_packet(self, first_packet): self.field_count = first_packet.read_length_encoded_integer() self._get_descriptions() self._read_rowdata_packet() def _read_rowdata_packet_unbuffered(self): # Check if in an active query if not self.unbuffered_active: return # EOF packet = self.connection._read_packet() if self._check_packet_is_eof(packet): self.unbuffered_active = False self.connection = None self.rows = None return row = self._read_row_from_packet(packet) self.affected_rows = 1 self.rows = (row,) # rows should tuple of row for MySQL-python compatibility. return row def _finish_unbuffered_query(self): # After much reading on the MySQL protocol, it appears that there is, # in fact, no way to stop MySQL from sending all the data after # executing a query, so we just spin, and wait for an EOF packet. while self.unbuffered_active: packet = self.connection._read_packet() if self._check_packet_is_eof(packet): self.unbuffered_active = False self.connection = None # release reference to kill cyclic reference. def _read_rowdata_packet(self): """Read a rowdata packet for each data row in the result set.""" rows = [] while True: packet = self.connection._read_packet() if self._check_packet_is_eof(packet): self.connection = None # release reference to kill cyclic reference. break rows.append(self._read_row_from_packet(packet)) self.affected_rows = len(rows) self.rows = tuple(rows) def _read_row_from_packet(self, packet): row = [] for encoding, converter in self.converters: try: data = packet.read_length_coded_string() except IndexError: # No more columns in this row # See https://github.com/PyMySQL/PyMySQL/pull/434 break if data is not None: if encoding is not None: data = data.decode(encoding) if DEBUG: print("DEBUG: DATA = ", data) if converter is not None: data = converter(data) row.append(data) return tuple(row) def _get_descriptions(self): """Read a column descriptor packet for each column in the result.""" self.fields = [] self.converters = [] use_unicode = self.connection.use_unicode conn_encoding = self.connection.encoding description = [] for i in range_type(self.field_count): field = self.connection._read_packet(FieldDescriptorPacket) self.fields.append(field) description.append(field.description()) field_type = field.type_code if use_unicode: if field_type == FIELD_TYPE.JSON: # When SELECT from JSON column: charset = binary # When SELECT CAST(... AS JSON): charset = connection encoding # This behavior is different from TEXT / BLOB. # We should decode result by connection encoding regardless charsetnr. # See https://github.com/PyMySQL/PyMySQL/issues/488 encoding = conn_encoding # SELECT CAST(... AS JSON) elif field_type in TEXT_TYPES: if field.charsetnr == 63: # binary # TEXTs with charset=binary means BINARY types. encoding = None else: encoding = conn_encoding else: # Integers, Dates and Times, and other basic data is encoded in ascii encoding = 'ascii' else: encoding = None converter = self.connection.decoders.get(field_type) if converter is converters.through: converter = None if DEBUG: print("DEBUG: field={}, converter={}".format(field, converter)) self.converters.append((encoding, converter)) eof_packet = self.connection._read_packet() assert eof_packet.is_eof_packet(), 'Protocol error, expecting EOF' self.description = tuple(description) class LoadLocalFile(object): def __init__(self, filename, connection): self.filename = filename self.connection = connection def send_data(self): """Send data packets from the local file to the server""" if not self.connection._sock: raise err.InterfaceError("(0, '')") conn = self.connection try: with open(self.filename, 'rb') as open_file: packet_size = min(conn.max_allowed_packet, 16*1024) # 16KB is efficient enough while True: chunk = open_file.read(packet_size) if not chunk: break conn.write_packet(chunk) except IOError: raise err.OperationalError(1017, "Can't find file '{0}'".format(self.filename)) finally: # send the empty packet to signify we are done sending data conn.write_packet(b'') PyMySQL-0.9.3/pymysql/constants/000077500000000000000000000000001340615555200165415ustar00rootroot00000000000000PyMySQL-0.9.3/pymysql/constants/CLIENT.py000066400000000000000000000015251340615555200200740ustar00rootroot00000000000000# https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags LONG_PASSWORD = 1 FOUND_ROWS = 1 << 1 LONG_FLAG = 1 << 2 CONNECT_WITH_DB = 1 << 3 NO_SCHEMA = 1 << 4 COMPRESS = 1 << 5 ODBC = 1 << 6 LOCAL_FILES = 1 << 7 IGNORE_SPACE = 1 << 8 PROTOCOL_41 = 1 << 9 INTERACTIVE = 1 << 10 SSL = 1 << 11 IGNORE_SIGPIPE = 1 << 12 TRANSACTIONS = 1 << 13 SECURE_CONNECTION = 1 << 15 MULTI_STATEMENTS = 1 << 16 MULTI_RESULTS = 1 << 17 PS_MULTI_RESULTS = 1 << 18 PLUGIN_AUTH = 1 << 19 CONNECT_ATTRS = 1 << 20 PLUGIN_AUTH_LENENC_CLIENT_DATA = 1 << 21 CAPABILITIES = ( LONG_PASSWORD | LONG_FLAG | PROTOCOL_41 | TRANSACTIONS | SECURE_CONNECTION | MULTI_RESULTS | PLUGIN_AUTH | PLUGIN_AUTH_LENENC_CLIENT_DATA | CONNECT_ATTRS) # Not done yet HANDLE_EXPIRED_PASSWORDS = 1 << 22 SESSION_TRACK = 1 << 23 DEPRECATE_EOF = 1 << 24 PyMySQL-0.9.3/pymysql/constants/COMMAND.py000066400000000000000000000012501340615555200201670ustar00rootroot00000000000000 COM_SLEEP = 0x00 COM_QUIT = 0x01 COM_INIT_DB = 0x02 COM_QUERY = 0x03 COM_FIELD_LIST = 0x04 COM_CREATE_DB = 0x05 COM_DROP_DB = 0x06 COM_REFRESH = 0x07 COM_SHUTDOWN = 0x08 COM_STATISTICS = 0x09 COM_PROCESS_INFO = 0x0a COM_CONNECT = 0x0b COM_PROCESS_KILL = 0x0c COM_DEBUG = 0x0d COM_PING = 0x0e COM_TIME = 0x0f COM_DELAYED_INSERT = 0x10 COM_CHANGE_USER = 0x11 COM_BINLOG_DUMP = 0x12 COM_TABLE_DUMP = 0x13 COM_CONNECT_OUT = 0x14 COM_REGISTER_SLAVE = 0x15 COM_STMT_PREPARE = 0x16 COM_STMT_EXECUTE = 0x17 COM_STMT_SEND_LONG_DATA = 0x18 COM_STMT_CLOSE = 0x19 COM_STMT_RESET = 0x1a COM_SET_OPTION = 0x1b COM_STMT_FETCH = 0x1c COM_DAEMON = 0x1d COM_BINLOG_DUMP_GTID = 0x1e COM_END = 0x1f PyMySQL-0.9.3/pymysql/constants/CR.py000066400000000000000000000042641340615555200174250ustar00rootroot00000000000000# flake8: noqa # errmsg.h CR_ERROR_FIRST = 2000 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_EMBEDDED_CONNECTION = 2021 CR_PROBE_SLAVE_STATUS = 2022 CR_PROBE_SLAVE_HOSTS = 2023 CR_PROBE_SLAVE_CONNECT = 2024 CR_PROBE_MASTER_CONNECT = 2025 CR_SSL_CONNECTION_ERROR = 2026 CR_MALFORMED_PACKET = 2027 CR_WRONG_LICENSE = 2028 CR_NULL_POINTER = 2029 CR_NO_PREPARE_STMT = 2030 CR_PARAMS_NOT_BOUND = 2031 CR_DATA_TRUNCATED = 2032 CR_NO_PARAMETERS_EXISTS = 2033 CR_INVALID_PARAMETER_NO = 2034 CR_INVALID_BUFFER_USE = 2035 CR_UNSUPPORTED_PARAM_TYPE = 2036 CR_SHARED_MEMORY_CONNECTION = 2037 CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR = 2038 CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR = 2039 CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR = 2040 CR_SHARED_MEMORY_CONNECT_MAP_ERROR = 2041 CR_SHARED_MEMORY_FILE_MAP_ERROR = 2042 CR_SHARED_MEMORY_MAP_ERROR = 2043 CR_SHARED_MEMORY_EVENT_ERROR = 2044 CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR = 2045 CR_SHARED_MEMORY_CONNECT_SET_ERROR = 2046 CR_CONN_UNKNOW_PROTOCOL = 2047 CR_INVALID_CONN_HANDLE = 2048 CR_SECURE_AUTH = 2049 CR_FETCH_CANCELED = 2050 CR_NO_DATA = 2051 CR_NO_STMT_METADATA = 2052 CR_NO_RESULT_SET = 2053 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_ERROR_LAST = 2061 PyMySQL-0.9.3/pymysql/constants/ER.py000066400000000000000000000300111340615555200174140ustar00rootroot00000000000000 ERROR_FIRST = 1000 HASHCHK = 1000 NISAMCHK = 1001 NO = 1002 YES = 1003 CANT_CREATE_FILE = 1004 CANT_CREATE_TABLE = 1005 CANT_CREATE_DB = 1006 DB_CREATE_EXISTS = 1007 DB_DROP_EXISTS = 1008 DB_DROP_DELETE = 1009 DB_DROP_RMDIR = 1010 CANT_DELETE_FILE = 1011 CANT_FIND_SYSTEM_REC = 1012 CANT_GET_STAT = 1013 CANT_GET_WD = 1014 CANT_LOCK = 1015 CANT_OPEN_FILE = 1016 FILE_NOT_FOUND = 1017 CANT_READ_DIR = 1018 CANT_SET_WD = 1019 CHECKREAD = 1020 DISK_FULL = 1021 DUP_KEY = 1022 ERROR_ON_CLOSE = 1023 ERROR_ON_READ = 1024 ERROR_ON_RENAME = 1025 ERROR_ON_WRITE = 1026 FILE_USED = 1027 FILSORT_ABORT = 1028 FORM_NOT_FOUND = 1029 GET_ERRNO = 1030 ILLEGAL_HA = 1031 KEY_NOT_FOUND = 1032 NOT_FORM_FILE = 1033 NOT_KEYFILE = 1034 OLD_KEYFILE = 1035 OPEN_AS_READONLY = 1036 OUTOFMEMORY = 1037 OUT_OF_SORTMEMORY = 1038 UNEXPECTED_EOF = 1039 CON_COUNT_ERROR = 1040 OUT_OF_RESOURCES = 1041 BAD_HOST_ERROR = 1042 HANDSHAKE_ERROR = 1043 DBACCESS_DENIED_ERROR = 1044 ACCESS_DENIED_ERROR = 1045 NO_DB_ERROR = 1046 UNKNOWN_COM_ERROR = 1047 BAD_NULL_ERROR = 1048 BAD_DB_ERROR = 1049 TABLE_EXISTS_ERROR = 1050 BAD_TABLE_ERROR = 1051 NON_UNIQ_ERROR = 1052 SERVER_SHUTDOWN = 1053 BAD_FIELD_ERROR = 1054 WRONG_FIELD_WITH_GROUP = 1055 WRONG_GROUP_FIELD = 1056 WRONG_SUM_SELECT = 1057 WRONG_VALUE_COUNT = 1058 TOO_LONG_IDENT = 1059 DUP_FIELDNAME = 1060 DUP_KEYNAME = 1061 DUP_ENTRY = 1062 WRONG_FIELD_SPEC = 1063 PARSE_ERROR = 1064 EMPTY_QUERY = 1065 NONUNIQ_TABLE = 1066 INVALID_DEFAULT = 1067 MULTIPLE_PRI_KEY = 1068 TOO_MANY_KEYS = 1069 TOO_MANY_KEY_PARTS = 1070 TOO_LONG_KEY = 1071 KEY_COLUMN_DOES_NOT_EXITS = 1072 BLOB_USED_AS_KEY = 1073 TOO_BIG_FIELDLENGTH = 1074 WRONG_AUTO_KEY = 1075 READY = 1076 NORMAL_SHUTDOWN = 1077 GOT_SIGNAL = 1078 SHUTDOWN_COMPLETE = 1079 FORCING_CLOSE = 1080 IPSOCK_ERROR = 1081 NO_SUCH_INDEX = 1082 WRONG_FIELD_TERMINATORS = 1083 BLOBS_AND_NO_TERMINATED = 1084 TEXTFILE_NOT_READABLE = 1085 FILE_EXISTS_ERROR = 1086 LOAD_INFO = 1087 ALTER_INFO = 1088 WRONG_SUB_KEY = 1089 CANT_REMOVE_ALL_FIELDS = 1090 CANT_DROP_FIELD_OR_KEY = 1091 INSERT_INFO = 1092 UPDATE_TABLE_USED = 1093 NO_SUCH_THREAD = 1094 KILL_DENIED_ERROR = 1095 NO_TABLES_USED = 1096 TOO_BIG_SET = 1097 NO_UNIQUE_LOGFILE = 1098 TABLE_NOT_LOCKED_FOR_WRITE = 1099 TABLE_NOT_LOCKED = 1100 BLOB_CANT_HAVE_DEFAULT = 1101 WRONG_DB_NAME = 1102 WRONG_TABLE_NAME = 1103 TOO_BIG_SELECT = 1104 UNKNOWN_ERROR = 1105 UNKNOWN_PROCEDURE = 1106 WRONG_PARAMCOUNT_TO_PROCEDURE = 1107 WRONG_PARAMETERS_TO_PROCEDURE = 1108 UNKNOWN_TABLE = 1109 FIELD_SPECIFIED_TWICE = 1110 INVALID_GROUP_FUNC_USE = 1111 UNSUPPORTED_EXTENSION = 1112 TABLE_MUST_HAVE_COLUMNS = 1113 RECORD_FILE_FULL = 1114 UNKNOWN_CHARACTER_SET = 1115 TOO_MANY_TABLES = 1116 TOO_MANY_FIELDS = 1117 TOO_BIG_ROWSIZE = 1118 STACK_OVERRUN = 1119 WRONG_OUTER_JOIN = 1120 NULL_COLUMN_IN_INDEX = 1121 CANT_FIND_UDF = 1122 CANT_INITIALIZE_UDF = 1123 UDF_NO_PATHS = 1124 UDF_EXISTS = 1125 CANT_OPEN_LIBRARY = 1126 CANT_FIND_DL_ENTRY = 1127 FUNCTION_NOT_DEFINED = 1128 HOST_IS_BLOCKED = 1129 HOST_NOT_PRIVILEGED = 1130 PASSWORD_ANONYMOUS_USER = 1131 PASSWORD_NOT_ALLOWED = 1132 PASSWORD_NO_MATCH = 1133 UPDATE_INFO = 1134 CANT_CREATE_THREAD = 1135 WRONG_VALUE_COUNT_ON_ROW = 1136 CANT_REOPEN_TABLE = 1137 INVALID_USE_OF_NULL = 1138 REGEXP_ERROR = 1139 MIX_OF_GROUP_FUNC_AND_FIELDS = 1140 NONEXISTING_GRANT = 1141 TABLEACCESS_DENIED_ERROR = 1142 COLUMNACCESS_DENIED_ERROR = 1143 ILLEGAL_GRANT_FOR_TABLE = 1144 GRANT_WRONG_HOST_OR_USER = 1145 NO_SUCH_TABLE = 1146 NONEXISTING_TABLE_GRANT = 1147 NOT_ALLOWED_COMMAND = 1148 SYNTAX_ERROR = 1149 DELAYED_CANT_CHANGE_LOCK = 1150 TOO_MANY_DELAYED_THREADS = 1151 ABORTING_CONNECTION = 1152 NET_PACKET_TOO_LARGE = 1153 NET_READ_ERROR_FROM_PIPE = 1154 NET_FCNTL_ERROR = 1155 NET_PACKETS_OUT_OF_ORDER = 1156 NET_UNCOMPRESS_ERROR = 1157 NET_READ_ERROR = 1158 NET_READ_INTERRUPTED = 1159 NET_ERROR_ON_WRITE = 1160 NET_WRITE_INTERRUPTED = 1161 TOO_LONG_STRING = 1162 TABLE_CANT_HANDLE_BLOB = 1163 TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164 DELAYED_INSERT_TABLE_LOCKED = 1165 WRONG_COLUMN_NAME = 1166 WRONG_KEY_COLUMN = 1167 WRONG_MRG_TABLE = 1168 DUP_UNIQUE = 1169 BLOB_KEY_WITHOUT_LENGTH = 1170 PRIMARY_CANT_HAVE_NULL = 1171 TOO_MANY_ROWS = 1172 REQUIRES_PRIMARY_KEY = 1173 NO_RAID_COMPILED = 1174 UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175 KEY_DOES_NOT_EXITS = 1176 CHECK_NO_SUCH_TABLE = 1177 CHECK_NOT_IMPLEMENTED = 1178 CANT_DO_THIS_DURING_AN_TRANSACTION = 1179 ERROR_DURING_COMMIT = 1180 ERROR_DURING_ROLLBACK = 1181 ERROR_DURING_FLUSH_LOGS = 1182 ERROR_DURING_CHECKPOINT = 1183 NEW_ABORTING_CONNECTION = 1184 DUMP_NOT_IMPLEMENTED = 1185 FLUSH_MASTER_BINLOG_CLOSED = 1186 INDEX_REBUILD = 1187 MASTER = 1188 MASTER_NET_READ = 1189 MASTER_NET_WRITE = 1190 FT_MATCHING_KEY_NOT_FOUND = 1191 LOCK_OR_ACTIVE_TRANSACTION = 1192 UNKNOWN_SYSTEM_VARIABLE = 1193 CRASHED_ON_USAGE = 1194 CRASHED_ON_REPAIR = 1195 WARNING_NOT_COMPLETE_ROLLBACK = 1196 TRANS_CACHE_FULL = 1197 SLAVE_MUST_STOP = 1198 SLAVE_NOT_RUNNING = 1199 BAD_SLAVE = 1200 MASTER_INFO = 1201 SLAVE_THREAD = 1202 TOO_MANY_USER_CONNECTIONS = 1203 SET_CONSTANTS_ONLY = 1204 LOCK_WAIT_TIMEOUT = 1205 LOCK_TABLE_FULL = 1206 READ_ONLY_TRANSACTION = 1207 DROP_DB_WITH_READ_LOCK = 1208 CREATE_DB_WITH_READ_LOCK = 1209 WRONG_ARGUMENTS = 1210 NO_PERMISSION_TO_CREATE_USER = 1211 UNION_TABLES_IN_DIFFERENT_DIR = 1212 LOCK_DEADLOCK = 1213 TABLE_CANT_HANDLE_FT = 1214 CANNOT_ADD_FOREIGN = 1215 NO_REFERENCED_ROW = 1216 ROW_IS_REFERENCED = 1217 CONNECT_TO_MASTER = 1218 QUERY_ON_MASTER = 1219 ERROR_WHEN_EXECUTING_COMMAND = 1220 WRONG_USAGE = 1221 WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222 CANT_UPDATE_WITH_READLOCK = 1223 MIXING_NOT_ALLOWED = 1224 DUP_ARGUMENT = 1225 USER_LIMIT_REACHED = 1226 SPECIFIC_ACCESS_DENIED_ERROR = 1227 LOCAL_VARIABLE = 1228 GLOBAL_VARIABLE = 1229 NO_DEFAULT = 1230 WRONG_VALUE_FOR_VAR = 1231 WRONG_TYPE_FOR_VAR = 1232 VAR_CANT_BE_READ = 1233 CANT_USE_OPTION_HERE = 1234 NOT_SUPPORTED_YET = 1235 MASTER_FATAL_ERROR_READING_BINLOG = 1236 SLAVE_IGNORED_TABLE = 1237 INCORRECT_GLOBAL_LOCAL_VAR = 1238 WRONG_FK_DEF = 1239 KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240 OPERAND_COLUMNS = 1241 SUBQUERY_NO_1_ROW = 1242 UNKNOWN_STMT_HANDLER = 1243 CORRUPT_HELP_DB = 1244 CYCLIC_REFERENCE = 1245 AUTO_CONVERT = 1246 ILLEGAL_REFERENCE = 1247 DERIVED_MUST_HAVE_ALIAS = 1248 SELECT_REDUCED = 1249 TABLENAME_NOT_ALLOWED_HERE = 1250 NOT_SUPPORTED_AUTH_MODE = 1251 SPATIAL_CANT_HAVE_NULL = 1252 COLLATION_CHARSET_MISMATCH = 1253 SLAVE_WAS_RUNNING = 1254 SLAVE_WAS_NOT_RUNNING = 1255 TOO_BIG_FOR_UNCOMPRESS = 1256 ZLIB_Z_MEM_ERROR = 1257 ZLIB_Z_BUF_ERROR = 1258 ZLIB_Z_DATA_ERROR = 1259 CUT_VALUE_GROUP_CONCAT = 1260 WARN_TOO_FEW_RECORDS = 1261 WARN_TOO_MANY_RECORDS = 1262 WARN_NULL_TO_NOTNULL = 1263 WARN_DATA_OUT_OF_RANGE = 1264 WARN_DATA_TRUNCATED = 1265 WARN_USING_OTHER_HANDLER = 1266 CANT_AGGREGATE_2COLLATIONS = 1267 DROP_USER = 1268 REVOKE_GRANTS = 1269 CANT_AGGREGATE_3COLLATIONS = 1270 CANT_AGGREGATE_NCOLLATIONS = 1271 VARIABLE_IS_NOT_STRUCT = 1272 UNKNOWN_COLLATION = 1273 SLAVE_IGNORED_SSL_PARAMS = 1274 SERVER_IS_IN_SECURE_AUTH_MODE = 1275 WARN_FIELD_RESOLVED = 1276 BAD_SLAVE_UNTIL_COND = 1277 MISSING_SKIP_SLAVE = 1278 UNTIL_COND_IGNORED = 1279 WRONG_NAME_FOR_INDEX = 1280 WRONG_NAME_FOR_CATALOG = 1281 WARN_QC_RESIZE = 1282 BAD_FT_COLUMN = 1283 UNKNOWN_KEY_CACHE = 1284 WARN_HOSTNAME_WONT_WORK = 1285 UNKNOWN_STORAGE_ENGINE = 1286 WARN_DEPRECATED_SYNTAX = 1287 NON_UPDATABLE_TABLE = 1288 FEATURE_DISABLED = 1289 OPTION_PREVENTS_STATEMENT = 1290 DUPLICATED_VALUE_IN_TYPE = 1291 TRUNCATED_WRONG_VALUE = 1292 TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293 INVALID_ON_UPDATE = 1294 UNSUPPORTED_PS = 1295 GET_ERRMSG = 1296 GET_TEMPORARY_ERRMSG = 1297 UNKNOWN_TIME_ZONE = 1298 WARN_INVALID_TIMESTAMP = 1299 INVALID_CHARACTER_STRING = 1300 WARN_ALLOWED_PACKET_OVERFLOWED = 1301 CONFLICTING_DECLARATIONS = 1302 SP_NO_RECURSIVE_CREATE = 1303 SP_ALREADY_EXISTS = 1304 SP_DOES_NOT_EXIST = 1305 SP_DROP_FAILED = 1306 SP_STORE_FAILED = 1307 SP_LILABEL_MISMATCH = 1308 SP_LABEL_REDEFINE = 1309 SP_LABEL_MISMATCH = 1310 SP_UNINIT_VAR = 1311 SP_BADSELECT = 1312 SP_BADRETURN = 1313 SP_BADSTATEMENT = 1314 UPDATE_LOG_DEPRECATED_IGNORED = 1315 UPDATE_LOG_DEPRECATED_TRANSLATED = 1316 QUERY_INTERRUPTED = 1317 SP_WRONG_NO_OF_ARGS = 1318 SP_COND_MISMATCH = 1319 SP_NORETURN = 1320 SP_NORETURNEND = 1321 SP_BAD_CURSOR_QUERY = 1322 SP_BAD_CURSOR_SELECT = 1323 SP_CURSOR_MISMATCH = 1324 SP_CURSOR_ALREADY_OPEN = 1325 SP_CURSOR_NOT_OPEN = 1326 SP_UNDECLARED_VAR = 1327 SP_WRONG_NO_OF_FETCH_ARGS = 1328 SP_FETCH_NO_DATA = 1329 SP_DUP_PARAM = 1330 SP_DUP_VAR = 1331 SP_DUP_COND = 1332 SP_DUP_CURS = 1333 SP_CANT_ALTER = 1334 SP_SUBSELECT_NYI = 1335 STMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336 SP_VARCOND_AFTER_CURSHNDLR = 1337 SP_CURSOR_AFTER_HANDLER = 1338 SP_CASE_NOT_FOUND = 1339 FPARSER_TOO_BIG_FILE = 1340 FPARSER_BAD_HEADER = 1341 FPARSER_EOF_IN_COMMENT = 1342 FPARSER_ERROR_IN_PARAMETER = 1343 FPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344 VIEW_NO_EXPLAIN = 1345 FRM_UNKNOWN_TYPE = 1346 WRONG_OBJECT = 1347 NONUPDATEABLE_COLUMN = 1348 VIEW_SELECT_DERIVED = 1349 VIEW_SELECT_CLAUSE = 1350 VIEW_SELECT_VARIABLE = 1351 VIEW_SELECT_TMPTABLE = 1352 VIEW_WRONG_LIST = 1353 WARN_VIEW_MERGE = 1354 WARN_VIEW_WITHOUT_KEY = 1355 VIEW_INVALID = 1356 SP_NO_DROP_SP = 1357 SP_GOTO_IN_HNDLR = 1358 TRG_ALREADY_EXISTS = 1359 TRG_DOES_NOT_EXIST = 1360 TRG_ON_VIEW_OR_TEMP_TABLE = 1361 TRG_CANT_CHANGE_ROW = 1362 TRG_NO_SUCH_ROW_IN_TRG = 1363 NO_DEFAULT_FOR_FIELD = 1364 DIVISION_BY_ZERO = 1365 TRUNCATED_WRONG_VALUE_FOR_FIELD = 1366 ILLEGAL_VALUE_FOR_TYPE = 1367 VIEW_NONUPD_CHECK = 1368 VIEW_CHECK_FAILED = 1369 PROCACCESS_DENIED_ERROR = 1370 RELAY_LOG_FAIL = 1371 PASSWD_LENGTH = 1372 UNKNOWN_TARGET_BINLOG = 1373 IO_ERR_LOG_INDEX_READ = 1374 BINLOG_PURGE_PROHIBITED = 1375 FSEEK_FAIL = 1376 BINLOG_PURGE_FATAL_ERR = 1377 LOG_IN_USE = 1378 LOG_PURGE_UNKNOWN_ERR = 1379 RELAY_LOG_INIT = 1380 NO_BINARY_LOGGING = 1381 RESERVED_SYNTAX = 1382 WSAS_FAILED = 1383 DIFF_GROUPS_PROC = 1384 NO_GROUP_FOR_PROC = 1385 ORDER_WITH_PROC = 1386 LOGGING_PROHIBIT_CHANGING_OF = 1387 NO_FILE_MAPPING = 1388 WRONG_MAGIC = 1389 PS_MANY_PARAM = 1390 KEY_PART_0 = 1391 VIEW_CHECKSUM = 1392 VIEW_MULTIUPDATE = 1393 VIEW_NO_INSERT_FIELD_LIST = 1394 VIEW_DELETE_MERGE_VIEW = 1395 CANNOT_USER = 1396 XAER_NOTA = 1397 XAER_INVAL = 1398 XAER_RMFAIL = 1399 XAER_OUTSIDE = 1400 XAER_RMERR = 1401 XA_RBROLLBACK = 1402 NONEXISTING_PROC_GRANT = 1403 PROC_AUTO_GRANT_FAIL = 1404 PROC_AUTO_REVOKE_FAIL = 1405 DATA_TOO_LONG = 1406 SP_BAD_SQLSTATE = 1407 STARTUP = 1408 LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409 CANT_CREATE_USER_WITH_GRANT = 1410 WRONG_VALUE_FOR_TYPE = 1411 TABLE_DEF_CHANGED = 1412 SP_DUP_HANDLER = 1413 SP_NOT_VAR_ARG = 1414 SP_NO_RETSET = 1415 CANT_CREATE_GEOMETRY_OBJECT = 1416 FAILED_ROUTINE_BREAK_BINLOG = 1417 BINLOG_UNSAFE_ROUTINE = 1418 BINLOG_CREATE_ROUTINE_NEED_SUPER = 1419 EXEC_STMT_WITH_OPEN_CURSOR = 1420 STMT_HAS_NO_OPEN_CURSOR = 1421 COMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422 NO_DEFAULT_FOR_VIEW_FIELD = 1423 SP_NO_RECURSION = 1424 TOO_BIG_SCALE = 1425 TOO_BIG_PRECISION = 1426 M_BIGGER_THAN_D = 1427 WRONG_LOCK_OF_SYSTEM_TABLE = 1428 CONNECT_TO_FOREIGN_DATA_SOURCE = 1429 QUERY_ON_FOREIGN_DATA_SOURCE = 1430 FOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431 FOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432 FOREIGN_DATA_STRING_INVALID = 1433 CANT_CREATE_FEDERATED_TABLE = 1434 TRG_IN_WRONG_SCHEMA = 1435 STACK_OVERRUN_NEED_MORE = 1436 TOO_LONG_BODY = 1437 WARN_CANT_DROP_DEFAULT_KEYCACHE = 1438 TOO_BIG_DISPLAYWIDTH = 1439 XAER_DUPID = 1440 DATETIME_FUNCTION_OVERFLOW = 1441 CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442 VIEW_PREVENT_UPDATE = 1443 PS_NO_RECURSION = 1444 SP_CANT_SET_AUTOCOMMIT = 1445 MALFORMED_DEFINER = 1446 VIEW_FRM_NO_USER = 1447 VIEW_OTHER_USER = 1448 NO_SUCH_USER = 1449 FORBID_SCHEMA_CHANGE = 1450 ROW_IS_REFERENCED_2 = 1451 NO_REFERENCED_ROW_2 = 1452 SP_BAD_VAR_SHADOW = 1453 TRG_NO_DEFINER = 1454 OLD_FILE_FORMAT = 1455 SP_RECURSION_LIMIT = 1456 SP_PROC_TABLE_CORRUPT = 1457 SP_WRONG_NAME = 1458 TABLE_NEEDS_UPGRADE = 1459 SP_NO_AGGREGATE = 1460 MAX_PREPARED_STMT_COUNT_REACHED = 1461 VIEW_RECURSIVE = 1462 NON_GROUPING_FIELD_USED = 1463 TABLE_CANT_HANDLE_SPKEYS = 1464 NO_TRIGGERS_ON_SYSTEM_SCHEMA = 1465 USERNAME = 1466 HOSTNAME = 1467 WRONG_STRING_LENGTH = 1468 ERROR_LAST = 1468 # https://github.com/PyMySQL/PyMySQL/issues/607 CONSTRAINT_FAILED = 4025 PyMySQL-0.9.3/pymysql/constants/FIELD_TYPE.py000066400000000000000000000005641340615555200206040ustar00rootroot00000000000000 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 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 CHAR = TINY INTERVAL = ENUM PyMySQL-0.9.3/pymysql/constants/FLAG.py000066400000000000000000000003261340615555200176250ustar00rootroot00000000000000NOT_NULL = 1 PRI_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 PART_KEY = 16384 GROUP = 32767 UNIQUE = 65536 PyMySQL-0.9.3/pymysql/constants/SERVER_STATUS.py000066400000000000000000000005161340615555200212260ustar00rootroot00000000000000 SERVER_STATUS_IN_TRANS = 1 SERVER_STATUS_AUTOCOMMIT = 2 SERVER_MORE_RESULTS_EXISTS = 8 SERVER_QUERY_NO_GOOD_INDEX_USED = 16 SERVER_QUERY_NO_INDEX_USED = 32 SERVER_STATUS_CURSOR_EXISTS = 64 SERVER_STATUS_LAST_ROW_SENT = 128 SERVER_STATUS_DB_DROPPED = 256 SERVER_STATUS_NO_BACKSLASH_ESCAPES = 512 SERVER_STATUS_METADATA_CHANGED = 1024 PyMySQL-0.9.3/pymysql/constants/__init__.py000066400000000000000000000000001340615555200206400ustar00rootroot00000000000000PyMySQL-0.9.3/pymysql/converters.py000066400000000000000000000277131340615555200173030ustar00rootroot00000000000000from ._compat import PY2, text_type, long_type, JYTHON, IRONPYTHON, unichr import datetime from decimal import Decimal import re import time from .constants import FIELD_TYPE, FLAG from .charset import charset_by_id, charset_to_encoding def escape_item(val, charset, mapping=None): if mapping is None: mapping = encoders encoder = mapping.get(type(val)) # Fallback to default when no encoder found if not encoder: try: encoder = mapping[text_type] except KeyError: raise TypeError("no default type converter defined") if encoder in (escape_dict, escape_sequence): val = encoder(val, charset, mapping) else: val = encoder(val, mapping) return val def escape_dict(val, charset, mapping=None): n = {} for k, v in val.items(): quoted = escape_item(v, charset, mapping) n[k] = quoted return n def escape_sequence(val, charset, mapping=None): n = [] for item in val: quoted = escape_item(item, charset, mapping) n.append(quoted) return "(" + ",".join(n) + ")" def escape_set(val, charset, mapping=None): return ','.join([escape_item(x, charset, mapping) for x in val]) def escape_bool(value, mapping=None): return str(int(value)) def escape_object(value, mapping=None): return str(value) def escape_int(value, mapping=None): return str(value) def escape_float(value, mapping=None): return ('%.15g' % value) _escape_table = [unichr(x) for x in range(128)] _escape_table[0] = u'\\0' _escape_table[ord('\\')] = u'\\\\' _escape_table[ord('\n')] = u'\\n' _escape_table[ord('\r')] = u'\\r' _escape_table[ord('\032')] = u'\\Z' _escape_table[ord('"')] = u'\\"' _escape_table[ord("'")] = u"\\'" def _escape_unicode(value, mapping=None): """escapes *value* without adding quote. Value should be unicode """ return value.translate(_escape_table) if PY2: def escape_string(value, mapping=None): """escape_string escapes *value* but not surround it with quotes. Value should be bytes or unicode. """ if isinstance(value, unicode): return _escape_unicode(value) assert isinstance(value, (bytes, bytearray)) value = value.replace('\\', '\\\\') value = value.replace('\0', '\\0') value = value.replace('\n', '\\n') value = value.replace('\r', '\\r') value = value.replace('\032', '\\Z') value = value.replace("'", "\\'") value = value.replace('"', '\\"') return value def escape_bytes_prefixed(value, mapping=None): assert isinstance(value, (bytes, bytearray)) return b"_binary'%s'" % escape_string(value) def escape_bytes(value, mapping=None): assert isinstance(value, (bytes, bytearray)) return b"'%s'" % escape_string(value) else: escape_string = _escape_unicode # On Python ~3.5, str.decode('ascii', 'surrogateescape') is slow. # (fixed in Python 3.6, http://bugs.python.org/issue24870) # Workaround is str.decode('latin1') then translate 0x80-0xff into 0udc80-0udcff. # We can escape special chars and surrogateescape at once. _escape_bytes_table = _escape_table + [chr(i) for i in range(0xdc80, 0xdd00)] def escape_bytes_prefixed(value, mapping=None): return "_binary'%s'" % value.decode('latin1').translate(_escape_bytes_table) def escape_bytes(value, mapping=None): return "'%s'" % value.decode('latin1').translate(_escape_bytes_table) def escape_unicode(value, mapping=None): return u"'%s'" % _escape_unicode(value) def escape_str(value, mapping=None): return "'%s'" % escape_string(str(value), mapping) def escape_None(value, mapping=None): return 'NULL' def escape_timedelta(obj, mapping=None): seconds = int(obj.seconds) % 60 minutes = int(obj.seconds // 60) % 60 hours = int(obj.seconds // 3600) % 24 + int(obj.days) * 24 if obj.microseconds: fmt = "'{0:02d}:{1:02d}:{2:02d}.{3:06d}'" else: fmt = "'{0:02d}:{1:02d}:{2:02d}'" return fmt.format(hours, minutes, seconds, obj.microseconds) def escape_time(obj, mapping=None): if obj.microsecond: fmt = "'{0.hour:02}:{0.minute:02}:{0.second:02}.{0.microsecond:06}'" else: fmt = "'{0.hour:02}:{0.minute:02}:{0.second:02}'" return fmt.format(obj) def escape_datetime(obj, mapping=None): if obj.microsecond: fmt = "'{0.year:04}-{0.month:02}-{0.day:02} {0.hour:02}:{0.minute:02}:{0.second:02}.{0.microsecond:06}'" else: fmt = "'{0.year:04}-{0.month:02}-{0.day:02} {0.hour:02}:{0.minute:02}:{0.second:02}'" return fmt.format(obj) def escape_date(obj, mapping=None): fmt = "'{0.year:04}-{0.month:02}-{0.day:02}'" return fmt.format(obj) def escape_struct_time(obj, mapping=None): return escape_datetime(datetime.datetime(*obj[:6])) def _convert_second_fraction(s): if not s: return 0 # Pad zeros to ensure the fraction length in microseconds s = s.ljust(6, '0') return int(s[:6]) DATETIME_RE = re.compile(r"(\d{1,4})-(\d{1,2})-(\d{1,2})[T ](\d{1,2}):(\d{1,2}):(\d{1,2})(?:.(\d{1,6}))?") def convert_datetime(obj): """Returns a DATETIME or TIMESTAMP column value as a datetime object: >>> datetime_or_None('2007-02-25 23:06:20') datetime.datetime(2007, 2, 25, 23, 6, 20) >>> datetime_or_None('2007-02-25T23:06:20') datetime.datetime(2007, 2, 25, 23, 6, 20) Illegal values are returned as None: >>> datetime_or_None('2007-02-31T23:06:20') is None True >>> datetime_or_None('0000-00-00 00:00:00') is None True """ if not PY2 and isinstance(obj, (bytes, bytearray)): obj = obj.decode('ascii') m = DATETIME_RE.match(obj) if not m: return convert_date(obj) try: groups = list(m.groups()) groups[-1] = _convert_second_fraction(groups[-1]) return datetime.datetime(*[ int(x) for x in groups ]) except ValueError: return convert_date(obj) TIMEDELTA_RE = re.compile(r"(-)?(\d{1,3}):(\d{1,2}):(\d{1,2})(?:.(\d{1,6}))?") def convert_timedelta(obj): """Returns a TIME column as a timedelta object: >>> timedelta_or_None('25:06:17') datetime.timedelta(1, 3977) >>> timedelta_or_None('-25:06:17') datetime.timedelta(-2, 83177) Illegal values are returned as None: >>> timedelta_or_None('random crap') is None True Note that MySQL always returns TIME columns as (+|-)HH:MM:SS, but can accept values as (+|-)DD HH:MM:SS. The latter format will not be parsed correctly by this function. """ if not PY2 and isinstance(obj, (bytes, bytearray)): obj = obj.decode('ascii') m = TIMEDELTA_RE.match(obj) if not m: return obj try: groups = list(m.groups()) groups[-1] = _convert_second_fraction(groups[-1]) negate = -1 if groups[0] else 1 hours, minutes, seconds, microseconds = groups[1:] tdelta = datetime.timedelta( hours = int(hours), minutes = int(minutes), seconds = int(seconds), microseconds = int(microseconds) ) * negate return tdelta except ValueError: return obj TIME_RE = re.compile(r"(\d{1,2}):(\d{1,2}):(\d{1,2})(?:.(\d{1,6}))?") def convert_time(obj): """Returns a TIME column as a time object: >>> time_or_None('15:06:17') datetime.time(15, 6, 17) Illegal values are returned as None: >>> time_or_None('-25:06:17') is None True >>> time_or_None('random crap') is None True Note that MySQL always returns TIME columns as (+|-)HH:MM:SS, but can accept values as (+|-)DD HH:MM:SS. The latter format will not be parsed correctly by this function. Also note that MySQL's TIME column corresponds more closely to Python's timedelta and not time. However if you want TIME columns to be treated as time-of-day and not a time offset, then you can use set this function as the converter for FIELD_TYPE.TIME. """ if not PY2 and isinstance(obj, (bytes, bytearray)): obj = obj.decode('ascii') m = TIME_RE.match(obj) if not m: return obj try: groups = list(m.groups()) groups[-1] = _convert_second_fraction(groups[-1]) hours, minutes, seconds, microseconds = groups return datetime.time(hour=int(hours), minute=int(minutes), second=int(seconds), microsecond=int(microseconds)) except ValueError: return obj def convert_date(obj): """Returns a DATE column as a date object: >>> date_or_None('2007-02-26') datetime.date(2007, 2, 26) Illegal values are returned as None: >>> date_or_None('2007-02-31') is None True >>> date_or_None('0000-00-00') is None True """ if not PY2 and isinstance(obj, (bytes, bytearray)): obj = obj.decode('ascii') try: return datetime.date(*[ int(x) for x in obj.split('-', 2) ]) except ValueError: return obj def convert_mysql_timestamp(timestamp): """Convert a MySQL TIMESTAMP to a Timestamp object. MySQL >= 4.1 returns TIMESTAMP in the same format as DATETIME: >>> mysql_timestamp_converter('2007-02-25 22:32:17') datetime.datetime(2007, 2, 25, 22, 32, 17) MySQL < 4.1 uses a big string of numbers: >>> mysql_timestamp_converter('20070225223217') datetime.datetime(2007, 2, 25, 22, 32, 17) Illegal values are returned as None: >>> mysql_timestamp_converter('2007-02-31 22:32:17') is None True >>> mysql_timestamp_converter('00000000000000') is None True """ if not PY2 and isinstance(timestamp, (bytes, bytearray)): timestamp = timestamp.decode('ascii') if timestamp[4] == '-': return convert_datetime(timestamp) timestamp += "0"*(14-len(timestamp)) # padding year, month, day, hour, minute, second = \ int(timestamp[:4]), int(timestamp[4:6]), int(timestamp[6:8]), \ int(timestamp[8:10]), int(timestamp[10:12]), int(timestamp[12:14]) try: return datetime.datetime(year, month, day, hour, minute, second) except ValueError: return timestamp def convert_set(s): if isinstance(s, (bytes, bytearray)): return set(s.split(b",")) return set(s.split(",")) def through(x): return x #def convert_bit(b): # b = "\x00" * (8 - len(b)) + b # pad w/ zeroes # return struct.unpack(">Q", b)[0] # # the snippet above is right, but MySQLdb doesn't process bits, # so we shouldn't either convert_bit = through encoders = { bool: escape_bool, int: escape_int, long_type: escape_int, float: escape_float, str: escape_str, text_type: escape_unicode, tuple: escape_sequence, list: escape_sequence, set: escape_sequence, frozenset: escape_sequence, dict: escape_dict, type(None): escape_None, datetime.date: escape_date, datetime.datetime: escape_datetime, datetime.timedelta: escape_timedelta, datetime.time: escape_time, time.struct_time: escape_struct_time, Decimal: escape_object, } if not PY2 or JYTHON or IRONPYTHON: encoders[bytes] = escape_bytes decoders = { FIELD_TYPE.BIT: convert_bit, FIELD_TYPE.TINY: int, FIELD_TYPE.SHORT: int, FIELD_TYPE.LONG: int, FIELD_TYPE.FLOAT: float, FIELD_TYPE.DOUBLE: float, FIELD_TYPE.LONGLONG: int, FIELD_TYPE.INT24: int, FIELD_TYPE.YEAR: int, FIELD_TYPE.TIMESTAMP: convert_mysql_timestamp, FIELD_TYPE.DATETIME: convert_datetime, FIELD_TYPE.TIME: convert_timedelta, FIELD_TYPE.DATE: convert_date, FIELD_TYPE.SET: convert_set, FIELD_TYPE.BLOB: through, FIELD_TYPE.TINY_BLOB: through, FIELD_TYPE.MEDIUM_BLOB: through, FIELD_TYPE.LONG_BLOB: through, FIELD_TYPE.STRING: through, FIELD_TYPE.VAR_STRING: through, FIELD_TYPE.VARCHAR: through, FIELD_TYPE.DECIMAL: Decimal, FIELD_TYPE.NEWDECIMAL: Decimal, } # for MySQLdb compatibility conversions = encoders.copy() conversions.update(decoders) Thing2Literal = escape_str PyMySQL-0.9.3/pymysql/cursors.py000066400000000000000000000415261340615555200166070ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import print_function, absolute_import from functools import partial import re import warnings from ._compat import range_type, text_type, PY2 from . import err #: Regular expression for :meth:`Cursor.executemany`. #: executemany only suports simple bulk insert. #: You can use it to load large dataset. RE_INSERT_VALUES = re.compile( r"\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)" + r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))" + r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z", re.IGNORECASE | re.DOTALL) class Cursor(object): """ This is the object you use to interact with the database. Do not create an instance of a Cursor yourself. Call connections.Connection.cursor(). See `Cursor `_ in the specification. """ #: Max statement size which :meth:`executemany` generates. #: #: Max size of allowed statement is max_allowed_packet - packet_header_size. #: Default value of max_allowed_packet is 1048576. max_stmt_length = 1024000 _defer_warnings = False def __init__(self, connection): self.connection = connection self.description = None self.rownumber = 0 self.rowcount = -1 self.arraysize = 1 self._executed = None self._result = None self._rows = None self._warnings_handled = False def close(self): """ Closing a cursor just exhausts all remaining data. """ conn = self.connection if conn is None: return try: while self.nextset(): pass finally: self.connection = None def __enter__(self): return self def __exit__(self, *exc_info): del exc_info self.close() def _get_db(self): if not self.connection: raise err.ProgrammingError("Cursor closed") return self.connection def _check_executed(self): if not self._executed: raise err.ProgrammingError("execute() first") def _conv_row(self, row): return row def setinputsizes(self, *args): """Does nothing, required by DB API.""" def setoutputsizes(self, *args): """Does nothing, required by DB API.""" def _nextset(self, unbuffered=False): """Get the next query set""" conn = self._get_db() current_result = self._result # for unbuffered queries warnings are only available once whole result has been read if unbuffered: self._show_warnings() if current_result is None or current_result is not conn._result: return None if not current_result.has_next: return None self._result = None self._clear_result() conn.next_result(unbuffered=unbuffered) self._do_get_result() return True def nextset(self): return self._nextset(False) def _ensure_bytes(self, x, encoding=None): if isinstance(x, text_type): x = x.encode(encoding) elif isinstance(x, (tuple, list)): x = type(x)(self._ensure_bytes(v, encoding=encoding) for v in x) return x def _escape_args(self, args, conn): ensure_bytes = partial(self._ensure_bytes, encoding=conn.encoding) if isinstance(args, (tuple, list)): if PY2: args = tuple(map(ensure_bytes, args)) return tuple(conn.literal(arg) for arg in args) elif isinstance(args, dict): if PY2: args = {ensure_bytes(key): ensure_bytes(val) for (key, val) in args.items()} return {key: conn.literal(val) for (key, val) in args.items()} else: # If it's not a dictionary let's try escaping it anyways. # Worst case it will throw a Value error if PY2: args = ensure_bytes(args) return conn.escape(args) def mogrify(self, query, args=None): """ Returns the exact string that is sent to the database by calling the execute() method. This method follows the extension to the DB API 2.0 followed by Psycopg. """ conn = self._get_db() if PY2: # Use bytes on Python 2 always query = self._ensure_bytes(query, encoding=conn.encoding) if args is not None: query = query % self._escape_args(args, conn) return query def execute(self, query, args=None): """Execute a query :param str query: Query to execute. :param args: parameters used with query. (optional) :type args: tuple, list or dict :return: Number of affected rows :rtype: int If args is a list or tuple, %s can be used as a placeholder in the query. If args is a dict, %(name)s can be used as a placeholder in the query. """ while self.nextset(): pass query = self.mogrify(query, args) result = self._query(query) self._executed = query return result def executemany(self, query, args): # type: (str, list) -> int """Run several data against one query :param query: query to execute on server :param args: Sequence of sequences or mappings. It is used as parameter. :return: Number of rows affected, if any. This method improves performance on multiple-row INSERT and REPLACE. Otherwise it is equivalent to looping over args with execute(). """ if not args: return m = RE_INSERT_VALUES.match(query) if m: q_prefix = m.group(1) % () q_values = m.group(2).rstrip() q_postfix = m.group(3) or '' assert q_values[0] == '(' and q_values[-1] == ')' return self._do_execute_many(q_prefix, q_values, q_postfix, args, self.max_stmt_length, self._get_db().encoding) self.rowcount = sum(self.execute(query, arg) for arg in args) return self.rowcount def _do_execute_many(self, prefix, values, postfix, args, max_stmt_length, encoding): conn = self._get_db() escape = self._escape_args if isinstance(prefix, text_type): prefix = prefix.encode(encoding) if PY2 and isinstance(values, text_type): values = values.encode(encoding) if isinstance(postfix, text_type): postfix = postfix.encode(encoding) sql = bytearray(prefix) args = iter(args) v = values % escape(next(args), conn) if isinstance(v, text_type): if PY2: v = v.encode(encoding) else: v = v.encode(encoding, 'surrogateescape') sql += v rows = 0 for arg in args: v = values % escape(arg, conn) if isinstance(v, text_type): if PY2: v = v.encode(encoding) else: v = v.encode(encoding, 'surrogateescape') if len(sql) + len(v) + len(postfix) + 1 > max_stmt_length: rows += self.execute(sql + postfix) sql = bytearray(prefix) else: sql += b',' sql += v rows += self.execute(sql + postfix) self.rowcount = rows return rows def callproc(self, procname, args=()): """Execute stored procedure procname with args procname -- string, name of procedure to execute on server args -- Sequence of parameters to use with procedure Returns the original args. Compatibility warning: PEP-249 specifies that any modified parameters must be returned. This is currently impossible as they are only available by storing them in a server variable and then retrieved by a query. Since stored procedures return zero or more result sets, there is no reliable way to get at OUT or INOUT parameters via callproc. The server variables are named @_procname_n, where procname is the parameter above and n is the position of the parameter (from zero). Once all result sets generated by the procedure have been fetched, you can issue a SELECT @_procname_0, ... query using .execute() to get any OUT or INOUT values. Compatibility warning: The act of calling a stored procedure itself creates an empty result set. This appears after any result sets generated by the procedure. This is non-standard behavior with respect to the DB-API. Be sure to use nextset() to advance through all result sets; otherwise you may get disconnected. """ conn = self._get_db() if args: fmt = '@_{0}_%d=%s'.format(procname) self._query('SET %s' % ','.join(fmt % (index, conn.escape(arg)) for index, arg in enumerate(args))) self.nextset() q = "CALL %s(%s)" % (procname, ','.join(['@_%s_%d' % (procname, i) for i in range_type(len(args))])) self._query(q) self._executed = q return args def fetchone(self): """Fetch the next row""" self._check_executed() if self._rows is None or self.rownumber >= len(self._rows): return None result = self._rows[self.rownumber] self.rownumber += 1 return result def fetchmany(self, size=None): """Fetch several rows""" self._check_executed() if self._rows is None: return () end = self.rownumber + (size or self.arraysize) result = self._rows[self.rownumber:end] self.rownumber = min(end, len(self._rows)) return result def fetchall(self): """Fetch all the rows""" self._check_executed() if self._rows is None: return () if self.rownumber: result = self._rows[self.rownumber:] else: result = self._rows self.rownumber = len(self._rows) return result def scroll(self, value, mode='relative'): self._check_executed() if mode == 'relative': r = self.rownumber + value elif mode == 'absolute': r = value else: raise err.ProgrammingError("unknown scroll mode %s" % mode) if not (0 <= r < len(self._rows)): raise IndexError("out of range") self.rownumber = r def _query(self, q): conn = self._get_db() self._last_executed = q self._clear_result() conn.query(q) self._do_get_result() return self.rowcount def _clear_result(self): self.rownumber = 0 self._result = None self.rowcount = 0 self.description = None self.lastrowid = None self._rows = None def _do_get_result(self): conn = self._get_db() self._result = result = conn._result self.rowcount = result.affected_rows self.description = result.description self.lastrowid = result.insert_id self._rows = result.rows self._warnings_handled = False if not self._defer_warnings: self._show_warnings() def _show_warnings(self): if self._warnings_handled: return self._warnings_handled = True if self._result and (self._result.has_next or not self._result.warning_count): return ws = self._get_db().show_warnings() if ws is None: return for w in ws: msg = w[-1] if PY2: if isinstance(msg, unicode): msg = msg.encode('utf-8', 'replace') warnings.warn(err.Warning(*w[1:3]), stacklevel=4) def __iter__(self): return iter(self.fetchone, None) Warning = err.Warning Error = err.Error InterfaceError = err.InterfaceError DatabaseError = err.DatabaseError DataError = err.DataError OperationalError = err.OperationalError IntegrityError = err.IntegrityError InternalError = err.InternalError ProgrammingError = err.ProgrammingError NotSupportedError = err.NotSupportedError class DictCursorMixin(object): # You can override this to use OrderedDict or other dict-like types. dict_type = dict def _do_get_result(self): super(DictCursorMixin, self)._do_get_result() fields = [] if self.description: for f in self._result.fields: name = f.name if name in fields: name = f.table_name + '.' + name fields.append(name) self._fields = fields if fields and self._rows: self._rows = [self._conv_row(r) for r in self._rows] def _conv_row(self, row): if row is None: return None return self.dict_type(zip(self._fields, row)) class DictCursor(DictCursorMixin, Cursor): """A cursor which returns results as a dictionary""" class SSCursor(Cursor): """ Unbuffered Cursor, mainly useful for queries that return a lot of data, or for connections to remote servers over a slow network. Instead of copying every row of data into a buffer, this will fetch rows as needed. The upside of this is the client uses much less memory, and rows are returned much faster when traveling over a slow network or if the result set is very big. There are limitations, though. The MySQL protocol doesn't support returning the total number of rows, so the only way to tell how many rows there are is to iterate over every row returned. Also, it currently isn't possible to scroll backwards, as only the current row is held in memory. """ _defer_warnings = True def _conv_row(self, row): return row def close(self): conn = self.connection if conn is None: return if self._result is not None and self._result is conn._result: self._result._finish_unbuffered_query() try: while self.nextset(): pass finally: self.connection = None __del__ = close def _query(self, q): conn = self._get_db() self._last_executed = q self._clear_result() conn.query(q, unbuffered=True) self._do_get_result() return self.rowcount def nextset(self): return self._nextset(unbuffered=True) def read_next(self): """Read next row""" return self._conv_row(self._result._read_rowdata_packet_unbuffered()) def fetchone(self): """Fetch next row""" self._check_executed() row = self.read_next() if row is None: self._show_warnings() return None self.rownumber += 1 return row def fetchall(self): """ Fetch all, as per MySQLdb. Pretty useless for large queries, as it is buffered. See fetchall_unbuffered(), if you want an unbuffered generator version of this method. """ return list(self.fetchall_unbuffered()) def fetchall_unbuffered(self): """ Fetch all, implemented as a generator, which isn't to standard, however, it doesn't make sense to return everything in a list, as that would use ridiculous memory for large result sets. """ return iter(self.fetchone, None) def __iter__(self): return self.fetchall_unbuffered() def fetchmany(self, size=None): """Fetch many""" self._check_executed() if size is None: size = self.arraysize rows = [] for i in range_type(size): row = self.read_next() if row is None: self._show_warnings() break rows.append(row) self.rownumber += 1 return rows def scroll(self, value, mode='relative'): self._check_executed() if mode == 'relative': if value < 0: raise err.NotSupportedError( "Backwards scrolling not supported by this cursor") for _ in range_type(value): self.read_next() self.rownumber += value elif mode == 'absolute': if value < self.rownumber: raise err.NotSupportedError( "Backwards scrolling not supported by this cursor") end = value - self.rownumber for _ in range_type(end): self.read_next() self.rownumber = value else: raise err.ProgrammingError("unknown scroll mode %s" % mode) class SSDictCursor(DictCursorMixin, SSCursor): """An unbuffered cursor, which returns results as a dictionary""" PyMySQL-0.9.3/pymysql/err.py000066400000000000000000000072041340615555200156720ustar00rootroot00000000000000import struct from .constants import ER class MySQLError(Exception): """Exception related to operation with MySQL.""" class Warning(Warning, MySQLError): """Exception raised for important warnings like data truncations while inserting, etc.""" class Error(MySQLError): """Exception that is the base class of all other error exceptions (not Warning).""" class InterfaceError(Error): """Exception raised for errors that are related to the database interface rather than the database itself.""" class DatabaseError(Error): """Exception raised for errors that are related to the database.""" class DataError(DatabaseError): """Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range, etc.""" class OperationalError(DatabaseError): """Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer, e.g. an unexpected disconnect occurs, the data source name is not found, a transaction could not be processed, a memory allocation error occurred during processing, etc.""" class IntegrityError(DatabaseError): """Exception raised when the relational integrity of the database is affected, e.g. a foreign key check fails, duplicate key, etc.""" class InternalError(DatabaseError): """Exception raised when the database encounters an internal error, e.g. the cursor is not valid anymore, the transaction is out of sync, etc.""" class ProgrammingError(DatabaseError): """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.""" class NotSupportedError(DatabaseError): """Exception raised in case a method or database API was used which is not supported by the database, e.g. requesting a .rollback() on a connection that does not support transaction or has transactions turned off.""" error_map = {} def _map_error(exc, *errors): for error in errors: error_map[error] = exc _map_error(ProgrammingError, ER.DB_CREATE_EXISTS, ER.SYNTAX_ERROR, ER.PARSE_ERROR, ER.NO_SUCH_TABLE, ER.WRONG_DB_NAME, ER.WRONG_TABLE_NAME, ER.FIELD_SPECIFIED_TWICE, ER.INVALID_GROUP_FUNC_USE, ER.UNSUPPORTED_EXTENSION, ER.TABLE_MUST_HAVE_COLUMNS, ER.CANT_DO_THIS_DURING_AN_TRANSACTION, ER.WRONG_DB_NAME, ER.WRONG_COLUMN_NAME, ) _map_error(DataError, ER.WARN_DATA_TRUNCATED, ER.WARN_NULL_TO_NOTNULL, ER.WARN_DATA_OUT_OF_RANGE, ER.NO_DEFAULT, ER.PRIMARY_CANT_HAVE_NULL, ER.DATA_TOO_LONG, ER.DATETIME_FUNCTION_OVERFLOW) _map_error(IntegrityError, ER.DUP_ENTRY, ER.NO_REFERENCED_ROW, ER.NO_REFERENCED_ROW_2, ER.ROW_IS_REFERENCED, ER.ROW_IS_REFERENCED_2, ER.CANNOT_ADD_FOREIGN, ER.BAD_NULL_ERROR) _map_error(NotSupportedError, ER.WARNING_NOT_COMPLETE_ROLLBACK, ER.NOT_SUPPORTED_YET, ER.FEATURE_DISABLED, ER.UNKNOWN_STORAGE_ENGINE) _map_error(OperationalError, ER.DBACCESS_DENIED_ERROR, ER.ACCESS_DENIED_ERROR, ER.CON_COUNT_ERROR, ER.TABLEACCESS_DENIED_ERROR, ER.COLUMNACCESS_DENIED_ERROR, ER.CONSTRAINT_FAILED, ER.LOCK_DEADLOCK) del _map_error, ER def raise_mysql_exception(data): errno = struct.unpack('= 2 and value[0] == value[-1] == quote: return value[1:-1] return value def get(self, section, option): value = configparser.RawConfigParser.get(self, section, option) return self.__remove_quotes(value) PyMySQL-0.9.3/pymysql/protocol.py000066400000000000000000000273701340615555200167510ustar00rootroot00000000000000# Python implementation of low level MySQL client-server protocol # http://dev.mysql.com/doc/internals/en/client-server-protocol.html from __future__ import print_function from .charset import MBLENGTH from ._compat import PY2, range_type from .constants import FIELD_TYPE, SERVER_STATUS from . import err from .util import byte2int import struct import sys DEBUG = False NULL_COLUMN = 251 UNSIGNED_CHAR_COLUMN = 251 UNSIGNED_SHORT_COLUMN = 252 UNSIGNED_INT24_COLUMN = 253 UNSIGNED_INT64_COLUMN = 254 def dump_packet(data): # pragma: no cover def printable(data): if 32 <= byte2int(data) < 127: if isinstance(data, int): return chr(data) return data return '.' try: print("packet length:", len(data)) for i in range(1, 7): f = sys._getframe(i) print("call[%d]: %s (line %d)" % (i, f.f_code.co_name, f.f_lineno)) print("-" * 66) except ValueError: pass dump_data = [data[i:i+16] for i in range_type(0, min(len(data), 256), 16)] for d in dump_data: print(' '.join("{:02X}".format(byte2int(x)) for x in d) + ' ' * (16 - len(d)) + ' ' * 2 + ''.join(printable(x) for x in d)) print("-" * 66) print() class MysqlPacket(object): """Representation of a MySQL response packet. Provides an interface for reading/parsing the packet results. """ __slots__ = ('_position', '_data') def __init__(self, data, encoding): self._position = 0 self._data = data def get_all_data(self): return self._data def read(self, size): """Read the first 'size' bytes in packet and advance cursor past them.""" result = self._data[self._position:(self._position+size)] if len(result) != size: error = ('Result length not requested length:\n' 'Expected=%s. Actual=%s. Position: %s. Data Length: %s' % (size, len(result), self._position, len(self._data))) if DEBUG: print(error) self.dump() raise AssertionError(error) self._position += size return result def read_all(self): """Read all remaining data in the packet. (Subsequent read() will return errors.) """ result = self._data[self._position:] self._position = None # ensure no subsequent read() return result def advance(self, length): """Advance the cursor in data buffer 'length' bytes.""" new_position = self._position + length if new_position < 0 or new_position > len(self._data): raise Exception('Invalid advance amount (%s) for cursor. ' 'Position=%s' % (length, new_position)) self._position = new_position def rewind(self, position=0): """Set the position of the data buffer cursor to 'position'.""" if position < 0 or position > len(self._data): raise Exception("Invalid position to rewind cursor to: %s." % position) self._position = position def get_bytes(self, position, length=1): """Get 'length' bytes starting at 'position'. Position is start of payload (first four packet header bytes are not included) starting at index '0'. No error checking is done. If requesting outside end of buffer an empty string (or string shorter than 'length') may be returned! """ return self._data[position:(position+length)] if PY2: def read_uint8(self): result = ord(self._data[self._position]) self._position += 1 return result else: def read_uint8(self): result = self._data[self._position] self._position += 1 return result def read_uint16(self): result = struct.unpack_from('= 7 def is_eof_packet(self): # http://dev.mysql.com/doc/internals/en/generic-response-packets.html#packet-EOF_Packet # Caution: \xFE may be LengthEncodedInteger. # If \xFE is LengthEncodedInteger header, 8bytes followed. return self._data[0:1] == b'\xfe' and len(self._data) < 9 def is_auth_switch_request(self): # http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest return self._data[0:1] == b'\xfe' def is_extra_auth_data(self): # https://dev.mysql.com/doc/internals/en/successful-authentication.html return self._data[0:1] == b'\x01' def is_resultset_packet(self): field_count = ord(self._data[0:1]) return 1 <= field_count <= 250 def is_load_local_packet(self): return self._data[0:1] == b'\xfb' def is_error_packet(self): return self._data[0:1] == b'\xff' def check_error(self): if self.is_error_packet(): self.rewind() self.advance(1) # field_count == error (we already know that) errno = self.read_uint16() if DEBUG: print("errno =", errno) err.raise_mysql_exception(self._data) def dump(self): dump_packet(self._data) class FieldDescriptorPacket(MysqlPacket): """A MysqlPacket that represents a specific column's metadata in the result. Parsing is automatically done and the results are exported via public attributes on the class such as: db, table_name, name, length, type_code. """ def __init__(self, data, encoding): MysqlPacket.__init__(self, data, encoding) self._parse_field_descriptor(encoding) def _parse_field_descriptor(self, encoding): """Parse the 'Field Descriptor' (Metadata) packet. This is compatible with MySQL 4.1+ (not compatible with MySQL 4.0). """ self.catalog = self.read_length_coded_string() self.db = self.read_length_coded_string() self.table_name = self.read_length_coded_string().decode(encoding) self.org_table = self.read_length_coded_string().decode(encoding) self.name = self.read_length_coded_string().decode(encoding) self.org_name = self.read_length_coded_string().decode(encoding) self.charsetnr, self.length, self.type_code, self.flags, self.scale = ( self.read_struct('= version_tuple _connections = None @property def connections(self): if self._connections is None: self._connections = [] for params in self.databases: self._connections.append(pymysql.connect(**params)) self.addCleanup(self._teardown_connections) return self._connections def connect(self, **params): p = self.databases[0].copy() p.update(params) conn = pymysql.connect(**p) @self.addCleanup def teardown(): if conn.open: conn.close() return conn def _teardown_connections(self): if self._connections: for connection in self._connections: if connection.open: connection.close() self._connections = None def safe_create_table(self, connection, tablename, ddl, cleanup=True): """create a table. Ensures any existing version of that table is first dropped. Also adds a cleanup rule to drop the table after the test completes. """ cursor = connection.cursor() with warnings.catch_warnings(): warnings.simplefilter("ignore") cursor.execute("drop table if exists `%s`" % (tablename,)) cursor.execute(ddl) cursor.close() if cleanup: self.addCleanup(self.drop_table, connection, tablename) def drop_table(self, connection, tablename): cursor = connection.cursor() with warnings.catch_warnings(): warnings.simplefilter("ignore") cursor.execute("drop table if exists `%s`" % (tablename,)) cursor.close() def safe_gc_collect(self): """Ensure cycles are collected via gc. Runs additional times on non-CPython platforms. """ gc.collect() if not CPYTHON: gc.collect() PyMySQL-0.9.3/pymysql/tests/data/000077500000000000000000000000001340615555200166005ustar00rootroot00000000000000PyMySQL-0.9.3/pymysql/tests/data/load_local_data.txt000066400000000000000000003361311340615555200224320ustar00rootroot000000000000001,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 71,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, PyMySQL-0.9.3/pymysql/tests/data/load_local_warn_data.txt000066400000000000000000000003711340615555200234530ustar00rootroot000000000000001,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, ,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, 5,6, 7,8, 1,2, 3,4, PyMySQL-0.9.3/pymysql/tests/test_DictCursor.py000066400000000000000000000110071340615555200213600ustar00rootroot00000000000000from pymysql.tests import base import pymysql.cursors import datetime import warnings class TestDictCursor(base.PyMySQLTestCase): bob = {'name': 'bob', 'age': 21, 'DOB': datetime.datetime(1990, 2, 6, 23, 4, 56)} jim = {'name': 'jim', 'age': 56, 'DOB': datetime.datetime(1955, 5, 9, 13, 12, 45)} fred = {'name': 'fred', 'age': 100, 'DOB': datetime.datetime(1911, 9, 12, 1, 1, 1)} cursor_type = pymysql.cursors.DictCursor def setUp(self): super(TestDictCursor, self).setUp() self.conn = conn = self.connect() c = conn.cursor(self.cursor_type) # create a table ane some data to query with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists dictcursor") # include in filterwarnings since for unbuffered dict cursor warning for lack of table # will only be propagated at start of next execute() call c.execute("""CREATE TABLE dictcursor (name char(20), age int , DOB datetime)""") data = [("bob", 21, "1990-02-06 23:04:56"), ("jim", 56, "1955-05-09 13:12:45"), ("fred", 100, "1911-09-12 01:01:01")] c.executemany("insert into dictcursor values (%s,%s,%s)", data) def tearDown(self): c = self.conn.cursor() c.execute("drop table dictcursor") super(TestDictCursor, self).tearDown() def _ensure_cursor_expired(self, cursor): pass def test_DictCursor(self): bob, jim, fred = self.bob.copy(), self.jim.copy(), self.fred.copy() #all assert test compare to the structure as would come out from MySQLdb conn = self.conn c = conn.cursor(self.cursor_type) # try an update which should return no rows c.execute("update dictcursor set age=20 where name='bob'") bob['age'] = 20 # pull back the single row dict for bob and check c.execute("SELECT * from dictcursor where name='bob'") r = c.fetchone() self.assertEqual(bob, r, "fetchone via DictCursor failed") self._ensure_cursor_expired(c) # same again, but via fetchall => tuple) c.execute("SELECT * from dictcursor where name='bob'") r = c.fetchall() self.assertEqual([bob], r, "fetch a 1 row result via fetchall failed via DictCursor") # same test again but iterate over the c.execute("SELECT * from dictcursor where name='bob'") for r in c: self.assertEqual(bob, r, "fetch a 1 row result via iteration failed via DictCursor") # get all 3 row via fetchall c.execute("SELECT * from dictcursor") r = c.fetchall() self.assertEqual([bob,jim,fred], r, "fetchall failed via DictCursor") #same test again but do a list comprehension c.execute("SELECT * from dictcursor") r = list(c) self.assertEqual([bob,jim,fred], r, "DictCursor should be iterable") # get all 2 row via fetchmany c.execute("SELECT * from dictcursor") r = c.fetchmany(2) self.assertEqual([bob, jim], r, "fetchmany failed via DictCursor") self._ensure_cursor_expired(c) def test_custom_dict(self): class MyDict(dict): pass class MyDictCursor(self.cursor_type): dict_type = MyDict keys = ['name', 'age', 'DOB'] bob = MyDict([(k, self.bob[k]) for k in keys]) jim = MyDict([(k, self.jim[k]) for k in keys]) fred = MyDict([(k, self.fred[k]) for k in keys]) cur = self.conn.cursor(MyDictCursor) cur.execute("SELECT * FROM dictcursor WHERE name='bob'") r = cur.fetchone() self.assertEqual(bob, r, "fetchone() returns MyDictCursor") self._ensure_cursor_expired(cur) cur.execute("SELECT * FROM dictcursor") r = cur.fetchall() self.assertEqual([bob, jim, fred], r, "fetchall failed via MyDictCursor") cur.execute("SELECT * FROM dictcursor") r = list(cur) self.assertEqual([bob, jim, fred], r, "list failed via MyDictCursor") cur.execute("SELECT * FROM dictcursor") r = cur.fetchmany(2) self.assertEqual([bob, jim], r, "list failed via MyDictCursor") self._ensure_cursor_expired(cur) class TestSSDictCursor(TestDictCursor): cursor_type = pymysql.cursors.SSDictCursor def _ensure_cursor_expired(self, cursor): list(cursor.fetchall_unbuffered()) if __name__ == "__main__": import unittest unittest.main() PyMySQL-0.9.3/pymysql/tests/test_SSCursor.py000066400000000000000000000072661340615555200210360ustar00rootroot00000000000000import sys try: from pymysql.tests import base import pymysql.cursors from pymysql.constants import CLIENT except Exception: # For local testing from top-level directory, without installing sys.path.append('../pymysql') from pymysql.tests import base import pymysql.cursors from pymysql.constants import CLIENT class TestSSCursor(base.PyMySQLTestCase): def test_SSCursor(self): affected_rows = 18446744073709551615 conn = self.connect(client_flag=CLIENT.MULTI_STATEMENTS) data = [ ('America', '', 'America/Jamaica'), ('America', '', 'America/Los_Angeles'), ('America', '', 'America/Lima'), ('America', '', 'America/New_York'), ('America', '', 'America/Menominee'), ('America', '', 'America/Havana'), ('America', '', 'America/El_Salvador'), ('America', '', 'America/Costa_Rica'), ('America', '', 'America/Denver'), ('America', '', 'America/Detroit'),] cursor = conn.cursor(pymysql.cursors.SSCursor) # Create table cursor.execute('CREATE TABLE tz_data (' 'region VARCHAR(64),' 'zone VARCHAR(64),' 'name VARCHAR(64))') conn.begin() # Test INSERT for i in data: cursor.execute('INSERT INTO tz_data VALUES (%s, %s, %s)', i) self.assertEqual(conn.affected_rows(), 1, 'affected_rows does not match') conn.commit() # Test fetchone() iter = 0 cursor.execute('SELECT * FROM tz_data') while True: row = cursor.fetchone() if row is None: break iter += 1 # Test cursor.rowcount self.assertEqual(cursor.rowcount, affected_rows, 'cursor.rowcount != %s' % (str(affected_rows))) # Test cursor.rownumber self.assertEqual(cursor.rownumber, iter, 'cursor.rowcount != %s' % (str(iter))) # Test row came out the same as it went in self.assertEqual((row in data), True, 'Row not found in source data') # Test fetchall cursor.execute('SELECT * FROM tz_data') self.assertEqual(len(cursor.fetchall()), len(data), 'fetchall failed. Number of rows does not match') # Test fetchmany cursor.execute('SELECT * FROM tz_data') self.assertEqual(len(cursor.fetchmany(2)), 2, 'fetchmany failed. Number of rows does not match') # So MySQLdb won't throw "Commands out of sync" while True: res = cursor.fetchone() if res is None: break # Test update, affected_rows() cursor.execute('UPDATE tz_data SET zone = %s', ['Foo']) conn.commit() self.assertEqual(cursor.rowcount, len(data), 'Update failed. affected_rows != %s' % (str(len(data)))) # Test executemany cursor.executemany('INSERT INTO tz_data VALUES (%s, %s, %s)', data) self.assertEqual(cursor.rowcount, len(data), 'executemany failed. cursor.rowcount != %s' % (str(len(data)))) # Test multiple datasets cursor.execute('SELECT 1; SELECT 2; SELECT 3') self.assertListEqual(list(cursor), [(1, )]) self.assertTrue(cursor.nextset()) self.assertListEqual(list(cursor), [(2, )]) self.assertTrue(cursor.nextset()) self.assertListEqual(list(cursor), [(3, )]) self.assertFalse(cursor.nextset()) cursor.execute('DROP TABLE IF EXISTS tz_data') cursor.close() __all__ = ["TestSSCursor"] if __name__ == "__main__": import unittest unittest.main() PyMySQL-0.9.3/pymysql/tests/test_basic.py000066400000000000000000000347321340615555200203720ustar00rootroot00000000000000# coding: utf-8 import datetime import json import time import warnings from unittest2 import SkipTest from pymysql import util import pymysql.cursors from pymysql.tests import base from pymysql.err import ProgrammingError __all__ = ["TestConversion", "TestCursor", "TestBulkInserts"] class TestConversion(base.PyMySQLTestCase): def test_datatypes(self): """ test every data type """ conn = self.connect() c = conn.cursor() c.execute("create table test_datatypes (b bit, i int, l bigint, f real, s varchar(32), u varchar(32), bb blob, d date, dt datetime, ts timestamp, td time, t time, st datetime)") try: # insert values v = (True, -3, 123456789012, 5.7, "hello'\" world", u"Espa\xc3\xb1ol", "binary\x00data".encode(conn.encoding), datetime.date(1988,2,2), datetime.datetime(2014, 5, 15, 7, 45, 57), datetime.timedelta(5,6), datetime.time(16,32), time.localtime()) c.execute("insert into test_datatypes (b,i,l,f,s,u,bb,d,dt,td,t,st) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)", v) c.execute("select b,i,l,f,s,u,bb,d,dt,td,t,st from test_datatypes") r = c.fetchone() self.assertEqual(util.int2byte(1), r[0]) self.assertEqual(v[1:10], r[1:10]) self.assertEqual(datetime.timedelta(0, 60 * (v[10].hour * 60 + v[10].minute)), r[10]) self.assertEqual(datetime.datetime(*v[-1][:6]), r[-1]) c.execute("delete from test_datatypes") # check nulls c.execute("insert into test_datatypes (b,i,l,f,s,u,bb,d,dt,td,t,st) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)", [None] * 12) c.execute("select b,i,l,f,s,u,bb,d,dt,td,t,st from test_datatypes") r = c.fetchone() self.assertEqual(tuple([None] * 12), r) c.execute("delete from test_datatypes") # check sequences type for seq_type in (tuple, list, set, frozenset): c.execute("insert into test_datatypes (i, l) values (2,4), (6,8), (10,12)") seq = seq_type([2,6]) c.execute("select l from test_datatypes where i in %s order by i", (seq,)) r = c.fetchall() self.assertEqual(((4,),(8,)), r) c.execute("delete from test_datatypes") finally: c.execute("drop table test_datatypes") def test_dict(self): """ test dict escaping """ conn = self.connect() c = conn.cursor() c.execute("create table test_dict (a integer, b integer, c integer)") try: c.execute("insert into test_dict (a,b,c) values (%(a)s, %(b)s, %(c)s)", {"a":1,"b":2,"c":3}) c.execute("select a,b,c from test_dict") self.assertEqual((1,2,3), c.fetchone()) finally: c.execute("drop table test_dict") def test_string(self): conn = self.connect() c = conn.cursor() c.execute("create table test_dict (a text)") test_value = "I am a test string" try: c.execute("insert into test_dict (a) values (%s)", test_value) c.execute("select a from test_dict") self.assertEqual((test_value,), c.fetchone()) finally: c.execute("drop table test_dict") def test_integer(self): conn = self.connect() c = conn.cursor() c.execute("create table test_dict (a integer)") test_value = 12345 try: c.execute("insert into test_dict (a) values (%s)", test_value) c.execute("select a from test_dict") self.assertEqual((test_value,), c.fetchone()) finally: c.execute("drop table test_dict") def test_binary(self): """test binary data""" data = bytes(bytearray(range(255))) conn = self.connect() self.safe_create_table( conn, "test_binary", "create table test_binary (b binary(255))") with conn.cursor() as c: c.execute("insert into test_binary (b) values (_binary %s)", (data,)) c.execute("select b from test_binary") self.assertEqual(data, c.fetchone()[0]) def test_blob(self): """test blob data""" data = bytes(bytearray(range(256)) * 4) conn = self.connect() self.safe_create_table( conn, "test_blob", "create table test_blob (b blob)") with conn.cursor() as c: c.execute("insert into test_blob (b) values (_binary %s)", (data,)) c.execute("select b from test_blob") self.assertEqual(data, c.fetchone()[0]) def test_untyped(self): """ test conversion of null, empty string """ conn = self.connect() c = conn.cursor() c.execute("select null,''") self.assertEqual((None,u''), c.fetchone()) c.execute("select '',null") self.assertEqual((u'',None), c.fetchone()) def test_timedelta(self): """ test timedelta conversion """ conn = self.connect() c = conn.cursor() c.execute("select time('12:30'), time('23:12:59'), time('23:12:59.05100'), time('-12:30'), time('-23:12:59'), time('-23:12:59.05100'), time('-00:30')") self.assertEqual((datetime.timedelta(0, 45000), datetime.timedelta(0, 83579), datetime.timedelta(0, 83579, 51000), -datetime.timedelta(0, 45000), -datetime.timedelta(0, 83579), -datetime.timedelta(0, 83579, 51000), -datetime.timedelta(0, 1800)), c.fetchone()) def test_datetime_microseconds(self): """ test datetime conversion w microseconds""" conn = self.connect() if not self.mysql_server_is(conn, (5, 6, 4)): raise SkipTest("target backend does not support microseconds") c = conn.cursor() dt = datetime.datetime(2013, 11, 12, 9, 9, 9, 123450) c.execute("create table test_datetime (id int, ts datetime(6))") try: c.execute( "insert into test_datetime values (%s, %s)", (1, dt) ) c.execute("select ts from test_datetime") self.assertEqual((dt,), c.fetchone()) finally: c.execute("drop table test_datetime") class TestCursor(base.PyMySQLTestCase): # this test case does not work quite right yet, however, # we substitute in None for the erroneous field which is # compatible with the DB-API 2.0 spec and has not broken # any unit tests for anything we've tried. #def test_description(self): # """ test description attribute """ # # result is from MySQLdb module # r = (('Host', 254, 11, 60, 60, 0, 0), # ('User', 254, 16, 16, 16, 0, 0), # ('Password', 254, 41, 41, 41, 0, 0), # ('Select_priv', 254, 1, 1, 1, 0, 0), # ('Insert_priv', 254, 1, 1, 1, 0, 0), # ('Update_priv', 254, 1, 1, 1, 0, 0), # ('Delete_priv', 254, 1, 1, 1, 0, 0), # ('Create_priv', 254, 1, 1, 1, 0, 0), # ('Drop_priv', 254, 1, 1, 1, 0, 0), # ('Reload_priv', 254, 1, 1, 1, 0, 0), # ('Shutdown_priv', 254, 1, 1, 1, 0, 0), # ('Process_priv', 254, 1, 1, 1, 0, 0), # ('File_priv', 254, 1, 1, 1, 0, 0), # ('Grant_priv', 254, 1, 1, 1, 0, 0), # ('References_priv', 254, 1, 1, 1, 0, 0), # ('Index_priv', 254, 1, 1, 1, 0, 0), # ('Alter_priv', 254, 1, 1, 1, 0, 0), # ('Show_db_priv', 254, 1, 1, 1, 0, 0), # ('Super_priv', 254, 1, 1, 1, 0, 0), # ('Create_tmp_table_priv', 254, 1, 1, 1, 0, 0), # ('Lock_tables_priv', 254, 1, 1, 1, 0, 0), # ('Execute_priv', 254, 1, 1, 1, 0, 0), # ('Repl_slave_priv', 254, 1, 1, 1, 0, 0), # ('Repl_client_priv', 254, 1, 1, 1, 0, 0), # ('Create_view_priv', 254, 1, 1, 1, 0, 0), # ('Show_view_priv', 254, 1, 1, 1, 0, 0), # ('Create_routine_priv', 254, 1, 1, 1, 0, 0), # ('Alter_routine_priv', 254, 1, 1, 1, 0, 0), # ('Create_user_priv', 254, 1, 1, 1, 0, 0), # ('Event_priv', 254, 1, 1, 1, 0, 0), # ('Trigger_priv', 254, 1, 1, 1, 0, 0), # ('ssl_type', 254, 0, 9, 9, 0, 0), # ('ssl_cipher', 252, 0, 65535, 65535, 0, 0), # ('x509_issuer', 252, 0, 65535, 65535, 0, 0), # ('x509_subject', 252, 0, 65535, 65535, 0, 0), # ('max_questions', 3, 1, 11, 11, 0, 0), # ('max_updates', 3, 1, 11, 11, 0, 0), # ('max_connections', 3, 1, 11, 11, 0, 0), # ('max_user_connections', 3, 1, 11, 11, 0, 0)) # conn = self.connect() # c = conn.cursor() # c.execute("select * from mysql.user") # # self.assertEqual(r, c.description) def test_fetch_no_result(self): """ test a fetchone() with no rows """ conn = self.connect() c = conn.cursor() c.execute("create table test_nr (b varchar(32))") try: data = "pymysql" c.execute("insert into test_nr (b) values (%s)", (data,)) self.assertEqual(None, c.fetchone()) finally: c.execute("drop table test_nr") def test_aggregates(self): """ test aggregate functions """ conn = self.connect() c = conn.cursor() try: c.execute('create table test_aggregates (i integer)') for i in range(0, 10): c.execute('insert into test_aggregates (i) values (%s)', (i,)) c.execute('select sum(i) from test_aggregates') r, = c.fetchone() self.assertEqual(sum(range(0,10)), r) finally: c.execute('drop table test_aggregates') def test_single_tuple(self): """ test a single tuple """ conn = self.connect() c = conn.cursor() self.safe_create_table( conn, 'mystuff', "create table mystuff (id integer primary key)") c.execute("insert into mystuff (id) values (1)") c.execute("insert into mystuff (id) values (2)") c.execute("select id from mystuff where id in %s", ((1,),)) self.assertEqual([(1,)], list(c.fetchall())) c.close() def test_json(self): args = self.databases[0].copy() args["charset"] = "utf8mb4" conn = pymysql.connect(**args) if not self.mysql_server_is(conn, (5, 7, 0)): raise SkipTest("JSON type is not supported on MySQL <= 5.6") self.safe_create_table(conn, "test_json", """\ create table test_json ( id int not null, json JSON not null, primary key (id) );""") cur = conn.cursor() json_str = u'{"hello": "こんにちは"}' cur.execute("INSERT INTO test_json (id, `json`) values (42, %s)", (json_str,)) cur.execute("SELECT `json` from `test_json` WHERE `id`=42") res = cur.fetchone()[0] self.assertEqual(json.loads(res), json.loads(json_str)) cur.execute("SELECT CAST(%s AS JSON) AS x", (json_str,)) res = cur.fetchone()[0] self.assertEqual(json.loads(res), json.loads(json_str)) class TestBulkInserts(base.PyMySQLTestCase): cursor_type = pymysql.cursors.DictCursor def setUp(self): super(TestBulkInserts, self).setUp() self.conn = conn = self.connect() c = conn.cursor(self.cursor_type) # create a table ane some data to query self.safe_create_table(conn, 'bulkinsert', """\ CREATE TABLE bulkinsert ( id int(11), name char(20), age int, height int, PRIMARY KEY (id) ) """) def _verify_records(self, data): conn = self.connect() cursor = conn.cursor() cursor.execute("SELECT id, name, age, height from bulkinsert") result = cursor.fetchall() self.assertEqual(sorted(data), sorted(result)) def test_bulk_insert(self): conn = self.connect() cursor = conn.cursor() data = [(0, "bob", 21, 123), (1, "jim", 56, 45), (2, "fred", 100, 180)] cursor.executemany("insert into bulkinsert (id, name, age, height) " "values (%s,%s,%s,%s)", data) self.assertEqual( cursor._last_executed, bytearray( b"insert into bulkinsert (id, name, age, height) values " b"(0,'bob',21,123),(1,'jim',56,45),(2,'fred',100,180)")) cursor.execute('commit') self._verify_records(data) def test_bulk_insert_multiline_statement(self): conn = self.connect() cursor = conn.cursor() data = [(0, "bob", 21, 123), (1, "jim", 56, 45), (2, "fred", 100, 180)] cursor.executemany("""insert into bulkinsert (id, name, age, height) values (%s, %s , %s, %s ) """, data) self.assertEqual(cursor._last_executed.strip(), bytearray(b"""insert into bulkinsert (id, name, age, height) values (0, 'bob' , 21, 123 ),(1, 'jim' , 56, 45 ),(2, 'fred' , 100, 180 )""")) cursor.execute('commit') self._verify_records(data) def test_bulk_insert_single_record(self): conn = self.connect() cursor = conn.cursor() data = [(0, "bob", 21, 123)] cursor.executemany("insert into bulkinsert (id, name, age, height) " "values (%s,%s,%s,%s)", data) cursor.execute('commit') self._verify_records(data) def test_issue_288(self): """executemany should work with "insert ... on update" """ conn = self.connect() cursor = conn.cursor() data = [(0, "bob", 21, 123), (1, "jim", 56, 45), (2, "fred", 100, 180)] cursor.executemany("""insert into bulkinsert (id, name, age, height) values (%s, %s , %s, %s ) on duplicate key update age = values(age) """, data) self.assertEqual(cursor._last_executed.strip(), bytearray(b"""insert into bulkinsert (id, name, age, height) values (0, 'bob' , 21, 123 ),(1, 'jim' , 56, 45 ),(2, 'fred' , 100, 180 ) on duplicate key update age = values(age)""")) cursor.execute('commit') self._verify_records(data) def test_warnings(self): con = self.connect() cur = con.cursor() with warnings.catch_warnings(record=True) as ws: warnings.simplefilter("always") cur.execute("drop table if exists no_exists_table") self.assertEqual(len(ws), 1) self.assertEqual(ws[0].category, pymysql.Warning) if u"no_exists_table" not in str(ws[0].message): self.fail("'no_exists_table' not in %s" % (str(ws[0].message),)) PyMySQL-0.9.3/pymysql/tests/test_connection.py000066400000000000000000000576161340615555200214560ustar00rootroot00000000000000import datetime import sys import time import unittest2 import pymysql from pymysql.tests import base from pymysql._compat import text_type from pymysql.constants import CLIENT import pytest class TempUser: def __init__(self, c, user, db, auth=None, authdata=None, password=None): self._c = c self._user = user self._db = db create = "CREATE USER " + user if password is not None: create += " IDENTIFIED BY '%s'" % password elif auth is not None: create += " IDENTIFIED WITH %s" % auth if authdata is not None: create += " AS '%s'" % authdata try: c.execute(create) self._created = True except pymysql.err.InternalError: # already exists - TODO need to check the same plugin applies self._created = False try: c.execute("GRANT SELECT ON %s.* TO %s" % (db, user)) self._grant = True except pymysql.err.InternalError: self._grant = False def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): if self._grant: self._c.execute("REVOKE SELECT ON %s.* FROM %s" % (self._db, self._user)) if self._created: self._c.execute("DROP USER %s" % self._user) class TestAuthentication(base.PyMySQLTestCase): socket_auth = False socket_found = False two_questions_found = False three_attempts_found = False pam_found = False mysql_old_password_found = False sha256_password_found = False import os osuser = os.environ.get('USER') # socket auth requires the current user and for the connection to be a socket # rest do grants @localhost due to incomplete logic - TODO change to @% then db = base.PyMySQLTestCase.databases[0].copy() socket_auth = db.get('unix_socket') is not None \ and db.get('host') in ('localhost', '127.0.0.1') cur = pymysql.connect(**db).cursor() del db['user'] cur.execute("SHOW PLUGINS") for r in cur: if (r[1], r[2]) != (u'ACTIVE', u'AUTHENTICATION'): continue if r[3] == u'auth_socket.so': socket_plugin_name = r[0] socket_found = True elif r[3] == u'dialog_examples.so': if r[0] == 'two_questions': two_questions_found = True elif r[0] == 'three_attempts': three_attempts_found = True elif r[0] == u'pam': pam_found = True pam_plugin_name = r[3].split('.')[0] if pam_plugin_name == 'auth_pam': pam_plugin_name = 'pam' # MySQL: authentication_pam # https://dev.mysql.com/doc/refman/5.5/en/pam-authentication-plugin.html # MariaDB: pam # https://mariadb.com/kb/en/mariadb/pam-authentication-plugin/ # Names differ but functionality is close elif r[0] == u'mysql_old_password': mysql_old_password_found = True elif r[0] == u'sha256_password': sha256_password_found = True #else: # print("plugin: %r" % r[0]) def test_plugin(self): conn = self.connect() if not self.mysql_server_is(conn, (5, 5, 0)): raise unittest2.SkipTest("MySQL-5.5 required for plugins") cur = conn.cursor() cur.execute("select plugin from mysql.user where concat(user, '@', host)=current_user()") for r in cur: self.assertIn(conn._auth_plugin_name, (r[0], 'mysql_native_password')) @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipIf(socket_found, "socket plugin already installed") def testSocketAuthInstallPlugin(self): # needs plugin. lets install it. cur = self.connect().cursor() try: cur.execute("install plugin auth_socket soname 'auth_socket.so'") TestAuthentication.socket_found = True self.socket_plugin_name = 'auth_socket' self.realtestSocketAuth() except pymysql.err.InternalError: try: cur.execute("install soname 'auth_socket'") TestAuthentication.socket_found = True self.socket_plugin_name = 'unix_socket' self.realtestSocketAuth() except pymysql.err.InternalError: TestAuthentication.socket_found = False raise unittest2.SkipTest('we couldn\'t install the socket plugin') finally: if TestAuthentication.socket_found: cur.execute("uninstall plugin %s" % self.socket_plugin_name) @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipUnless(socket_found, "no socket plugin") def testSocketAuth(self): self.realtestSocketAuth() def realtestSocketAuth(self): with TempUser(self.connect().cursor(), TestAuthentication.osuser + '@localhost', self.databases[0]['db'], self.socket_plugin_name) as u: c = pymysql.connect(user=TestAuthentication.osuser, **self.db) class Dialog(object): fail=False def __init__(self, con): self.fail=TestAuthentication.Dialog.fail pass def prompt(self, echo, prompt): if self.fail: self.fail=False return b'bad guess at a password' return self.m.get(prompt) class DialogHandler(object): def __init__(self, con): self.con=con def authenticate(self, pkt): while True: flag = pkt.read_uint8() echo = (flag & 0x06) == 0x02 last = (flag & 0x01) == 0x01 prompt = pkt.read_all() if prompt == b'Password, please:': self.con.write_packet(b'stillnotverysecret\0') else: self.con.write_packet(b'no idea what to do with this prompt\0') pkt = self.con._read_packet() pkt.check_error() if pkt.is_ok_packet() or last: break return pkt class DefectiveHandler(object): def __init__(self, con): self.con=con @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipIf(two_questions_found, "two_questions plugin already installed") def testDialogAuthTwoQuestionsInstallPlugin(self): # needs plugin. lets install it. cur = self.connect().cursor() try: cur.execute("install plugin two_questions soname 'dialog_examples.so'") TestAuthentication.two_questions_found = True self.realTestDialogAuthTwoQuestions() except pymysql.err.InternalError: raise unittest2.SkipTest('we couldn\'t install the two_questions plugin') finally: if TestAuthentication.two_questions_found: cur.execute("uninstall plugin two_questions") @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipUnless(two_questions_found, "no two questions auth plugin") def testDialogAuthTwoQuestions(self): self.realTestDialogAuthTwoQuestions() def realTestDialogAuthTwoQuestions(self): TestAuthentication.Dialog.fail=False TestAuthentication.Dialog.m = {b'Password, please:': b'notverysecret', b'Are you sure ?': b'yes, of course'} with TempUser(self.connect().cursor(), 'pymysql_2q@localhost', self.databases[0]['db'], 'two_questions', 'notverysecret') as u: with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_2q', **self.db) pymysql.connect(user='pymysql_2q', auth_plugin_map={b'dialog': TestAuthentication.Dialog}, **self.db) @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipIf(three_attempts_found, "three_attempts plugin already installed") def testDialogAuthThreeAttemptsQuestionsInstallPlugin(self): # needs plugin. lets install it. cur = self.connect().cursor() try: cur.execute("install plugin three_attempts soname 'dialog_examples.so'") TestAuthentication.three_attempts_found = True self.realTestDialogAuthThreeAttempts() except pymysql.err.InternalError: raise unittest2.SkipTest('we couldn\'t install the three_attempts plugin') finally: if TestAuthentication.three_attempts_found: cur.execute("uninstall plugin three_attempts") @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipUnless(three_attempts_found, "no three attempts plugin") def testDialogAuthThreeAttempts(self): self.realTestDialogAuthThreeAttempts() def realTestDialogAuthThreeAttempts(self): TestAuthentication.Dialog.m = {b'Password, please:': b'stillnotverysecret'} TestAuthentication.Dialog.fail=True # fail just once. We've got three attempts after all with TempUser(self.connect().cursor(), 'pymysql_3a@localhost', self.databases[0]['db'], 'three_attempts', 'stillnotverysecret') as u: pymysql.connect(user='pymysql_3a', auth_plugin_map={b'dialog': TestAuthentication.Dialog}, **self.db) pymysql.connect(user='pymysql_3a', auth_plugin_map={b'dialog': TestAuthentication.DialogHandler}, **self.db) with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_3a', auth_plugin_map={b'dialog': object}, **self.db) with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_3a', auth_plugin_map={b'dialog': TestAuthentication.DefectiveHandler}, **self.db) with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_3a', auth_plugin_map={b'notdialogplugin': TestAuthentication.Dialog}, **self.db) TestAuthentication.Dialog.m = {b'Password, please:': b'I do not know'} with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_3a', auth_plugin_map={b'dialog': TestAuthentication.Dialog}, **self.db) TestAuthentication.Dialog.m = {b'Password, please:': None} with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_3a', auth_plugin_map={b'dialog': TestAuthentication.Dialog}, **self.db) @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipIf(pam_found, "pam plugin already installed") @unittest2.skipIf(os.environ.get('PASSWORD') is None, "PASSWORD env var required") @unittest2.skipIf(os.environ.get('PAMSERVICE') is None, "PAMSERVICE env var required") def testPamAuthInstallPlugin(self): # needs plugin. lets install it. cur = self.connect().cursor() try: cur.execute("install plugin pam soname 'auth_pam.so'") TestAuthentication.pam_found = True self.realTestPamAuth() except pymysql.err.InternalError: raise unittest2.SkipTest('we couldn\'t install the auth_pam plugin') finally: if TestAuthentication.pam_found: cur.execute("uninstall plugin pam") @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipUnless(pam_found, "no pam plugin") @unittest2.skipIf(os.environ.get('PASSWORD') is None, "PASSWORD env var required") @unittest2.skipIf(os.environ.get('PAMSERVICE') is None, "PAMSERVICE env var required") def testPamAuth(self): self.realTestPamAuth() def realTestPamAuth(self): db = self.db.copy() import os db['password'] = os.environ.get('PASSWORD') cur = self.connect().cursor() try: cur.execute('show grants for ' + TestAuthentication.osuser + '@localhost') grants = cur.fetchone()[0] cur.execute('drop user ' + TestAuthentication.osuser + '@localhost') except pymysql.OperationalError as e: # assuming the user doesn't exist which is ok too self.assertEqual(1045, e.args[0]) grants = None with TempUser(cur, TestAuthentication.osuser + '@localhost', self.databases[0]['db'], 'pam', os.environ.get('PAMSERVICE')) as u: try: c = pymysql.connect(user=TestAuthentication.osuser, **db) db['password'] = 'very bad guess at password' with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user=TestAuthentication.osuser, auth_plugin_map={b'mysql_cleartext_password': TestAuthentication.DefectiveHandler}, **self.db) except pymysql.OperationalError as e: self.assertEqual(1045, e.args[0]) # we had 'bad guess at password' work with pam. Well at least we get a permission denied here with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user=TestAuthentication.osuser, auth_plugin_map={b'mysql_cleartext_password': TestAuthentication.DefectiveHandler}, **self.db) if grants: # recreate the user cur.execute(grants) # select old_password("crummy p\tassword"); #| old_password("crummy p\tassword") | #| 2a01785203b08770 | @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipUnless(mysql_old_password_found, "no mysql_old_password plugin") def testMySQLOldPasswordAuth(self): conn = self.connect() if self.mysql_server_is(conn, (5, 7, 0)): raise unittest2.SkipTest('Old passwords aren\'t supported in 5.7') # pymysql.err.OperationalError: (1045, "Access denied for user 'old_pass_user'@'localhost' (using password: YES)") # from login in MySQL-5.6 if self.mysql_server_is(conn, (5, 6, 0)): raise unittest2.SkipTest('Old passwords don\'t authenticate in 5.6') db = self.db.copy() db['password'] = "crummy p\tassword" c = conn.cursor() # deprecated in 5.6 if sys.version_info[0:2] >= (3,2) and self.mysql_server_is(conn, (5, 6, 0)): with self.assertWarns(pymysql.err.Warning) as cm: c.execute("SELECT OLD_PASSWORD('%s')" % db['password']) else: c.execute("SELECT OLD_PASSWORD('%s')" % db['password']) v = c.fetchone()[0] self.assertEqual(v, '2a01785203b08770') # only works in MariaDB and MySQL-5.6 - can't separate out by version #if self.mysql_server_is(self.connect(), (5, 5, 0)): # with TempUser(c, 'old_pass_user@localhost', # self.databases[0]['db'], 'mysql_old_password', '2a01785203b08770') as u: # cur = pymysql.connect(user='old_pass_user', **db).cursor() # cur.execute("SELECT VERSION()") c.execute("SELECT @@secure_auth") secure_auth_setting = c.fetchone()[0] c.execute('set old_passwords=1') # pymysql.err.Warning: 'pre-4.1 password hash' is deprecated and will be removed in a future release. Please use post-4.1 password hash instead if sys.version_info[0:2] >= (3,2) and self.mysql_server_is(conn, (5, 6, 0)): with self.assertWarns(pymysql.err.Warning) as cm: c.execute('set global secure_auth=0') else: c.execute('set global secure_auth=0') with TempUser(c, 'old_pass_user@localhost', self.databases[0]['db'], password=db['password']) as u: cur = pymysql.connect(user='old_pass_user', **db).cursor() cur.execute("SELECT VERSION()") c.execute('set global secure_auth=%r' % secure_auth_setting) @unittest2.skipUnless(socket_auth, "connection to unix_socket required") @unittest2.skipUnless(sha256_password_found, "no sha256 password authentication plugin found") def testAuthSHA256(self): conn = self.connect() c = conn.cursor() with TempUser(c, 'pymysql_sha256@localhost', self.databases[0]['db'], 'sha256_password') as u: if self.mysql_server_is(conn, (5, 7, 0)): c.execute("SET PASSWORD FOR 'pymysql_sha256'@'localhost' ='Sh@256Pa33'") else: c.execute('SET old_passwords = 2') c.execute("SET PASSWORD FOR 'pymysql_sha256'@'localhost' = PASSWORD('Sh@256Pa33')") c.execute("FLUSH PRIVILEGES") db = self.db.copy() db['password'] = "Sh@256Pa33" # Although SHA256 is supported, need the configuration of public key of the mysql server. Currently will get error by this test. with self.assertRaises(pymysql.err.OperationalError): pymysql.connect(user='pymysql_sha256', **db) class TestConnection(base.PyMySQLTestCase): def test_utf8mb4(self): """This test requires MySQL >= 5.5""" arg = self.databases[0].copy() arg['charset'] = 'utf8mb4' conn = pymysql.connect(**arg) def test_largedata(self): """Large query and response (>=16MB)""" cur = self.connect().cursor() cur.execute("SELECT @@max_allowed_packet") if cur.fetchone()[0] < 16*1024*1024 + 10: print("Set max_allowed_packet to bigger than 17MB") return t = 'a' * (16*1024*1024) cur.execute("SELECT '" + t + "'") assert cur.fetchone()[0] == t def test_autocommit(self): con = self.connect() self.assertFalse(con.get_autocommit()) cur = con.cursor() cur.execute("SET AUTOCOMMIT=1") self.assertTrue(con.get_autocommit()) con.autocommit(False) self.assertFalse(con.get_autocommit()) cur.execute("SELECT @@AUTOCOMMIT") self.assertEqual(cur.fetchone()[0], 0) def test_select_db(self): con = self.connect() current_db = self.databases[0]['db'] other_db = self.databases[1]['db'] cur = con.cursor() cur.execute('SELECT database()') self.assertEqual(cur.fetchone()[0], current_db) con.select_db(other_db) cur.execute('SELECT database()') self.assertEqual(cur.fetchone()[0], other_db) def test_connection_gone_away(self): """ http://dev.mysql.com/doc/refman/5.0/en/gone-away.html http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html#error_cr_server_gone_error """ con = self.connect() cur = con.cursor() cur.execute("SET wait_timeout=1") time.sleep(2) with self.assertRaises(pymysql.OperationalError) as cm: cur.execute("SELECT 1+1") # error occures while reading, not writing because of socket buffer. #self.assertEqual(cm.exception.args[0], 2006) self.assertIn(cm.exception.args[0], (2006, 2013)) def test_init_command(self): conn = self.connect( init_command='SELECT "bar"; SELECT "baz"', client_flag=CLIENT.MULTI_STATEMENTS) c = conn.cursor() c.execute('select "foobar";') self.assertEqual(('foobar',), c.fetchone()) conn.close() with self.assertRaises(pymysql.err.Error): conn.ping(reconnect=False) def test_read_default_group(self): conn = self.connect( read_default_group='client', ) self.assertTrue(conn.open) def test_context(self): with self.assertRaises(ValueError): c = self.connect() with pytest.warns(DeprecationWarning): with c as cur: cur.execute('create table test ( a int ) ENGINE=InnoDB') c.begin() cur.execute('insert into test values ((1))') raise ValueError('pseudo abort') c = self.connect() with pytest.warns(DeprecationWarning): with c as cur: cur.execute('select count(*) from test') self.assertEqual(0, cur.fetchone()[0]) cur.execute('insert into test values ((1))') with pytest.warns(DeprecationWarning): with c as cur: cur.execute('select count(*) from test') self.assertEqual(1,cur.fetchone()[0]) cur.execute('drop table test') def test_set_charset(self): c = self.connect() c.set_charset('utf8mb4') # TODO validate setting here def test_defer_connect(self): import socket d = self.databases[0].copy() try: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(d['unix_socket']) except KeyError: sock.close() sock = socket.create_connection( (d.get('host', 'localhost'), d.get('port', 3306))) for k in ['unix_socket', 'host', 'port']: try: del d[k] except KeyError: pass c = pymysql.connect(defer_connect=True, **d) self.assertFalse(c.open) c.connect(sock) c.close() sock.close() # A custom type and function to escape it class Foo(object): value = "bar" def escape_foo(x, d): return x.value class TestEscape(base.PyMySQLTestCase): def test_escape_string(self): con = self.connect() cur = con.cursor() self.assertEqual(con.escape("foo'bar"), "'foo\\'bar'") # added NO_AUTO_CREATE_USER as not including it in 5.7 generates warnings # mysql-8.0 removes the option however if self.mysql_server_is(con, (8, 0, 0)): cur.execute("SET sql_mode='NO_BACKSLASH_ESCAPES'") else: cur.execute("SET sql_mode='NO_BACKSLASH_ESCAPES,NO_AUTO_CREATE_USER'") self.assertEqual(con.escape("foo'bar"), "'foo''bar'") def test_escape_builtin_encoders(self): con = self.connect() cur = con.cursor() val = datetime.datetime(2012, 3, 4, 5, 6) self.assertEqual(con.escape(val, con.encoders), "'2012-03-04 05:06:00'") def test_escape_custom_object(self): con = self.connect() cur = con.cursor() mapping = {Foo: escape_foo} self.assertEqual(con.escape(Foo(), mapping), "bar") def test_escape_fallback_encoder(self): con = self.connect() cur = con.cursor() class Custom(str): pass mapping = {text_type: pymysql.escape_string} self.assertEqual(con.escape(Custom('foobar'), mapping), "'foobar'") def test_escape_no_default(self): con = self.connect() cur = con.cursor() self.assertRaises(TypeError, con.escape, 42, {}) def test_escape_dict_value(self): con = self.connect() cur = con.cursor() mapping = con.encoders.copy() mapping[Foo] = escape_foo self.assertEqual(con.escape({'foo': Foo()}, mapping), {'foo': "bar"}) def test_escape_list_item(self): con = self.connect() cur = con.cursor() mapping = con.encoders.copy() mapping[Foo] = escape_foo self.assertEqual(con.escape([Foo()], mapping), "(bar)") def test_previous_cursor_not_closed(self): con = self.connect( init_command='SELECT "bar"; SELECT "baz"', client_flag=CLIENT.MULTI_STATEMENTS) cur1 = con.cursor() cur1.execute("SELECT 1; SELECT 2") cur2 = con.cursor() cur2.execute("SELECT 3") self.assertEqual(cur2.fetchone()[0], 3) def test_commit_during_multi_result(self): con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS) cur = con.cursor() cur.execute("SELECT 1; SELECT 2") con.commit() cur.execute("SELECT 3") self.assertEqual(cur.fetchone()[0], 3) PyMySQL-0.9.3/pymysql/tests/test_converters.py000066400000000000000000000042141340615555200214730ustar00rootroot00000000000000import datetime from unittest import TestCase from pymysql._compat import PY2 from pymysql import converters __all__ = ["TestConverter"] class TestConverter(TestCase): def test_escape_string(self): self.assertEqual( converters.escape_string(u"foo\nbar"), u"foo\\nbar" ) if PY2: def test_escape_string_bytes(self): self.assertEqual( converters.escape_string(b"foo\nbar"), b"foo\\nbar" ) def test_convert_datetime(self): expected = datetime.datetime(2007, 2, 24, 23, 6, 20) dt = converters.convert_datetime('2007-02-24 23:06:20') self.assertEqual(dt, expected) def test_convert_datetime_with_fsp(self): expected = datetime.datetime(2007, 2, 24, 23, 6, 20, 511581) dt = converters.convert_datetime('2007-02-24 23:06:20.511581') self.assertEqual(dt, expected) def _test_convert_timedelta(self, with_negate=False, with_fsp=False): d = {'hours': 789, 'minutes': 12, 'seconds': 34} s = '%(hours)s:%(minutes)s:%(seconds)s' % d if with_fsp: d['microseconds'] = 511581 s += '.%(microseconds)s' % d expected = datetime.timedelta(**d) if with_negate: expected = -expected s = '-' + s tdelta = converters.convert_timedelta(s) self.assertEqual(tdelta, expected) def test_convert_timedelta(self): self._test_convert_timedelta(with_negate=False, with_fsp=False) self._test_convert_timedelta(with_negate=True, with_fsp=False) def test_convert_timedelta_with_fsp(self): self._test_convert_timedelta(with_negate=False, with_fsp=True) self._test_convert_timedelta(with_negate=False, with_fsp=True) def test_convert_time(self): expected = datetime.time(23, 6, 20) time_obj = converters.convert_time('23:06:20') self.assertEqual(time_obj, expected) def test_convert_time_with_fsp(self): expected = datetime.time(23, 6, 20, 511581) time_obj = converters.convert_time('23:06:20.511581') self.assertEqual(time_obj, expected) PyMySQL-0.9.3/pymysql/tests/test_cursor.py000066400000000000000000000101341340615555200206140ustar00rootroot00000000000000import warnings from pymysql.tests import base import pymysql.cursors class CursorTest(base.PyMySQLTestCase): def setUp(self): super(CursorTest, self).setUp() conn = self.connect() self.safe_create_table( conn, "test", "create table test (data varchar(10))", ) cursor = conn.cursor() cursor.execute( "insert into test (data) values " "('row1'), ('row2'), ('row3'), ('row4'), ('row5')") cursor.close() self.test_connection = pymysql.connect(**self.databases[0]) self.addCleanup(self.test_connection.close) def test_cleanup_rows_unbuffered(self): conn = self.test_connection cursor = conn.cursor(pymysql.cursors.SSCursor) cursor.execute("select * from test as t1, test as t2") for counter, row in enumerate(cursor): if counter > 10: break del cursor self.safe_gc_collect() c2 = conn.cursor() c2.execute("select 1") self.assertEqual(c2.fetchone(), (1,)) self.assertIsNone(c2.fetchone()) def test_cleanup_rows_buffered(self): conn = self.test_connection cursor = conn.cursor(pymysql.cursors.Cursor) cursor.execute("select * from test as t1, test as t2") for counter, row in enumerate(cursor): if counter > 10: break del cursor self.safe_gc_collect() c2 = conn.cursor() c2.execute("select 1") self.assertEqual( c2.fetchone(), (1,) ) self.assertIsNone(c2.fetchone()) def test_executemany(self): conn = self.test_connection cursor = conn.cursor(pymysql.cursors.Cursor) m = pymysql.cursors.RE_INSERT_VALUES.match("INSERT INTO TEST (ID, NAME) VALUES (%s, %s)") self.assertIsNotNone(m, 'error parse %s') self.assertEqual(m.group(3), '', 'group 3 not blank, bug in RE_INSERT_VALUES?') m = pymysql.cursors.RE_INSERT_VALUES.match("INSERT INTO TEST (ID, NAME) VALUES (%(id)s, %(name)s)") self.assertIsNotNone(m, 'error parse %(name)s') self.assertEqual(m.group(3), '', 'group 3 not blank, bug in RE_INSERT_VALUES?') m = pymysql.cursors.RE_INSERT_VALUES.match("INSERT INTO TEST (ID, NAME) VALUES (%(id_name)s, %(name)s)") self.assertIsNotNone(m, 'error parse %(id_name)s') self.assertEqual(m.group(3), '', 'group 3 not blank, bug in RE_INSERT_VALUES?') m = pymysql.cursors.RE_INSERT_VALUES.match("INSERT INTO TEST (ID, NAME) VALUES (%(id_name)s, %(name)s) ON duplicate update") self.assertIsNotNone(m, 'error parse %(id_name)s') self.assertEqual(m.group(3), ' ON duplicate update', 'group 3 not ON duplicate update, bug in RE_INSERT_VALUES?') # https://github.com/PyMySQL/PyMySQL/pull/597 m = pymysql.cursors.RE_INSERT_VALUES.match("INSERT INTO bloup(foo, bar)VALUES(%s, %s)") assert m is not None # cursor._executed must bee "insert into test (data) values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)" # list args data = range(10) cursor.executemany("insert into test (data) values (%s)", data) self.assertTrue(cursor._executed.endswith(b",(7),(8),(9)"), 'execute many with %s not in one query') # dict args data_dict = [{'data': i} for i in range(10)] cursor.executemany("insert into test (data) values (%(data)s)", data_dict) self.assertTrue(cursor._executed.endswith(b",(7),(8),(9)"), 'execute many with %(data)s not in one query') # %% in column set cursor.execute("""\ CREATE TABLE percent_test ( `A%` INTEGER, `B%` INTEGER)""") try: q = "INSERT INTO percent_test (`A%%`, `B%%`) VALUES (%s, %s)" self.assertIsNotNone(pymysql.cursors.RE_INSERT_VALUES.match(q)) cursor.executemany(q, [(3, 4), (5, 6)]) self.assertTrue(cursor._executed.endswith(b"(3, 4),(5, 6)"), "executemany with %% not in one query") finally: cursor.execute("DROP TABLE IF EXISTS percent_test") PyMySQL-0.9.3/pymysql/tests/test_err.py000066400000000000000000000012361340615555200200720ustar00rootroot00000000000000import unittest2 from pymysql import err __all__ = ["TestRaiseException"] class TestRaiseException(unittest2.TestCase): def test_raise_mysql_exception(self): data = b"\xff\x15\x04Access denied" with self.assertRaises(err.OperationalError) as cm: err.raise_mysql_exception(data) self.assertEqual(cm.exception.args, (1045, 'Access denied')) def test_raise_mysql_exception_client_protocol_41(self): data = b"\xff\x15\x04#28000Access denied" with self.assertRaises(err.OperationalError) as cm: err.raise_mysql_exception(data) self.assertEqual(cm.exception.args, (1045, 'Access denied')) PyMySQL-0.9.3/pymysql/tests/test_issues.py000066400000000000000000000472041340615555200206220ustar00rootroot00000000000000import datetime import time import warnings import sys import pymysql from pymysql import cursors from pymysql._compat import text_type from pymysql.tests import base import unittest2 try: import imp reload = imp.reload except AttributeError: pass __all__ = ["TestOldIssues", "TestNewIssues", "TestGitHubIssues"] class TestOldIssues(base.PyMySQLTestCase): def test_issue_3(self): """ undefined methods datetime_or_None, date_or_None """ conn = self.connect() c = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue3") c.execute("create table issue3 (d date, t time, dt datetime, ts timestamp)") try: c.execute("insert into issue3 (d, t, dt, ts) values (%s,%s,%s,%s)", (None, None, None, None)) c.execute("select d from issue3") self.assertEqual(None, c.fetchone()[0]) c.execute("select t from issue3") self.assertEqual(None, c.fetchone()[0]) c.execute("select dt from issue3") self.assertEqual(None, c.fetchone()[0]) c.execute("select ts from issue3") self.assertIn(type(c.fetchone()[0]), (type(None), datetime.datetime), 'expected Python type None or datetime from SQL timestamp') finally: c.execute("drop table issue3") def test_issue_4(self): """ can't retrieve TIMESTAMP fields """ conn = self.connect() c = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue4") c.execute("create table issue4 (ts timestamp)") try: c.execute("insert into issue4 (ts) values (now())") c.execute("select ts from issue4") self.assertTrue(isinstance(c.fetchone()[0], datetime.datetime)) finally: c.execute("drop table issue4") def test_issue_5(self): """ query on information_schema.tables fails """ con = self.connect() cur = con.cursor() cur.execute("select * from information_schema.tables") def test_issue_6(self): """ exception: TypeError: ord() expected a character, but string of length 0 found """ # ToDo: this test requires access to db 'mysql'. kwargs = self.databases[0].copy() kwargs['db'] = "mysql" conn = pymysql.connect(**kwargs) c = conn.cursor() c.execute("select * from user") conn.close() def test_issue_8(self): """ Primary Key and Index error when selecting data """ conn = self.connect() c = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists test") c.execute("""CREATE TABLE `test` (`station` int(10) NOT NULL DEFAULT '0', `dh` datetime NOT NULL DEFAULT '2015-01-01 00:00:00', `echeance` int(1) NOT NULL DEFAULT '0', `me` double DEFAULT NULL, `mo` double DEFAULT NULL, PRIMARY KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""") try: self.assertEqual(0, c.execute("SELECT * FROM test")) c.execute("ALTER TABLE `test` ADD INDEX `idx_station` (`station`)") self.assertEqual(0, c.execute("SELECT * FROM test")) finally: c.execute("drop table test") def test_issue_9(self): """ sets DeprecationWarning in Python 2.6 """ try: reload(pymysql) except DeprecationWarning: self.fail() def test_issue_13(self): """ can't handle large result fields """ conn = self.connect() cur = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") cur.execute("drop table if exists issue13") try: cur.execute("create table issue13 (t text)") # ticket says 18k size = 18*1024 cur.execute("insert into issue13 (t) values (%s)", ("x" * size,)) cur.execute("select t from issue13") # use assertTrue so that obscenely huge error messages don't print r = cur.fetchone()[0] self.assertTrue("x" * size == r) finally: cur.execute("drop table issue13") def test_issue_15(self): """ query should be expanded before perform character encoding """ conn = self.connect() c = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue15") c.execute("create table issue15 (t varchar(32))") try: c.execute("insert into issue15 (t) values (%s)", (u'\xe4\xf6\xfc',)) c.execute("select t from issue15") self.assertEqual(u'\xe4\xf6\xfc', c.fetchone()[0]) finally: c.execute("drop table issue15") def test_issue_16(self): """ Patch for string and tuple escaping """ conn = self.connect() c = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue16") c.execute("create table issue16 (name varchar(32) primary key, email varchar(32))") try: c.execute("insert into issue16 (name, email) values ('pete', 'floydophone')") c.execute("select email from issue16 where name=%s", ("pete",)) self.assertEqual("floydophone", c.fetchone()[0]) finally: c.execute("drop table issue16") @unittest2.skip("test_issue_17() requires a custom, legacy MySQL configuration and will not be run.") def test_issue_17(self): """could not connect mysql use passwod""" conn = self.connect() host = self.databases[0]["host"] db = self.databases[0]["db"] c = conn.cursor() # grant access to a table to a user with a password try: with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue17") c.execute("create table issue17 (x varchar(32) primary key)") c.execute("insert into issue17 (x) values ('hello, world!')") c.execute("grant all privileges on %s.issue17 to 'issue17user'@'%%' identified by '1234'" % db) conn.commit() conn2 = pymysql.connect(host=host, user="issue17user", passwd="1234", db=db) c2 = conn2.cursor() c2.execute("select x from issue17") self.assertEqual("hello, world!", c2.fetchone()[0]) finally: c.execute("drop table issue17") class TestNewIssues(base.PyMySQLTestCase): def test_issue_34(self): try: pymysql.connect(host="localhost", port=1237, user="root") self.fail() except pymysql.OperationalError as e: self.assertEqual(2003, e.args[0]) except Exception: self.fail() def test_issue_33(self): conn = pymysql.connect(charset="utf8", **self.databases[0]) self.safe_create_table(conn, u'hei\xdfe', u'create table hei\xdfe (name varchar(32))') c = conn.cursor() c.execute(u"insert into hei\xdfe (name) values ('Pi\xdfata')") c.execute(u"select name from hei\xdfe") self.assertEqual(u"Pi\xdfata", c.fetchone()[0]) @unittest2.skip("This test requires manual intervention") def test_issue_35(self): conn = self.connect() c = conn.cursor() print("sudo killall -9 mysqld within the next 10 seconds") try: c.execute("select sleep(10)") self.fail() except pymysql.OperationalError as e: self.assertEqual(2013, e.args[0]) def test_issue_36(self): # connection 0 is super user, connection 1 isn't conn = self.connections[1] c = conn.cursor() c.execute("show processlist") kill_id = None for row in c.fetchall(): id = row[0] info = row[7] if info == "show processlist": kill_id = id break self.assertEqual(kill_id, conn.thread_id()) # now nuke the connection self.connections[0].kill(kill_id) # make sure this connection has broken try: c.execute("show tables") self.fail() except Exception: pass c.close() conn.close() # check the process list from the other connection try: # Wait since Travis-CI sometimes fail this test. time.sleep(0.1) c = self.connections[0].cursor() c.execute("show processlist") ids = [row[0] for row in c.fetchall()] self.assertFalse(kill_id in ids) finally: del self.connections[1] def test_issue_37(self): conn = self.connect() c = conn.cursor() self.assertEqual(1, c.execute("SELECT @foo")) self.assertEqual((None,), c.fetchone()) self.assertEqual(0, c.execute("SET @foo = 'bar'")) c.execute("set @foo = 'bar'") def test_issue_38(self): conn = self.connect() c = conn.cursor() datum = "a" * 1024 * 1023 # reduced size for most default mysql installs try: with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue38") c.execute("create table issue38 (id integer, data mediumblob)") c.execute("insert into issue38 values (1, %s)", (datum,)) finally: c.execute("drop table issue38") def disabled_test_issue_54(self): conn = self.connect() c = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue54") big_sql = "select * from issue54 where " big_sql += " and ".join("%d=%d" % (i,i) for i in range(0, 100000)) try: c.execute("create table issue54 (id integer primary key)") c.execute("insert into issue54 (id) values (7)") c.execute(big_sql) self.assertEqual(7, c.fetchone()[0]) finally: c.execute("drop table issue54") class TestGitHubIssues(base.PyMySQLTestCase): def test_issue_66(self): """ 'Connection' object has no attribute 'insert_id' """ conn = self.connect() c = conn.cursor() self.assertEqual(0, conn.insert_id()) try: with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists issue66") c.execute("create table issue66 (id integer primary key auto_increment, x integer)") c.execute("insert into issue66 (x) values (1)") c.execute("insert into issue66 (x) values (1)") self.assertEqual(2, conn.insert_id()) finally: c.execute("drop table issue66") def test_issue_79(self): """ Duplicate field overwrites the previous one in the result of DictCursor """ conn = self.connect() c = conn.cursor(pymysql.cursors.DictCursor) with warnings.catch_warnings(): warnings.filterwarnings("ignore") c.execute("drop table if exists a") c.execute("drop table if exists b") c.execute("""CREATE TABLE a (id int, value int)""") c.execute("""CREATE TABLE b (id int, value int)""") a=(1,11) b=(1,22) try: c.execute("insert into a values (%s, %s)", a) c.execute("insert into b values (%s, %s)", b) c.execute("SELECT * FROM a inner join b on a.id = b.id") r = c.fetchall()[0] self.assertEqual(r['id'], 1) self.assertEqual(r['value'], 11) self.assertEqual(r['b.value'], 22) finally: c.execute("drop table a") c.execute("drop table b") def test_issue_95(self): """ Leftover trailing OK packet for "CALL my_sp" queries """ conn = self.connect() cur = conn.cursor() with warnings.catch_warnings(): warnings.filterwarnings("ignore") cur.execute("DROP PROCEDURE IF EXISTS `foo`") cur.execute("""CREATE PROCEDURE `foo` () BEGIN SELECT 1; END""") try: cur.execute("""CALL foo()""") cur.execute("""SELECT 1""") self.assertEqual(cur.fetchone()[0], 1) finally: with warnings.catch_warnings(): warnings.filterwarnings("ignore") cur.execute("DROP PROCEDURE IF EXISTS `foo`") def test_issue_114(self): """ autocommit is not set after reconnecting with ping() """ conn = pymysql.connect(charset="utf8", **self.databases[0]) conn.autocommit(False) c = conn.cursor() c.execute("""select @@autocommit;""") self.assertFalse(c.fetchone()[0]) conn.close() conn.ping() c.execute("""select @@autocommit;""") self.assertFalse(c.fetchone()[0]) conn.close() # Ensure autocommit() is still working conn = pymysql.connect(charset="utf8", **self.databases[0]) c = conn.cursor() c.execute("""select @@autocommit;""") self.assertFalse(c.fetchone()[0]) conn.close() conn.ping() conn.autocommit(True) c.execute("""select @@autocommit;""") self.assertTrue(c.fetchone()[0]) conn.close() def test_issue_175(self): """ The number of fields returned by server is read in wrong way """ conn = self.connect() cur = conn.cursor() for length in (200, 300): columns = ', '.join('c{0} integer'.format(i) for i in range(length)) sql = 'create table test_field_count ({0})'.format(columns) try: cur.execute(sql) cur.execute('select * from test_field_count') assert len(cur.description) == length finally: with warnings.catch_warnings(): warnings.filterwarnings("ignore") cur.execute('drop table if exists test_field_count') def test_issue_321(self): """ Test iterable as query argument. """ conn = pymysql.connect(charset="utf8", **self.databases[0]) self.safe_create_table( conn, "issue321", "create table issue321 (value_1 varchar(1), value_2 varchar(1))") sql_insert = "insert into issue321 (value_1, value_2) values (%s, %s)" sql_dict_insert = ("insert into issue321 (value_1, value_2) " "values (%(value_1)s, %(value_2)s)") sql_select = ("select * from issue321 where " "value_1 in %s and value_2=%s") data = [ [(u"a", ), u"\u0430"], [[u"b"], u"\u0430"], {"value_1": [[u"c"]], "value_2": u"\u0430"} ] cur = conn.cursor() self.assertEqual(cur.execute(sql_insert, data[0]), 1) self.assertEqual(cur.execute(sql_insert, data[1]), 1) self.assertEqual(cur.execute(sql_dict_insert, data[2]), 1) self.assertEqual( cur.execute(sql_select, [(u"a", u"b", u"c"), u"\u0430"]), 3) self.assertEqual(cur.fetchone(), (u"a", u"\u0430")) self.assertEqual(cur.fetchone(), (u"b", u"\u0430")) self.assertEqual(cur.fetchone(), (u"c", u"\u0430")) def test_issue_364(self): """ Test mixed unicode/binary arguments in executemany. """ conn = pymysql.connect(charset="utf8mb4", **self.databases[0]) self.safe_create_table( conn, "issue364", "create table issue364 (value_1 binary(3), value_2 varchar(3)) " "engine=InnoDB default charset=utf8mb4") sql = "insert into issue364 (value_1, value_2) values (_binary %s, %s)" usql = u"insert into issue364 (value_1, value_2) values (_binary %s, %s)" values = [pymysql.Binary(b"\x00\xff\x00"), u"\xe4\xf6\xfc"] # test single insert and select cur = conn.cursor() cur.execute(sql, args=values) cur.execute("select * from issue364") self.assertEqual(cur.fetchone(), tuple(values)) # test single insert unicode query cur.execute(usql, args=values) # test multi insert and select cur.executemany(sql, args=(values, values, values)) cur.execute("select * from issue364") for row in cur.fetchall(): self.assertEqual(row, tuple(values)) # test multi insert with unicode query cur.executemany(usql, args=(values, values, values)) def test_issue_363(self): """ Test binary / geometry types. """ conn = pymysql.connect(charset="utf8", **self.databases[0]) self.safe_create_table( conn, "issue363", "CREATE TABLE issue363 ( " "id INTEGER PRIMARY KEY, geom LINESTRING NOT NULL /*!80003 SRID 0 */, " "SPATIAL KEY geom (geom)) " "ENGINE=MyISAM") cur = conn.cursor() # From MySQL 5.7, ST_GeomFromText is added and GeomFromText is deprecated. if self.mysql_server_is(conn, (5, 7, 0)): geom_from_text = "ST_GeomFromText" geom_as_text = "ST_AsText" geom_as_bin = "ST_AsBinary" else: geom_from_text = "GeomFromText" geom_as_text = "AsText" geom_as_bin = "AsBinary" query = ("INSERT INTO issue363 (id, geom) VALUES" "(1998, %s('LINESTRING(1.1 1.1,2.2 2.2)'))" % geom_from_text) cur.execute(query) # select WKT query = "SELECT %s(geom) FROM issue363" % geom_as_text cur.execute(query) row = cur.fetchone() self.assertEqual(row, ("LINESTRING(1.1 1.1,2.2 2.2)", )) # select WKB query = "SELECT %s(geom) FROM issue363" % geom_as_bin cur.execute(query) row = cur.fetchone() self.assertEqual(row, (b"\x01\x02\x00\x00\x00\x02\x00\x00\x00" b"\x9a\x99\x99\x99\x99\x99\xf1?" b"\x9a\x99\x99\x99\x99\x99\xf1?" b"\x9a\x99\x99\x99\x99\x99\x01@" b"\x9a\x99\x99\x99\x99\x99\x01@", )) # select internal binary cur.execute("SELECT geom FROM issue363") row = cur.fetchone() # don't assert the exact internal binary value, as it could # vary across implementations self.assertTrue(isinstance(row[0], bytes)) def test_issue_491(self): """ Test warning propagation """ conn = pymysql.connect(charset="utf8", **self.databases[0]) with warnings.catch_warnings(): # Ignore all warnings other than pymysql generated ones warnings.simplefilter("ignore") warnings.simplefilter("error", category=pymysql.Warning) # verify for both buffered and unbuffered cursor types for cursor_class in (cursors.Cursor, cursors.SSCursor): c = conn.cursor(cursor_class) try: c.execute("SELECT CAST('124b' AS SIGNED)") c.fetchall() except pymysql.Warning as e: # Warnings should have errorcode and string message, just like exceptions self.assertEqual(len(e.args), 2) self.assertEqual(e.args[0], 1292) self.assertTrue(isinstance(e.args[1], text_type)) else: self.fail("Should raise Warning") finally: c.close() PyMySQL-0.9.3/pymysql/tests/test_load_local.py000066400000000000000000000067521340615555200214030ustar00rootroot00000000000000from pymysql import cursors, OperationalError, Warning from pymysql.tests import base import os import warnings __all__ = ["TestLoadLocal"] class TestLoadLocal(base.PyMySQLTestCase): def test_no_file(self): """Test load local infile when the file does not exist""" conn = self.connect() c = conn.cursor() c.execute("CREATE TABLE test_load_local (a INTEGER, b INTEGER)") try: self.assertRaises( OperationalError, c.execute, ("LOAD DATA LOCAL INFILE 'no_data.txt' INTO TABLE " "test_load_local fields terminated by ','") ) finally: c.execute("DROP TABLE test_load_local") c.close() def test_load_file(self): """Test load local infile with a valid file""" conn = self.connect() c = conn.cursor() c.execute("CREATE TABLE test_load_local (a INTEGER, b INTEGER)") filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'load_local_data.txt') try: c.execute( ("LOAD DATA LOCAL INFILE '{0}' INTO TABLE " + "test_load_local FIELDS TERMINATED BY ','").format(filename) ) c.execute("SELECT COUNT(*) FROM test_load_local") self.assertEqual(22749, c.fetchone()[0]) finally: c.execute("DROP TABLE test_load_local") def test_unbuffered_load_file(self): """Test unbuffered load local infile with a valid file""" conn = self.connect() c = conn.cursor(cursors.SSCursor) c.execute("CREATE TABLE test_load_local (a INTEGER, b INTEGER)") filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'load_local_data.txt') try: c.execute( ("LOAD DATA LOCAL INFILE '{0}' INTO TABLE " + "test_load_local FIELDS TERMINATED BY ','").format(filename) ) c.execute("SELECT COUNT(*) FROM test_load_local") self.assertEqual(22749, c.fetchone()[0]) finally: c.close() conn.close() conn.connect() c = conn.cursor() c.execute("DROP TABLE test_load_local") def test_load_warnings(self): """Test load local infile produces the appropriate warnings""" conn = self.connect() c = conn.cursor() c.execute("CREATE TABLE test_load_local (a INTEGER, b INTEGER)") filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'load_local_warn_data.txt') try: with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') c.execute( ("LOAD DATA LOCAL INFILE '{0}' INTO TABLE " + "test_load_local FIELDS TERMINATED BY ','").format(filename) ) self.assertEqual(w[0].category, Warning) expected_message = "Incorrect integer value" if expected_message not in str(w[-1].message): self.fail("%r not in %r" % (expected_message, w[-1].message)) finally: c.execute("DROP TABLE test_load_local") c.close() if __name__ == "__main__": import unittest unittest.main() PyMySQL-0.9.3/pymysql/tests/test_nextset.py000066400000000000000000000050361340615555200207760ustar00rootroot00000000000000import unittest2 import pymysql from pymysql import util from pymysql.tests import base from pymysql.constants import CLIENT class TestNextset(base.PyMySQLTestCase): def test_nextset(self): con = self.connect( init_command='SELECT "bar"; SELECT "baz"', client_flag=CLIENT.MULTI_STATEMENTS) cur = con.cursor() cur.execute("SELECT 1; SELECT 2;") self.assertEqual([(1,)], list(cur)) r = cur.nextset() self.assertTrue(r) self.assertEqual([(2,)], list(cur)) self.assertIsNone(cur.nextset()) def test_skip_nextset(self): cur = self.connect(client_flag=CLIENT.MULTI_STATEMENTS).cursor() cur.execute("SELECT 1; SELECT 2;") self.assertEqual([(1,)], list(cur)) cur.execute("SELECT 42") self.assertEqual([(42,)], list(cur)) def test_nextset_error(self): con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS) cur = con.cursor() for i in range(3): cur.execute("SELECT %s; xyzzy;", (i,)) self.assertEqual([(i,)], list(cur)) with self.assertRaises(pymysql.ProgrammingError): cur.nextset() self.assertEqual((), cur.fetchall()) def test_ok_and_next(self): cur = self.connect(client_flag=CLIENT.MULTI_STATEMENTS).cursor() cur.execute("SELECT 1; commit; SELECT 2;") self.assertEqual([(1,)], list(cur)) self.assertTrue(cur.nextset()) self.assertTrue(cur.nextset()) self.assertEqual([(2,)], list(cur)) self.assertFalse(bool(cur.nextset())) @unittest2.expectedFailure def test_multi_cursor(self): con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS) cur1 = con.cursor() cur2 = con.cursor() cur1.execute("SELECT 1; SELECT 2;") cur2.execute("SELECT 42") self.assertEqual([(1,)], list(cur1)) self.assertEqual([(42,)], list(cur2)) r = cur1.nextset() self.assertTrue(r) self.assertEqual([(2,)], list(cur1)) self.assertIsNone(cur1.nextset()) def test_multi_statement_warnings(self): con = self.connect( init_command='SELECT "bar"; SELECT "baz"', client_flag=CLIENT.MULTI_STATEMENTS) cursor = con.cursor() try: cursor.execute('DROP TABLE IF EXISTS a; ' 'DROP TABLE IF EXISTS b;') except TypeError: self.fail() #TODO: How about SSCursor and nextset? # It's very hard to implement correctly... PyMySQL-0.9.3/pymysql/tests/test_optionfile.py000066400000000000000000000013411340615555200214470ustar00rootroot00000000000000from pymysql.optionfile import Parser from unittest import TestCase from pymysql._compat import PY2 try: from cStringIO import StringIO except ImportError: from io import StringIO __all__ = ['TestParser'] _cfg_file = (r""" [default] string = foo quoted = "bar" single_quoted = 'foobar' skip-slave-start """) class TestParser(TestCase): def test_string(self): parser = Parser() if PY2: parser.readfp(StringIO(_cfg_file)) else: parser.read_file(StringIO(_cfg_file)) self.assertEqual(parser.get("default", "string"), "foo") self.assertEqual(parser.get("default", "quoted"), "bar") self.assertEqual(parser.get("default", "single_quoted"), "foobar") PyMySQL-0.9.3/pymysql/tests/thirdparty/000077500000000000000000000000001340615555200200615ustar00rootroot00000000000000PyMySQL-0.9.3/pymysql/tests/thirdparty/__init__.py000066400000000000000000000002521340615555200221710ustar00rootroot00000000000000from .test_MySQLdb import * if __name__ == "__main__": try: import unittest2 as unittest except ImportError: import unittest unittest.main() PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/000077500000000000000000000000001340615555200223735ustar00rootroot00000000000000PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/__init__.py000066400000000000000000000003631340615555200245060ustar00rootroot00000000000000from .test_MySQLdb_capabilities import test_MySQLdb as test_capabilities from .test_MySQLdb_nonstandard import * from .test_MySQLdb_dbapi20 import test_MySQLdb as test_dbapi2 if __name__ == "__main__": import unittest unittest.main() PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/capabilities.py000066400000000000000000000237201340615555200254020ustar00rootroot00000000000000""" Script to test database capabilities and the DB-API interface for functionality and memory leaks. Adapted from a script by M-A Lemburg. """ import sys from time import time try: import unittest2 as unittest except ImportError: import unittest PY2 = sys.version_info[0] == 2 class DatabaseTest(unittest.TestCase): db_module = None connect_args = () connect_kwargs = dict(use_unicode=True, charset="utf8mb4", binary_prefix=True) create_table_extra = "ENGINE=INNODB CHARACTER SET UTF8MB4" rows = 10 debug = False def setUp(self): db = self.db_module.connect(*self.connect_args, **self.connect_kwargs) self.connection = db self.cursor = db.cursor() self.BLOBText = ''.join([chr(i) for i in range(256)] * 100); if PY2: self.BLOBUText = unicode().join(unichr(i) for i in range(16834)) else: self.BLOBUText = "".join(chr(i) for i in range(16834)) data = bytearray(range(256)) * 16 self.BLOBBinary = self.db_module.Binary(data) leak_test = True def tearDown(self): if self.leak_test: import gc del self.cursor orphans = gc.collect() self.assertFalse(orphans, "%d orphaned objects found after deleting cursor" % orphans) del self.connection orphans = gc.collect() self.assertFalse(orphans, "%d orphaned objects found after deleting connection" % orphans) def table_exists(self, name): try: self.cursor.execute('select * from %s where 1=0' % name) except Exception: return False else: return True def quote_identifier(self, ident): return '"%s"' % ident def new_table_name(self): i = id(self.cursor) while True: name = self.quote_identifier('tb%08x' % i) if not self.table_exists(name): return name i = i + 1 def create_table(self, columndefs): """ Create a table using a list of column definitions given in columndefs. generator must be a function taking arguments (row_number, col_number) returning a suitable data object for insertion into the table. """ self.table = self.new_table_name() self.cursor.execute('CREATE TABLE %s (%s) %s' % (self.table, ',\n'.join(columndefs), self.create_table_extra)) def check_data_integrity(self, columndefs, generator): # insert self.create_table(columndefs) insert_statement = ('INSERT INTO %s VALUES (%s)' % (self.table, ','.join(['%s'] * len(columndefs)))) data = [ [ generator(i,j) for j in range(len(columndefs)) ] for i in range(self.rows) ] if self.debug: print(data) self.cursor.executemany(insert_statement, data) self.connection.commit() # verify self.cursor.execute('select * from %s' % self.table) l = self.cursor.fetchall() if self.debug: print(l) self.assertEqual(len(l), self.rows) try: for i in range(self.rows): for j in range(len(columndefs)): self.assertEqual(l[i][j], generator(i,j)) finally: if not self.debug: self.cursor.execute('drop table %s' % (self.table)) def test_transactions(self): columndefs = ( 'col1 INT', 'col2 VARCHAR(255)') def generator(row, col): if col == 0: return row else: return ('%i' % (row%10))*255 self.create_table(columndefs) insert_statement = ('INSERT INTO %s VALUES (%s)' % (self.table, ','.join(['%s'] * len(columndefs)))) data = [ [ generator(i,j) for j in range(len(columndefs)) ] for i in range(self.rows) ] self.cursor.executemany(insert_statement, data) # verify self.connection.commit() self.cursor.execute('select * from %s' % self.table) l = self.cursor.fetchall() self.assertEqual(len(l), self.rows) for i in range(self.rows): for j in range(len(columndefs)): self.assertEqual(l[i][j], generator(i,j)) delete_statement = 'delete from %s where col1=%%s' % self.table self.cursor.execute(delete_statement, (0,)) self.cursor.execute('select col1 from %s where col1=%s' % \ (self.table, 0)) l = self.cursor.fetchall() self.assertFalse(l, "DELETE didn't work") self.connection.rollback() self.cursor.execute('select col1 from %s where col1=%s' % \ (self.table, 0)) l = self.cursor.fetchall() self.assertTrue(len(l) == 1, "ROLLBACK didn't work") self.cursor.execute('drop table %s' % (self.table)) def test_truncation(self): columndefs = ( 'col1 INT', 'col2 VARCHAR(255)') def generator(row, col): if col == 0: return row else: return ('%i' % (row%10))*((255-self.rows//2)+row) self.create_table(columndefs) insert_statement = ('INSERT INTO %s VALUES (%s)' % (self.table, ','.join(['%s'] * len(columndefs)))) try: self.cursor.execute(insert_statement, (0, '0'*256)) except Warning: if self.debug: print(self.cursor.messages) except self.connection.DataError: pass else: self.fail("Over-long column did not generate warnings/exception with single insert") self.connection.rollback() try: for i in range(self.rows): data = [] for j in range(len(columndefs)): data.append(generator(i,j)) self.cursor.execute(insert_statement,tuple(data)) except Warning: if self.debug: print(self.cursor.messages) except self.connection.DataError: pass else: self.fail("Over-long columns did not generate warnings/exception with execute()") self.connection.rollback() try: data = [ [ generator(i,j) for j in range(len(columndefs)) ] for i in range(self.rows) ] self.cursor.executemany(insert_statement, data) except Warning: if self.debug: print(self.cursor.messages) except self.connection.DataError: pass else: self.fail("Over-long columns did not generate warnings/exception with executemany()") self.connection.rollback() self.cursor.execute('drop table %s' % (self.table)) def test_CHAR(self): # Character data def generator(row,col): return ('%i' % ((row+col) % 10)) * 255 self.check_data_integrity( ('col1 char(255)','col2 char(255)'), generator) def test_INT(self): # Number data def generator(row,col): return row*row self.check_data_integrity( ('col1 INT',), generator) def test_DECIMAL(self): # DECIMAL def generator(row,col): from decimal import Decimal return Decimal("%d.%02d" % (row, col)) self.check_data_integrity( ('col1 DECIMAL(5,2)',), generator) def test_DATE(self): ticks = time() def generator(row,col): return self.db_module.DateFromTicks(ticks+row*86400-col*1313) self.check_data_integrity( ('col1 DATE',), generator) def test_TIME(self): ticks = time() def generator(row,col): return self.db_module.TimeFromTicks(ticks+row*86400-col*1313) self.check_data_integrity( ('col1 TIME',), generator) def test_DATETIME(self): ticks = time() def generator(row,col): return self.db_module.TimestampFromTicks(ticks+row*86400-col*1313) self.check_data_integrity( ('col1 DATETIME',), generator) def test_TIMESTAMP(self): ticks = time() def generator(row,col): return self.db_module.TimestampFromTicks(ticks+row*86400-col*1313) self.check_data_integrity( ('col1 TIMESTAMP',), generator) def test_fractional_TIMESTAMP(self): ticks = time() def generator(row,col): return self.db_module.TimestampFromTicks(ticks+row*86400-col*1313+row*0.7*col/3.0) self.check_data_integrity( ('col1 TIMESTAMP',), generator) def test_LONG(self): def generator(row,col): if col == 0: return row else: return self.BLOBUText # 'BLOB Text ' * 1024 self.check_data_integrity( ('col1 INT', 'col2 LONG'), generator) def test_TEXT(self): def generator(row,col): if col == 0: return row else: return self.BLOBUText[:5192] # 'BLOB Text ' * 1024 self.check_data_integrity( ('col1 INT', 'col2 TEXT'), generator) def test_LONG_BYTE(self): def generator(row,col): if col == 0: return row else: return self.BLOBBinary # 'BLOB\000Binary ' * 1024 self.check_data_integrity( ('col1 INT','col2 LONG BYTE'), generator) def test_BLOB(self): def generator(row,col): if col == 0: return row else: return self.BLOBBinary # 'BLOB\000Binary ' * 1024 self.check_data_integrity( ('col1 INT','col2 BLOB'), generator) PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/dbapi20.py000066400000000000000000000752771340615555200242100ustar00rootroot00000000000000''' 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 ' try: import unittest2 as unittest except ImportError: import unittest import time # $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 heirarchy correctly # - self.populate is now self._populate(), so if a driver stub # overrides self.ddl1 this change propogates # - VARCHAR columns now have a width, which will hopefully make the # DDL even more portible (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 # 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 compiliance 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 = None connect_args = () # List of arguments to pass to connect connect_kw_args = {} # 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 lowerfunc = 'lower' # Name of stored procedure to convert string->lowercase # 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: con.close() def _connect(self): try: return self.driver.connect( *self.connect_args,**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 heirarchy. 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: 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: 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]),7, 'cursor.description[x] tuples must have 7 elements' ) self.assertEqual(cur.description[0][0].lower(),'name', 'cursor.description[x][0] must return 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: con.close() def test_rowcount(self): con = self._connect() try: cur = con.cursor() self.executeDDL1(cur) self.assertEqual(cur.rowcount,-1, 'cursor.rowcount should be -1 after executing no-result ' 'statements' ) cur.execute("insert into %sbooze values ('Victoria Bitter')" % ( self.table_prefix )) self.assertTrue(cur.rowcount in (-1,1), 'cursor.rowcount should == number or rows inserted, or ' 'set to -1 after executing an insert statement' ) cur.execute("select name from %sbooze" % self.table_prefix) self.assertTrue(cur.rowcount in (-1,1), 'cursor.rowcount should == number of rows returned, or ' 'set to -1 after executing a select statement' ) self.executeDDL2(cur) self.assertEqual(cur.rowcount,-1, 'cursor.rowcount not being reset to -1 after executing ' 'no-result statements' ) finally: con.close() lower_func = 'lower' def test_callproc(self): con = self._connect() try: cur = con.cursor() if self.lower_func and hasattr(cur,'callproc'): r = cur.callproc(self.lower_func,('FOO',)) self.assertEqual(len(r),1) self.assertEqual(r[0],'FOO') r = cur.fetchall() self.assertEqual(len(r),1,'callproc produced no result set') self.assertEqual(len(r[0]),1, 'callproc produced invalid result set' ) self.assertEqual(r[0][0],'foo', 'callproc produced invalid results' ) finally: con.close() 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): 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() # 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 cannnot 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 cannnot 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_nextset(self): con = self._connect() try: cur = con.cursor() if not hasattr(cur,'nextset'): return try: self.executeDDL1(cur) sql=self._populate() for sql in self._populate(): cur.execute(sql) self.help_nextset_setUp(cur) cur.callproc('deleteme') numberofrows=cur.fetchone() assert numberofrows[0]== len(self.samples) assert cur.nextset() names=cur.fetchall() assert len(names) == len(self.samples) s=cur.nextset() assert s == None,'No more return sets, should return None' finally: self.help_nextset_tearDown(cur) finally: con.close() def test_nextset(self): raise NotImplementedError('Drivers need to override this test') 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_setoutputsize_basic(self): # Basic test is to make sure setoutputsize doesn't blow up con = self._connect() try: cur = con.cursor() cur.setoutputsize(1000) cur.setoutputsize(2000,0) self._paraminsert(cur) # Make sure the cursor still works finally: con.close() def test_setoutputsize(self): # Real test for setoutputsize is driver dependant raise NotImplementedError('Driver need to override this test') 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) 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) 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.assertEqual(str(t1),str(t2)) def test_Timestamp(self): t1 = self.driver.Timestamp(2002,12,25,13,45,30) t2 = self.driver.TimestampFromTicks( time.mktime((2002,12,25,13,45,30,0,0,0)) ) # 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') b = self.driver.Binary(b'') 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.' ) PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py000066400000000000000000000057331340615555200300000ustar00rootroot00000000000000from . import capabilities try: import unittest2 as unittest except ImportError: import unittest import pymysql from pymysql.tests import base import warnings warnings.filterwarnings('error') class test_MySQLdb(capabilities.DatabaseTest): db_module = pymysql connect_args = () connect_kwargs = base.PyMySQLTestCase.databases[0].copy() connect_kwargs.update(dict(read_default_file='~/.my.cnf', use_unicode=True, binary_prefix=True, charset='utf8mb4', sql_mode="ANSI,STRICT_TRANS_TABLES,TRADITIONAL")) leak_test = False def quote_identifier(self, ident): return "`%s`" % ident def test_TIME(self): from datetime import timedelta def generator(row,col): return timedelta(0, row*8000) self.check_data_integrity( ('col1 TIME',), generator) def test_TINYINT(self): # Number data def generator(row,col): v = (row*row) % 256 if v > 127: v = v-256 return v self.check_data_integrity( ('col1 TINYINT',), generator) def test_stored_procedures(self): db = self.connection c = self.cursor try: self.create_table(('pos INT', 'tree CHAR(20)')) c.executemany("INSERT INTO %s (pos,tree) VALUES (%%s,%%s)" % self.table, list(enumerate('ash birch cedar larch pine'.split()))) db.commit() c.execute(""" CREATE PROCEDURE test_sp(IN t VARCHAR(255)) BEGIN SELECT pos FROM %s WHERE tree = t; END """ % self.table) db.commit() c.callproc('test_sp', ('larch',)) rows = c.fetchall() self.assertEqual(len(rows), 1) self.assertEqual(rows[0][0], 3) c.nextset() finally: c.execute("DROP PROCEDURE IF EXISTS test_sp") c.execute('drop table %s' % (self.table)) def test_small_CHAR(self): # Character data def generator(row,col): i = ((row+1)*(col+1)+62)%256 if i == 62: return '' if i == 63: return None return chr(i) self.check_data_integrity( ('col1 char(1)','col2 char(1)'), generator) def test_bug_2671682(self): from pymysql.constants import ER try: self.cursor.execute("describe some_non_existent_table"); except self.connection.ProgrammingError as msg: self.assertEqual(msg.args[0], ER.NO_SUCH_TABLE) def test_ping(self): self.connection.ping() def test_literal_int(self): self.assertTrue("2" == self.connection.literal(2)) def test_literal_float(self): self.assertTrue("3.1415" == self.connection.literal(3.1415)) def test_literal_string(self): self.assertTrue("'foo'" == self.connection.literal("foo")) PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py000066400000000000000000000170101340615555200265570ustar00rootroot00000000000000from . import dbapi20 import pymysql from pymysql.tests import base try: import unittest2 as unittest except ImportError: import unittest class test_MySQLdb(dbapi20.DatabaseAPI20Test): driver = pymysql connect_args = () connect_kw_args = base.PyMySQLTestCase.databases[0].copy() connect_kw_args.update(dict(read_default_file='~/.my.cnf', charset='utf8', sql_mode="ANSI,STRICT_TRANS_TABLES,TRADITIONAL")) def test_setoutputsize(self): pass def test_setoutputsize_basic(self): pass def test_nextset(self): pass """The tests on fetchone and fetchall and rowcount bogusly test for an exception if the statement cannot return a result set. MySQL always returns a result set; it's just that some things return empty result sets.""" 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_fetchone(self): con = self._connect() try: cur = con.cursor() # 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 cannnot 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 cannnot 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() # Same complaint as for fetchall and fetchone def test_rowcount(self): con = self._connect() try: cur = con.cursor() self.executeDDL1(cur) ## self.assertEqual(cur.rowcount,-1, ## 'cursor.rowcount should be -1 after executing no-result ' ## 'statements' ## ) cur.execute("insert into %sbooze values ('Victoria Bitter')" % ( self.table_prefix )) ## self.assertTrue(cur.rowcount in (-1,1), ## 'cursor.rowcount should == number or rows inserted, or ' ## 'set to -1 after executing an insert statement' ## ) cur.execute("select name from %sbooze" % self.table_prefix) self.assertTrue(cur.rowcount in (-1,1), 'cursor.rowcount should == number of rows returned, or ' 'set to -1 after executing a select statement' ) self.executeDDL2(cur) ## self.assertEqual(cur.rowcount,-1, ## 'cursor.rowcount not being reset to -1 after executing ' ## 'no-result statements' ## ) finally: con.close() def test_callproc(self): pass # performed in test_MySQL_capabilities 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" ''' sql=""" create procedure deleteme() begin select count(*) from %(tp)sbooze; select name from %(tp)sbooze; end """ % dict(tp=self.table_prefix) cur.execute(sql) def help_nextset_tearDown(self,cur): 'If cleaning up is needed after nextSetTest' cur.execute("drop procedure deleteme") def test_nextset(self): from warnings import warn con = self._connect() try: cur = con.cursor() if not hasattr(cur,'nextset'): return try: self.executeDDL1(cur) sql=self._populate() for sql in self._populate(): cur.execute(sql) self.help_nextset_setUp(cur) cur.callproc('deleteme') numberofrows=cur.fetchone() assert numberofrows[0]== len(self.samples) assert cur.nextset() names=cur.fetchall() assert len(names) == len(self.samples) s=cur.nextset() if s: empty = cur.fetchall() self.assertEqual(len(empty), 0, "non-empty result set after other result sets") #warn("Incompatibility: MySQL returns an empty result set for the CALL itself", # Warning) #assert s == None,'No more return sets, should return None' finally: self.help_nextset_tearDown(cur) finally: con.close() PyMySQL-0.9.3/pymysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py000066400000000000000000000061711340615555200276570ustar00rootroot00000000000000import sys try: import unittest2 as unittest except ImportError: import unittest import pymysql _mysql = pymysql from pymysql.constants import FIELD_TYPE from pymysql.tests import base from pymysql._compat import PY2, long_type if not PY2: basestring = str class TestDBAPISet(unittest.TestCase): def test_set_equality(self): self.assertTrue(pymysql.STRING == pymysql.STRING) def test_set_inequality(self): self.assertTrue(pymysql.STRING != pymysql.NUMBER) def test_set_equality_membership(self): self.assertTrue(FIELD_TYPE.VAR_STRING == pymysql.STRING) def test_set_inequality_membership(self): self.assertTrue(FIELD_TYPE.DATE != pymysql.STRING) class CoreModule(unittest.TestCase): """Core _mysql module features.""" def test_NULL(self): """Should have a NULL constant.""" self.assertEqual(_mysql.NULL, 'NULL') def test_version(self): """Version information sanity.""" self.assertTrue(isinstance(_mysql.__version__, basestring)) self.assertTrue(isinstance(_mysql.version_info, tuple)) self.assertEqual(len(_mysql.version_info), 5) def test_client_info(self): self.assertTrue(isinstance(_mysql.get_client_info(), basestring)) def test_thread_safe(self): self.assertTrue(isinstance(_mysql.thread_safe(), int)) class CoreAPI(unittest.TestCase): """Test _mysql interaction internals.""" def setUp(self): kwargs = base.PyMySQLTestCase.databases[0].copy() kwargs["read_default_file"] = "~/.my.cnf" self.conn = _mysql.connect(**kwargs) def tearDown(self): self.conn.close() def test_thread_id(self): tid = self.conn.thread_id() self.assertTrue(isinstance(tid, (int, long_type)), "thread_id didn't return an integral value.") self.assertRaises(TypeError, self.conn.thread_id, ('evil',), "thread_id shouldn't accept arguments.") def test_affected_rows(self): self.assertEqual(self.conn.affected_rows(), 0, "Should return 0 before we do anything.") #def test_debug(self): ## FIXME Only actually tests if you lack SUPER #self.assertRaises(pymysql.OperationalError, #self.conn.dump_debug_info) def test_charset_name(self): self.assertTrue(isinstance(self.conn.character_set_name(), basestring), "Should return a string.") def test_host_info(self): assert isinstance(self.conn.get_host_info(), basestring), "should return a string" def test_proto_info(self): self.assertTrue(isinstance(self.conn.get_proto_info(), int), "Should return an int.") def test_server_info(self): if sys.version_info[0] == 2: self.assertTrue(isinstance(self.conn.get_server_info(), basestring), "Should return an str.") else: self.assertTrue(isinstance(self.conn.get_server_info(), basestring), "Should return an str.") if __name__ == "__main__": unittest.main() PyMySQL-0.9.3/pymysql/times.py000066400000000000000000000005501340615555200162200ustar00rootroot00000000000000from time import localtime from datetime import date, datetime, time, timedelta Date = date Time = time TimeDelta = timedelta Timestamp = datetime def DateFromTicks(ticks): return date(*localtime(ticks)[:3]) def TimeFromTicks(ticks): return time(*localtime(ticks)[3:6]) def TimestampFromTicks(ticks): return datetime(*localtime(ticks)[:6]) PyMySQL-0.9.3/pymysql/util.py000066400000000000000000000002641340615555200160560ustar00rootroot00000000000000import struct def byte2int(b): if isinstance(b, int): return b else: return struct.unpack("!B", b)[0] def int2byte(i): return struct.pack("!B", i) PyMySQL-0.9.3/requirements.txt000066400000000000000000000000161340615555200162700ustar00rootroot00000000000000cryptography PyMySQL-0.9.3/runtests.py000077500000000000000000000014171340615555200152560ustar00rootroot00000000000000#!/usr/bin/env python import unittest2 from pymysql._compat import PYPY, JYTHON, IRONPYTHON #import pymysql #pymysql.connections.DEBUG = True #pymysql._auth.DEBUG = True if not (PYPY or JYTHON or IRONPYTHON): import atexit import gc gc.set_debug(gc.DEBUG_UNCOLLECTABLE) @atexit.register def report_uncollectable(): import gc if not gc.garbage: print("No garbages!") return print('uncollectable objects') for obj in gc.garbage: print(obj) if hasattr(obj, '__dict__'): print(obj.__dict__) for ref in gc.get_referrers(obj): print("referrer:", ref) print('---') import pymysql.tests unittest2.main(pymysql.tests, verbosity=2) PyMySQL-0.9.3/setup.cfg000066400000000000000000000004361340615555200146330ustar00rootroot00000000000000[flake8] ignore = E226,E301,E701 exclude = tests,build max-line-length = 119 [bdist_wheel] universal = 1 [metadata] license = "MIT" license_file = LICENSE author=yutaka.matsubara author_email=yutaka.matsubara@gmail.com maintainer=INADA Naoki maintainer_email=songofacandy@gmail.com PyMySQL-0.9.3/setup.py000077500000000000000000000023511340615555200145250ustar00rootroot00000000000000#!/usr/bin/env python import io from setuptools import setup, find_packages version = "0.9.3" with io.open('./README.rst', encoding='utf-8') as f: readme = f.read() setup( name="PyMySQL", version=version, url='https://github.com/PyMySQL/PyMySQL/', project_urls={ "Documentation": "https://pymysql.readthedocs.io/", }, description='Pure Python MySQL Driver', long_description=readme, packages=find_packages(exclude=['tests*', 'pymysql.tests*']), extras_require={ "rsa": ["cryptography"], }, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Topic :: Database', ], keywords="MySQL", ) PyMySQL-0.9.3/tests/000077500000000000000000000000001340615555200141515ustar00rootroot00000000000000PyMySQL-0.9.3/tests/__init__.py000066400000000000000000000000001340615555200162500ustar00rootroot00000000000000PyMySQL-0.9.3/tests/test_auth.py000066400000000000000000000034351340615555200165300ustar00rootroot00000000000000"""Test for auth methods supported by MySQL 8""" import os import pymysql # pymysql.connections.DEBUG = True # pymysql._auth.DEBUG = True host = "127.0.0.1" port = 3306 ca = os.path.expanduser("~/ca.pem") ssl = {'ca': ca, 'check_hostname': False} def test_sha256_no_password(): con = pymysql.connect(user="nopass_sha256", host=host, port=port, ssl=None) con.close() def test_sha256_no_passowrd_ssl(): con = pymysql.connect(user="nopass_sha256", host=host, port=port, ssl=ssl) con.close() def test_sha256_password(): con = pymysql.connect(user="user_sha256", password="pass_sha256", host=host, port=port, ssl=None) con.close() def test_sha256_password_ssl(): con = pymysql.connect(user="user_sha256", password="pass_sha256", host=host, port=port, ssl=ssl) con.close() def test_caching_sha2_no_password(): con = pymysql.connect(user="nopass_caching_sha2", host=host, port=port, ssl=None) con.close() def test_caching_sha2_no_password(): con = pymysql.connect(user="nopass_caching_sha2", host=host, port=port, ssl=ssl) con.close() def test_caching_sha2_password(): con = pymysql.connect(user="user_caching_sha2", password="pass_caching_sha2", host=host, port=port, ssl=None) con.close() # Fast path of caching sha2 con = pymysql.connect(user="user_caching_sha2", password="pass_caching_sha2", host=host, port=port, ssl=None) con.query("FLUSH PRIVILEGES") con.close() def test_caching_sha2_password_ssl(): con = pymysql.connect(user="user_caching_sha2", password="pass_caching_sha2", host=host, port=port, ssl=ssl) con.close() # Fast path of caching sha2 con = pymysql.connect(user="user_caching_sha2", password="pass_caching_sha2", host=host, port=port, ssl=None) con.query("FLUSH PRIVILEGES") con.close() PyMySQL-0.9.3/tox.ini000066400000000000000000000002751340615555200143260ustar00rootroot00000000000000[tox] envlist = py27,py34,py35,py36,py37,pypy,pypy3 [testenv] commands = coverage run ./runtests.py deps = unittest2 coverage passenv = USER PASSWORD PAMSERVICE